最近FPバグ(Haskellを学ぼうとしています)を見つけました。これまで見てきたこと(一流の関数、怠zyな評価、その他すべての利点)に感銘を受けました。 )。私はまだ専門家ではありませんが、基本的なアルゴリズムよりも「機能的に」推論する方が簡単であることにすでに気づき始めています(そして、私がしなければならない場所に戻るのに苦労しています)。
ただし、現在のFPがフラットに見えるのはGUIプログラミングです。 Haskellのアプローチは、命令型GUIツールキット(GTK +やwxWidgetsなど)を単にラップし、「do」ブロックを使用して命令型スタイルをシミュレートすることです。私はF#を使用していませんが、.NETクラスでOOPを使用して同様のことを行うと理解しています。明らかに、これには正当な理由があります-現在のGUIプログラミングはすべてIOと副作用に関するものであるため、ほとんどの現在のフレームワークでは純粋に機能的なプログラミングは不可能です。
私の質問は、GUIプログラミングに機能的なアプローチをとることは可能ですか?これが実際にどのように見えるか想像するのに苦労しています。このようなことを試みるフレームワーク(実験的またはその他)を知っている人はいますか(または、関数型言語のためにゼロから設計されたフレームワークさえ)。または、GUIパーツにOOPを、ロジックにFPを使用するハイブリッドアプローチを使用するソリューションですか? (私は好奇心を求めているだけです。FPは「未来」だと思いたいのですが、GUIプログラミングは埋めるにはかなり大きな穴のようです。)
Haskellのアプローチは、命令型GUIツールキット(GTK +やwxWidgetsなど)をラップし、「do」ブロックを使用して命令型スタイルをシミュレートすることです。
これは、実際には「Haskellアプローチ」ではありません。これは、命令型インターフェイスを介して命令型GUIツールキットに最も直接バインドする方法です。 Haskellはたまたまかなり目立ったバインディングを持っています。
主にHaskellで、主に機能的なリアクティブプログラミングを使用した、GUIに対する中程度に成熟した、またはより実験的な純粋に機能的/宣言的なアプローチがいくつかあります。
以下に例を示します。
Haskell、Flapjaxに慣れていない人のために、 http://www.flapjax-lang.org/ はJavaScriptに基づく機能的なリアクティブプログラミングの実装です。
私の質問は、GUIプログラミングに機能的なアプローチをとることは可能ですか?
あなたが探しているキーワードは「機能的反応型プログラミング」(FRP)です。
Conal Elliottおよび他の一部の企業は、FRPの適切な抽象化を探そうとすることで、家内産業を少し作りました。 HaskellにはFRPの概念の実装がいくつかあります。
Conalの最新の "Push-Pull Functional Reactive Programming" 論文から始めることを検討するかもしれませんが、他にも(古い)実装がいくつかあり、一部は haskell.orgサイト 。 Conalにはドメイン全体をカバーするコツがあり、彼の論文は以前のことを参照することなく読むことができます。
このアプローチがGUI開発にどのように使用できるかを理解するために、 Fudgets を見るとよいかもしれません。 、GUI設計への堅実なFRPアプローチを提示します。
Windows Presentation Foundationは、機能的なアプローチがGUIプログラミングで非常にうまく機能することの証明です。多くの機能的な側面があり、「優れた」WPFコード(MVVMパターンの検索)は、命令型よりも機能的なアプローチを強調しています。私は、WPFが最も成功した実世界の機能的なGUIツールキットであると断言できます:-)
WPFはXAMLでユーザーインターフェイスを記述します(ただし、機能的に見えるC#またはF#に書き換えることもできます)ので、ユーザーインターフェイスを作成するには、次のように記述します。
<!-- Declarative user interface in WPF and XAML -->
<Canvas Background="Black">
<Ellipse x:Name="greenEllipse" Width="75" Height="75"
Canvas.Left="0" Canvas.Top="0" Fill="LightGreen" />
</Canvas>
さらに、WPFでは、宣言タグの別のセットを使用して、イベントに対するアニメーションと反応を宣言的に記述することもできます(ここでも、同じことをC#/ F#コードとして記述できます)。
<DoubleAnimation
Storyboard.TargetName="greenEllipse"
Storyboard.TargetProperty="(Canvas.Left)"
From="0.0" To="100.0" Duration="0:0:5" />
実際、WPFにはHaskellのFRPと多くの共通点があると思います(ただし、WPFデザイナーはFRPを知らなかったため、少し不幸だと思いますが、WPFは、視点)。
実際には、関数型プログラミング(F#)は、たとえばC#よりもはるかに優れたユーザーインターフェイスプログラミングツールです。あなたは問題について少し異なって考える必要があります。
このトピックについては 私の関数型プログラミング 第16章の本で説明していますが、 無料抜粋 があり、F#で使用できる最も興味深いパターンを示しています(IMHO) 。長方形の描画を実装するとします(ユーザーがボタンを押し、マウスを動かしてボタンを離します)。 F#では、次のように記述できます。
let rec drawingLoop(clr, from) = async {
// Wait for the first MouseMove occurrence
let! move = Async.AwaitObservable(form.MouseMove)
if (move.Button &&& MouseButtons.Left) = MouseButtons.Left then
// Refresh the window & continue looping
drawRectangle(clr, from, (move.X, move.Y))
return! drawingLoop(clr, from)
else
// Return the end position of rectangle
return (move.X, move.Y) }
let waitingLoop() = async {
while true do
// Wait until the user starts drawing next rectangle
let! down = Async.AwaitObservable(form.MouseDown)
let downPos = (down.X, down.Y)
if (down.Button &&& MouseButtons.Left) = MouseButtons.Left then
// Wait for the end point of the rectangle
let! upPos = drawingLoop(Color.IndianRed, downPos)
do printfn "Drawn rectangle (%A, %A)" downPos upPos }
これは非常に命令的なアプローチです(通常の実用的なF#スタイル)が、描画の現在の状態を保存したり初期位置を保存したりするために可変状態を使用することを避けます。さらに機能的にすることができますが、修士論文の一部としてそれを行うライブラリを作成しました。これは、数日中に my blog で利用できるようになるはずです。
関数型リアクティブプログラミングはより機能的なアプローチですが、非常に高度なHaskellの機能(矢印など)に依存しているため、使用がやや難しくなります。ただし、多くの場合、非常にエレガントです。制限は、ステートマシン(リアクティブプログラムの有用なメンタルモデル)を簡単にエンコードできないことです。上記のF#テクニックを使用すると、これは非常に簡単です。
F#のDon Symeによるデモをご覧ください。次のリンクは、シリーズの3番目の部分にあります(そこから他の2つの部分にリンクできます)。
W#開発にF#を使用することは、非常に興味深いGUIパラダイムです...
http://channel9.msdn.com/shows/Going+Deep/C9-Lectures-Dr-Don-Syme-Introduction-to-F-3-of-3/
関数型リアクティブプログラミングの背後にある心を開くアイデアの1つは、イベントへの反応と次のイベント処理関数の両方を生成するイベント処理関数を持つことです。したがって、進化するシステムは、一連のイベント処理機能として表されます。
私にとって、Yampaの学習は、その関数を生成する関数を適切に取得するための重要なポイントになりました。ヤンパに関する素晴らしい論文がいくつかあります。 The Yampa Arcadeをお勧めします:
http://www.cs.nott.ac.uk/~nhn/Talks/HW2003-YampaArcade.pdf (スライド、PDF) http://www.cs.nott。 ac.uk/~nhn/Publications/hw2003.pdf (記事全文、PDF)
Haskell.orgのYampaにwikiページがあります
http://www.haskell.org/haskellwiki/Yampa
元のヤンパのホームページ:
http://www.haskell.org/yampa (残念ながら今のところ壊れています)
この質問が最初に尋ねられてから、機能的リアクティブプログラミングはElmによってもう少し主流になりました。
http://Elm-lang.org で確認することをお勧めします。これには、完全に機能するブラウザー内GUIの作成方法に関するいくつかの本当に優れたインタラクティブなチュートリアルもあります。
自分で提供する必要があるコードが純粋な関数のみで構成される、完全に機能するGUIを作成できます。個人的には、さまざまなHaskell GUIフレームワークよりも簡単にアクセスできることがわかりました。
FRPに関するエリオットの講演は こちら にあります。
さらに、実際には答えではなく、発言といくつかの考え:どういうわけか「機能的なGUI」という用語は、ちょっとした矛盾のように見えます(純粋さとIO(同じ用語で)。
しかし、私の漠然とした理解は、機能的なGUIプログラミングは(リアルタイム)時間依存のユーザー入力を取得し、時間依存のGUI出力を生成する時間依存関数を宣言的に定義することです。
つまり、この関数は、可変状態を命令的に使用するアルゴリズムではなく、微分方程式のように宣言的に定義されます。
したがって、従来のFPでは時間に依存しない関数を使用しますが、FRPでは、プログラムを記述するための構成要素として時間に依存する関数を使用します。
ユーザーが対話できるスプリング上のボールのシミュレーションについて考えてみましょう。ボールの位置はグラフィック出力(画面上)であり、ユーザーがボールを押すとキーが押されます(入力)。
FRPでのこのシミュレーションプログラムの記述(私の理解によると)は、単一の微分方程式(宣言的に)によって行われます:加速度*質量=-ばねの伸び*ばね定数+ユーザーが加える力。
この視点を示す Elm のビデオを次に示します。
2016年の時点で、Haskell用の比較的成熟したFRPフレームワークがいくつかあります(SodiumやReflex(Netwire)など)。
Functional Reactive ProgrammingのManning book は、実例としてJavaバージョンのSodiumを紹介し、FRP GUIコードベースの動作とスケーリングを命令型およびアクターと比較して示していますベースのアプローチ。
Arrowized FRPに関する最近の論文と、副作用、IO、および法を遵守する純粋なFRP設定に変異を組み込む見込みもあります。 http://haskell.cs.yale.edu/ wp-content/uploads/2015/10/dwc-yale-formatted-dissertation.pdf 。
また、ReactJSやAngularなどのJavaScriptフレームワークは、スケーラブルで構成可能なGUIコンポーネントを実現するために、FRPまたはその他の機能的アプローチを既に使用している、または使用しようとしていることにも注目してください。
XULのようなマークアップ言語を使用すると、宣言的な方法でGUIを構築できます。
これに対処するために、F#の使用に関する私の考えを投稿しました。
http://fadsworld.wordpress.com/2011/04/13/f-in-the-enterprise-i/ http://fadsworld.wordpress.com/2011/04/17/fin -the-enterprise-ii-2 /
また、シリーズを仕上げ、F#がUXプログラミングにどのように貢献できるかを示すビデオチュートリアルを行う予定です。
ここではF#のコンテキストでのみ話しています。
-ファハド
これらの他のすべての答えは、関数型プログラミングに基づいていますが、独自の設計上の決定を多数行います。基本的に完全に関数と単純な抽象データ型から構築されるライブラリの1つは、 gloss
です。ソースからの play
関数のタイプは次のとおりです。
-- | Play a game in a window. Like `simulate`, but you manage your own input events.
play :: Display -- ^ Display mode.
-> Color -- ^ Background color.
-> Int -- ^ Number of simulation steps to take for each second of real time.
-> world -- ^ The initial world.
-> (world -> Picture) -- ^ A function to convert the world a picture.
-> (Event -> world -> world)
-- ^ A function to handle input events.
-> (Float -> world -> world)
-- ^ A function to step the world one iteration.
-- It is passed the period of time (in seconds) needing to be advanced.
-> IO ()
ご覧のとおり、純粋な関数に単純な抽象型を提供することで完全に機能します。他のライブラリが役立ちます。
Haskellを初めて使用する人々が気付く最も明らかな革新は、外部との通信に関係する不純な世界と、計算とアルゴリズムの純粋な世界との間に分離があるということです。よくある初心者の質問は、「どのようにしてIO
を削除できますか、つまりIO a
をa
に変換できますか?」です。これを行う方法は、モナド(または他の抽象化)を使用して、IOおよびチェーン効果を実行するコードを記述することです。このコードは、外部の世界からデータを収集し、そのモデルを作成し、おそらく純粋なコードを使用して計算を行い、結果を出力します。
上記のモデルに関する限り、IO
モナドでGUIを操作することでひどく悪いことは見当たりません。このスタイルから生じる最大の問題は、モジュールがもう構成できないことです。つまり、プログラム内のステートメントのグローバルな実行順序に関する知識のほとんどを失います。それを回復するには、同時実行の命令型GUIコードと同様の推論を適用する必要があります。一方、不純で非GUIコードの場合、実行順序はIO
モナドの>==
演算子の定義のために明らかです(少なくとも1つのスレッドがある限り)。純粋なコードの場合、パフォーマンスを向上させるため、または⊥
をもたらす評価を回避するための特別な場合を除いて、まったく問題ではありません。
コンソールとグラフィカルIOの最大の哲学的違いは、前者を実装するプログラムは通常同期スタイルで書かれていることです。これは、(信号やその他の開いているファイル記述子を除いて)イベントのソースが1つだけであるため、一般的にstdin
と呼ばれるバイトストリームであるため可能です。ただし、GUIは本質的に非同期であり、キーボードイベントとマウスクリックに対応する必要があります。
機能的な方法で非同期IOを実行するという一般的な哲学は、Functional Reactive Programming(FRP)と呼ばれます。 ReactiveX などのライブラリとElmなどのフレームワークのおかげで、最近、不純な非機能言語で大きな注目を集めました。一言で言えば、GUI要素やその他のもの(ファイル、時計、アラーム、キーボード、マウスなど)を、「オブザーバブル」と呼ばれるイベントストリームとしてイベントソースとして表示するようなものです。これらのイベントは、map
、foldl
、Zip
、filter
、concat
、join
などの使い慣れた演算子を使用して結合され、新しいストリームを生成します。プログラムの状態自体はプログラムのscanl . map reactToEvents $ zipN <eventStreams>
として見ることができるため、これは便利です。ここでN
は、プログラムがこれまでに考慮したオブザーバブルの数に等しくなります。
FRPオブザーバブルを使用すると、ストリーム内のイベントが時間内に順序付けられるため、構成可能性を回復できます。その理由は、イベントストリームの抽象化により、すべてのオブザーバブルをブラックボックスとして表示できるようになるためです。最終的に、演算子を使用してイベントストリームを結合すると、実行時にローカルの順序付けが行われます。これにより、Haskellのすべての関数を参照的に透過的にする必要があるのと同様に、プログラムが実際にどの不変式に依存するかについてより正直になります。プログラムの別の部分からデータを取得する場合は、明示的にする必要があります広告は私の機能に適切なタイプを宣言します。 (IOモナドは、不純なコードを記述するためのドメイン固有の言語であり、事実上これを回避します)