web-dev-qa-db-ja.com

機能的なGUIプログラミングは可能ですか?

最近FPバグ(Haskellを学ぼうとしています)を見つけました。これまで見てきたこと(一流の関数、怠zyな評価、その他すべての利点)に感銘を受けました。 )。私はまだ専門家ではありませんが、基本的なアルゴリズムよりも「機能的に」推論する方が簡単であることにすでに気づき始めています(そして、私がしなければならない場所に戻るのに苦労しています)。

ただし、現在のFPがフラットに見えるのはGUIプログラミングです。 Haskellのアプローチは、命令型GUIツールキット(GTK +やwxWidgetsなど)を単にラップし、「do」ブロックを使用して命令型スタイルをシミュレートすることです。私はF#を使用していませんが、.NETクラスでOOPを使用して同様のことを行うと理解しています。明らかに、これには正当な理由があります-現在のGUIプログラミングはすべてIOと副作用に関するものであるため、ほとんどの現在のフレームワークでは純粋に機能的なプログラミングは不可能です。

私の質問は、GUIプログラミングに機能的なアプローチをとることは可能ですか?これが実際にどのように見えるか想像するのに苦労しています。このようなことを試みるフレームワーク(実験的またはその他)を知っている人はいますか(または、関数型言語のためにゼロから設計されたフレームワークさえ)。または、GUIパーツにOOPを、ロジックにFPを使用するハイブリッドアプローチを使用するソリューションですか? (私は好奇心を求めているだけです。FPは「未来」だと思いたいのですが、GUIプログラミングは埋めるにはかなり大きな穴のようです。)

389
shosti

Haskellのアプローチは、命令型GUIツールキット(GTK +やwxWidgetsなど)をラップし、「do」ブロックを使用して命令型スタイルをシミュレートすることです。

これは、実際には「Haskellアプローチ」ではありません。これは、命令型インターフェイスを介して命令型GUIツールキットに最も直接バインドする方法です。 Haskellはたまたまかなり目立ったバインディングを持っています。

主にHaskellで、主に機能的なリアクティブプログラミングを使用した、GUIに対する中程度に成熟した、またはより実験的な純粋に機能的/宣言的なアプローチがいくつかあります。

以下に例を示します。

Haskell、Flapjaxに慣れていない人のために、 http://www.flapjax-lang.org/ はJavaScriptに基づく機能的なリアクティブプログラミングの実装です。

178
Don Stewart

私の質問は、GUIプログラミングに機能的なアプローチをとることは可能ですか?

あなたが探しているキーワードは「機能的反応型プログラミング」(FRP)です。

Conal Elliottおよび他の一部の企業は、FRPの適切な抽象化を探そうとすることで、家内産業を少し作りました。 HaskellにはFRPの概念の実装がいくつかあります。

Conalの最新の "Push-Pull Functional Reactive Programming" 論文から始めることを検討するかもしれませんが、他にも(古い)実装がいくつかあり、一部は haskell.orgサイト 。 Conalにはドメイン全体をカバーするコツがあり、彼の論文は以前のことを参照することなく読むことができます。

このアプローチがGUI開発にどのように使用できるかを理解するために、 Fudgets を見るとよいかもしれません。 、GUI設計への堅実なFRPアプローチを提示します。

70
Edward KMETT

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は、視点)。

61
Tomas Petricek

実際には、関数型プログラミング(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#テクニックを使用すると、これは非常に簡単です。

27
Tomas Petricek

F#やOCamlのようなハイブリッド機能/ OO言語、または副作用がIOモナドに追いやられるHaskellのような純粋に機能的な言語のどちらであっても、mostlyGUIを管理するために必要な大量の作業が、純粋に機能的なアルゴリズムよりも「副作用」により近い場合。

とは言うものの、 機能的なGUI に関しては、非常に堅実な研究が行われています。 FudgetsFranTk などの(ほとんど)機能的なツールキットもあります。

17
sblom

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/

15
Kevin Won

関数型リアクティブプログラミングの背後にある心を開くアイデアの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 (残念ながら今のところ壊れています)

12
Boris Berkgaut

この質問が最初に尋ねられてから、機能的リアクティブプログラミングはElmによってもう少し主流になりました。

http://Elm-lang.org で確認することをお勧めします。これには、完全に機能するブラウザー内GUIの作成方法に関するいくつかの本当に優れたインタラクティブなチュートリアルもあります。

自分で提供する必要があるコードが純粋な関数のみで構成される、完全に機能するGUIを作成できます。個人的には、さまざまなHaskell GUIフレームワークよりも簡単にアクセスできることがわかりました。

6
saolof

FRPに関するエリオットの講演は こちら にあります。

さらに、実際には答えではなく、発言といくつかの考え:どういうわけか「機能的なGUI」という用語は、ちょっとした矛盾のように見えます(純粋さとIO(同じ用語で)。

しかし、私の漠然とした理解は、機能的なGUIプログラミングは(リアルタイム)時間依存のユーザー入力を取得し、時間依存のGUI出力を生成する時間依存関数を宣言的に定義することです。

つまり、この関数は、可変状態を命令的に使用するアルゴリズムではなく、微分方程式のように宣言的に定義されます。

したがって、従来のFPでは時間に依存しない関数を使用しますが、FRPでは、プログラムを記述するための構成要素として時間に依存する関数を使用します。

ユーザーが対話できるスプリング上のボールのシミュレーションについて考えてみましょう。ボールの位置はグラフィック出力(画面上)であり、ユーザーがボールを押すとキーが押されます(入力)。

FRPでのこのシミュレーションプログラムの記述(私の理解によると)は、単一の微分方程式(宣言的に)によって行われます:加速度*質量=-ばねの伸び*ばね定数+ユーザーが加える力。

この視点を示す Elm のビデオを次に示します。

6
jhegedus

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またはその他の機能的アプローチを既に使用している、または使用しようとしていることにも注目してください。

5
Erik Allik

XULのようなマークアップ言語を使用すると、宣言的な方法でGUIを構築できます。

4
StackedCrooked

これに対処するために、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#のコンテキストでのみ話しています。

-ファハド

4
Fahad

これらの他のすべての答えは、関数型プログラミングに基づいていますが、独自の設計上の決定を多数行います。基本的に完全に関数と単純な抽象データ型から構築されるライブラリの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 ()

ご覧のとおり、純粋な関数に単純な抽象型を提供することで完全に機能します。他のライブラリが役立ちます。

2
PyRulez

Haskellを初めて使用する人々が気付く最も明らかな革新は、外部との通信に関係する不純な世界と、計算とアルゴリズムの純粋な世界との間に分離があるということです。よくある初心者の質問は、「どのようにしてIOを削除できますか、つまりIO aaに変換できますか?」です。これを行う方法は、モナド(または他の抽象化)を使用して、IOおよびチェーン効果を実行するコードを記述することです。このコードは、外部の世界からデータを収集し、そのモデルを作成し、おそらく純粋なコードを使用して計算を行い、結果を出力します。

上記のモデルに関する限り、IOモナドでGUIを操作することでひどく悪いことは見当たりません。このスタイルから生じる最大の問題は、モジュールがもう構成できないことです。つまり、プログラム内のステートメントのグローバルな実行順序に関する知識のほとんどを失います。それを回復するには、同時実行の命令型GUIコードと同様の推論を適用する必要があります。一方、不純で非GUIコードの場合、実行順序はIOモナドの>==演算子の定義のために明らかです(少なくとも1つのスレッドがある限り)。純粋なコードの場合、パフォーマンスを向上させるため、またはをもたらす評価を回避するための特別な場合を除いて、まったく問題ではありません。

コンソールとグラフィカルIOの最大の哲学的違いは、前者を実装するプログラムは通常同期スタイルで書かれていることです。これは、(信号やその他の開いているファイル記述子を除いて)イベントのソースが1つだけであるため、一般的にstdinと呼ばれるバイトストリームであるため可能です。ただし、GUIは本質的に非同期であり、キーボードイベントとマウスクリックに対応する必要があります。

機能的な方法で非同期IOを実行するという一般的な哲学は、Functional Reactive Programming(FRP)と呼ばれます。 ReactiveX などのライブラリとElmなどのフレームワークのおかげで、最近、不純な非機能言語で大きな注目を集めました。一言で言えば、GUI要素やその他のもの(ファイル、時計、アラーム、キーボード、マウスなど)を、「オブザーバブル」と呼ばれるイベントストリームとしてイベントソースとして表示するようなものです。これらのイベントは、mapfoldlZipfilterconcatjoinなどの使い慣れた演算子を使用して結合され、新しいストリームを生成します。プログラムの状態自体はプログラムのscanl . map reactToEvents $ zipN <eventStreams>として見ることができるため、これは便利です。ここでNは、プログラムがこれまでに考慮したオブザーバブルの数に等しくなります。

FRPオブザーバブルを使用すると、ストリーム内のイベントが時間内に順序付けられるため、構成可能性を回復できます。その理由は、イベントストリームの抽象化により、すべてのオブザーバブルをブラックボックスとして表示できるようになるためです。最終的に、演算子を使用してイベントストリームを結合すると、実行時にローカルの順序付けが行われます。これにより、Haskellのすべての関数を参照的に透過的にする必要があるのと同様に、プログラムが実際にどの不変式に依存するかについてより正直になります。プログラムの別の部分からデータを取得する場合は、明示的にする必要があります広告は私の機能に適切なタイプを宣言します。 (IOモナドは、不純なコードを記述するためのドメイン固有の言語であり、事実上これを回避します)

1
MauganRa