機能的なGUIプログラミングは可能ですか?[閉まっている]


404

最近FPバグを見つけて(Haskellを学ぼうとしている)、これまでに見たもの(ファーストクラスの関数、遅延評価、その他すべての利点)に本当に感動しました。私はまだ専門家ではありませんが、基本的なアルゴリズムを強制的に実行するよりも「機能的に」推論する方が簡単だと気づき始めています(また、必要なところに戻るのに苦労しています)。

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

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


7
Common LispとOCamlのGUIを見てきましたが、おそらく、問題を引き起こしているのはHaskellの怠惰です。
new123456 2012年

5
@ new123456 Common Lispは関数型言語ではありませんが、可変データで機能し、副作用を取り入れています
Electric Coffee

3
@ElectricCoffee Lispは非常に柔軟な言語であり、さまざまなスタイルで使用でき、多くの人々は関数型スタイルでLispを使用することを選択します。
chrismamo1 2015

8
私の経験から(私はまだそれを信じようとしており、さらに学習しています)FRPはGUIプログラミングで本当に限界に達しています。ユースケースの80%は見事でエレガントですが、リッチウィジェットは内部状態(検索コンボボックスなど)を非常に正確に制御する必要があり、FRPが邪魔になります。命令型が常に悪であるとは限りません。命令型コードの量を最小限に抑えることは良いことですが、それを100%削除しますか?自明ではないUI開発で機能することをまだ確認していません。
AlexG

8
@ElectricCoffee「Common Lispは関数型言語ではありません」Lispはすべての関数型言語の母です。Lispは純粋ではないということです。
Jon Harrop、2017

回答:


184

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

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

GUIへの適度に成熟した、またはより実験的な純粋に機能的な/宣言型のアプローチがいくつかあり、主にHaskellで、主に機能的な反応プログラミングを使用しています。

次に例を示します。

Haskell、Flapjaxに慣れていない方のために、http: //www.flapjax-lang.org/ は、JavaScriptの上に機能的な反応型プログラミングを実装したものです。


32
テクニックと決定のすばらしい詳細な説明については、果物に関するConal Elliottの論文を参照してください。conal.net / papers / genuinely-functional-guis.pdf このスタイルで純粋に機能的なGUIプログラミングを数か月間行ってきました。私はそれが大好きです。これは、命令型UIプログラミングのスパゲッティの地獄からの心地よい救済です。これは、この点でほとんどの命令型プログラミングよりも悪いようです。
luqui

44
私はこれに100%同意します。明確にするために:既存のGUIツールキットがよく使用される理由は、それらが存在するためです。それらへのインターフェースが必須かつ不純になる傾向がある理由は、ツールキットが必須かつ不純になる傾向があるためです。ツールキットが必須で不純である傾向がある理由は、それらが依存するオペレーティングシステムが必須で不純である傾向があるためです。ただし、これらのいずれも基本的に不純である必要はありません。これらのツールキットには機能的なバインディングがあり、機能的なツールキットがあり、機能的なオペレーティングシステムさえあります。
イェルクWミッターク

16
それはすべて怠惰の問題です。(不適切な言葉遣いが意図されています。)
JörgW Mittag 2010

10
いつかすべてのGUIデザインがWYSIWYGを介して実装され、ロジックが機能的に実装されます。これは私の予測です。
BlueRaja-Danny Pflughoeft 2010

24
ルキが言及した紙は死んでいるようだ。ただし、Conal Elliottのサイトには有効な
papers

74

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

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

Conal Elliottと他の何人かは、FRPの正しい抽象化を見つけようとすることから、家内工業を少し作りました。HaskellにはFRPの概念の実装がいくつかあります。

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

このアプローチがGUI開発にどのように使用されるかを理解するには、Fudgetsを参照することをお勧めします。Fudgetsは、最近90年代半ばに設計されており、歯が少し長くなっていますが、しっかりとしたFRPアプローチを示しています。 GUIデザインへ。


もともとC#用に作成され、Java(RxJava)とJavaScript(RxJS)やさまざまな言語に移植された "Reactive Extensions"(FRP Libraries;ただしFPではありません)の使用の増加を追加したいと思います。Angular 2はRxJSを幅広く使用しています。
srph 2016年

63

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は、機能を使用している場合、少し奇妙で不明確に感じることがあります視点)。


12
XAMLは本質的に非常に宣言的ですが、MVVMは実際に関数型のプログラミングを奨励していますか?ビューモデルの概念全体は、ビューの状態を追跡する(そしてINotifyPropertyChangedすべてのもののインターフェイスを実装する)のが、FPとは正反対です。私は間違いなくFPの専門家ではありません。おそらく、宣言的な側面ではなく不変性の側面に過度に焦点を合わせすぎているかもしれませんが、MVVMパターン(通常使用される)がFPの例であることを理解するのに苦労しています。
devuxer 2015年

1
@devuxer私はそうだと主張します。厳密な不変コードに誰もが実際にFPを使用することはないと思います。代わりに、可変性の境界がどこにあるかを決定し、他のすべてのレベルで不変に動作します。この場合、実際に状態を変化させる単一の小さな部分を除いて、誰もが状態が不変であると想定できます。これは、HTMLのしくみに似ています。ええ、不変のDOMがありますが、ナビゲートするたびに、新しいDOMを作成する必要があります。INotifyPropertyChangedGUIの更新を処理する必要がある場所に渡す更新関数です。これは遅延の修正です。
Luaan

3
Steven PembertonがF#とWPFに関する2つの素晴らしい投稿を書きました。2 番目の投稿の終わりに向けた、F#を使用したWPF開発に関する彼の考えがこの議論に追加されています。イベントドリブンMVVMでの機能コントローラーの使用と、Flying Frog ConsultancyによるWPFコントロールデモでシンプルなインターフェイスを構築するための差別化されたユニオンと再帰の使用も、私を興味深くした2つの例です。
Funk

29

実際には、関数型プログラミング(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#スタイルの)非常に必須のアプローチですが、現在の描画状態の保存や初期位置の保存に可変状態を使用することを避けます。さらに機能的にすることもできますが、修士論文の一部としてそれを行うライブラリを作成しました。これは、今後数日のうちにブログで公開される予定です。

関数型リアクティブプログラミングはより機能的なアプローチですが、非常に高度なHaskell機能(矢印など)に依存しているため、使用するのが少し難しいと感じています。ただし、多くの場合、非常にエレガントです。これは、ステートマシン(リアクティブプログラムに役立つメンタルモデル)を簡単にエンコードできないという制限があります。これは、上記のF#テクニックを使用すると非常に簡単です。


7
+1これは、コンビネータライブラリとを使用してF#でいくつかのプロダクションGUIを作成した経験を反映していますIObservable
Jon Harrop、2011年

.NETライブラリへのリアクティブ拡張機能の導入以降、FRPに関するコメントは変更されましたか?
Fsharp Pete

1
ここではいくつかのArrowized FRPの研究とどのように影響して変異が法律を壊すことなくArrowized FRP内に埋め込むことができます。haskell.cs.yale.edu/wp-content/uploads/2015/10/...(ところで、ほとんどのFRPライブラリもモナドを使用しますか、 Applicativesなので、矢印が必要なのは正しくありません)。
Erik Kaplun、2016年

17

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

とはいえ機能的なGUIについては、非常に確かな研究がいくつか行われています。FudgetsFranTkなど、いくつかの(主に)機能的なツールキットもあります。


6
「機能的なGUI」リンクが壊れている:( cached:webcache.googleusercontent.com/search
Dan Burton


12

関数型反応型プログラミングの背後にある心を開くアイデアの1つは、イベントへの反応と次のイベント処理関数の両方を生成するイベント処理関数を持つことです。したがって、進化するシステムは、一連のイベント処理機能として表されます。

私にとって、ヤンパを学ぶことは、その機能を生み出す機能が適切に機能するための重要なポイントとなりました。ヤンパに関する素晴らしい論文がいくつかあります。私はヤンパアーケードをお勧めします:

http://www.cs.nott.ac.uk/~nhn/Talks/HW2003-YampaArcade.pdf(スライド、PDF) http://www.cs.nott.ac.uk/~nhn/Publications/hw2003。 pdf(記事全体、PDF)

YampaのHaskell.orgにwikiページがあります

http://www.haskell.org/haskellwiki/Yampa

オリジナルYampaホームページ:

http://www.haskell.org/yampa(残念ながら現時点では壊れています)


1
そのリンクは長い間壊れています。このYampaを
CoR

7

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

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

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



6

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

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

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

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

したがって、従来のFPでは時間に依存しない関数を使用し、FRPではプログラムを記述するためのビルディングブロックとして時間に依存する関数を使用します。

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

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

この視点を説明するELMのビデオを次に示します。


5

2016年の時点で、SodiumやReflexなどのHaskell用の比較的成熟したFRPフレームワークがいくつかあります(Netwireも含まれます)。

官能性反応プログラミング上のマニングブックは作業例については、ナトリウムのJavaバージョンを展示、そして不可欠との比較だけでなく、俳優ベースのアプローチにどのようにFRP GUIコードベースの振る舞いやスケールを示しています。

:Arrowized FRPと副作用を組み込むの見通し、IO、法律遵守、純粋なFRPの設定における変異に関する最近の論文もありますhttp://haskell.cs.yale.edu/wp-content/uploads/2015/10/は、 dwc-yale-formatted-dissertation.pdf

また注目に値するのは、ReactJSやAngularなどのJavaScriptフレームワークやその他の多くのフレームワークが、FRPまたはその他の機能的アプローチを使用して、スケーラブルでコンポーザブルなGUIコンポーネントを実現するために既に動いているか、動いていることです。


Sodiumのgithub readme
mac10688

4

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


3

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

-ファハド


2

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

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


1

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

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

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

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

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


-22

関数型プログラミングは大学時代から進んでいたかもしれませんが、関数型プログラミングシステムの主なポイントは、プログラマーが「副作用」を生み出すのを止めることでした。ただし、UIの更新などの副作用が発生するため、ユーザーはソフトウェアを購入します。


25
私はあなたがポイントを誤解したと思います:関数型プログラミングが世界に外的な影響を与えないというわけではありません-それはすべてのプログラムを完全に役に立たなくするでしょう!むしろ、関数型プログラミングでは、IOを隔離して、どのビットがそれを使用し、どのビットが使用しないかを知ることができます。
Tikhon Jelvis 2011

Whoooooooosh x20
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.