web-dev-qa-db-ja.com

矢の目的は何ですか?

私はHaskellで関数型プログラミングを学んでおり、なぜそれらが必要なのかを最初に理解することで概念を理解しようとしています。

関数型プログラミング言語における矢の目的を知りたいのですが。彼らはどのような問題を解決しますか? http://en.wikibooks.org/wiki/Haskell/Understanding_arrowshttp://www.cse.chalmers.se/~を確認しましたrjmh/afp-arrows.pdf 。私が理解しているのは、それらが計算のためのグラフを記述するために使用されていること、および簡単なポイントフリースタイルのコーディングを可能にすることです。

この記事では、ポイントフリースタイルの方が一般に理解しやすく記述しやすいと想定しています。これは私にはかなり主観的に思えます。別の記事( http://en.wikibooks.org/wiki/Haskell/StephensArrowTutorial#Hangman:_Main_program )では、絞首刑執行人ゲームが実装されていますが、矢印がどのように作成されるのかわかりませんこの実装は自然です。

コンセプトを説明する多くの論文を見つけることができましたが、動機については何もありませんでした。

何が欠けていますか?

63
Simon Bergot

私はパーティーに遅刻していることに気づきましたが、あなたはここで2つの理論的な答えを持っているので、私はかみ砕くための実用的な代替手段を提供したいと思いました。私は現在、私が現在取り組んでいるプロジェクトのために、最近Arrowsのテーマを強制的に行進させた相対的なHaskell noobとしてこれに来ています。

まず、Haskellのほとんどの問題をArrowsに手を伸ばすことなく生産的に解決できます。一部の著名なHaskellerは、本当にそれらを好きではなく、使用しません(これについては、 ここここ 、および ここ を参照してください)。したがって、「ねえ、これらは必要ない」と自分に言っている場合は、あなたが本当に正しいかもしれないことを理解してください。

Arrowsを最初に知ったときに私がArrowsについて最もいらいらしたのは、このテーマのチュートリアルが回路の類推のために必然的に到達した方法でした。アローコードを見ると、少なくとも砂糖漬けの品種ですが、ハードウェア定義言語ほど似ていません。入力は右側に整列し、出力は左側に整列します。それらをすべて正しく配線できない場合は、発射に失敗します。私は自分に考えました:本当に?これは私たちが終わったところですか?完全に高水準の言語を作成しましたが、再び銅線とはんだで構成されていますか?

これに対する正しい答えは、私が判断できた限り、次のとおりです:実際には、はいArrowsの現在のキラーユースケースは、 FRP(Yampa、ゲーム、音楽、反応システム一般を考えてください)。 FRPが直面している問題は、他のすべての同期メッセージングシステムが直面している問題とほぼ同じです。関連する情報を落としたりリークを引き起こしたりせずに、入力の連続ストリームを出力の連続ストリームに配線する方法です。ストリームをリストとしてモデル化できます。最近のいくつかのFRPシステムはこのアプローチを使用していますが、入力が多い場合、リストを管理することはほとんど不可能になります。流れから身を守る必要があります。

ArrowsがFRPシステムで許可するのは、ネットワークへの関数の構成であると同時に、それらの関数によって渡される基礎となる値への参照を完全に抽象化することです。 FPを初めて使用する場合、これは最初は混乱する可能性があり、その後、その影響を吸収したときに驚かされます。関数を抽象化できるという考えと、[(*), (+), (-)]のようなリストを[(a -> a -> a)]のタイプとして理解する方法を最近吸収しただけです。矢印を使用すると、抽象化をさらに1つのレイヤーにプッシュできます。

抽象化するこの追加機能は、それ自体に危険を伴います。一つには、型の仮定をどうするべきかわからないコーナーケースにGHCをプッシュすることができます。タイプレベルで考える準備をする必要があります。これは、種類やRankNTypesなどのトピックについて学ぶ絶好の機会です。

私が "Stupid Arrow Stunts"と呼ぶ例もいくつかあります。コーダーがいくつかのArrowコンビネーターに手を伸ばすのは、タプルを使ったきちんとしたトリックを披露したいからです。 (ここに私自身の 狂気へのささいな貢献 があります。)野生で遭遇したときに、そのようなホットドッグを無視してください。

注:上で述べたように、私は比較的初心者です。上記の誤解を広めた場合は、遠慮なく訂正してください。

44
rtperson

これは「やわらかい」答えのようなものであり、参照が実際にこのように述べているかどうかはわかりませんが、これが私が矢印を考えるようになった方法です。

矢印タイプA b cは基本的には関数b -> cですが、モナディック値M aが従来のaよりも多くの構造を持っているのと同じように、より多くの構造を持っています。

さて、その余分な構造がどうなるかは、あなたが話している特定の矢印インスタンスに依存します。モナドIO aMaybe aと同様に、それぞれに異なる追加の構造があります。

モナドで得られるのは、M aからaに移行できないことです。これは制限のように思えるかもしれませんが、実際には機能です。型システムは、モナドの値を単純な古い値に変換することから保護します。 >>=または特定のモナドインスタンスのプリミティブ操作を介してモナドに参加することによってのみ値を利用できます。

同様に、A b cから得られるのは、bを消費する新しいcを生成する「関数」を構築できないことです。矢印は、さまざまな矢印コンビネータに参加するか、特定の矢印インスタンスのプリミティブ操作を使用する場合を除いて、bを消費してcを作成することから保護します。

たとえば、Yampaのシグナル関数はおおよそ(Time -> a) -> (Time -> b)ですが、さらに特定の因果関係制限に従う必要があります。時間tの出力は、入力信号:将来を見ることはできません。つまり、(Time -> a) -> (Time -> b)を使用してプログラミングする代わりに、SF a bを使用してプログラミングし、プリミティブから信号関数を構築します。たまたまSF a bは関数のように動作するため、一般的な構造は「矢印」と呼ばれます。

30
Lambdageek

プログラマーがエキゾチックな関数の合成を行えるように、モナドやファンクターのような矢印を考えるのが好きです。

モナドまたは矢印(およびファンクタ)がない場合、関数型言語での関数の構成は、ある関数を別の関数の結果に適用することに限定されます。モナドとファンクタを使用すると、2つの関数を定義してから、特定のモナドのコンテキストで、それらの関数が相互にやり取りされ、それらに渡されるデータとどのように相互作用するかを指定する個別の再利用可能なコードを記述できます。このコードは、モナドのバインドコード内に配置されます。したがって、モナドは1つのビューであり、再利用可能なバインドコードのコンテナにすぎません。関数は、あるモナドと別のモナドのコンテキスト内で構成が異なります。

簡単な例は、Maybeモナドです。バインド関数にコードがあり、Maybeモナド内で関数Aが関数Bで構成され、BがNothingを生成する場合、バインドコードは、 2つの関数は、Bから出力されるNothing値にAを適用することなく、Nothingを出力します。モナドがない場合、プログラマはNothing入力をテストするためにコードをAに書き込む必要があります。

モナドは、プログラマーが各関数が必要とするパラメーターをソースコードに明示的に入力する必要がないことも意味します。バインド関数はパラメーターの受け渡しを処理します。したがって、モナドを使用すると、ソースコードは、関数AがパラメーターCとDを使用して関数Bを「呼び出し」ているように見えるのではなく、関数名の静的チェーンのように見え始める可能性があります。移動機械-命令よりも機能的。

矢印はまた、バインド関数と関数を結び付け、再利用可能な機能を提供し、パラメータを非表示にします。ただし、Arrows自体を接続して構成することができ、オプションで実行時にデータを他のArrowsにルーティングできます。これで、データに「異なる処理」を行うArrowsの2つのパスにデータを適用し、結果を再構成できます。または、データの一部の値に応じて、データを渡すArrowsのブランチを選択できます。結果のコードは、スイッチ、遅延、積分などを備えた電子回路にさらに似ています。プログラムは非常に静的に見え、進行中のデータの多くの操作を確認できないはずです。考慮すべきパラメーターがますます少なくなり、パラメーターが取る値と受けない値について考える必要が少なくなります。

Arrowizedプログラムの作成には、主に、スプリッター、スイッチ、遅延、積分器などの既製のArrowsを選択し、関数をそれらのArrowsに持ち上げ、より大きなArrowsを形成するためにArrowsを接続することが含まれます。 Arrowized Functional Reactive Programmingでは、Arrowがループを形成し、世界からの入力がプログラムの最後の反復からの出力と結合されるため、出力は実際の世界の入力に反応します。

現実の価値の1つは時間です。 Yampaでは、Signal Function Arrowはコンピュータープログラムを介して時間パラメーターを見えないようにスレッド化します-時間値にアクセスすることはありませんが、積分矢印をプログラムに接続すると、時間をかけて積分された値が出力されるため、これを使用して渡すことができます。他の矢。

14
Robert Onslow

他の答えへの単なる追加:個人的には、そのような概念が(数学的に)何であるか、そしてそれが私が知っている他の概念とどのように関連するかを理解するのに非常に役立ちます。

矢印の場合、私は次の論文が役に立ったと感じました-それはモナド、応用ファンクタ(イディオム)と矢印を比較します: イディオムは気づかない、矢印は細心の注意、モナドは無差別です サムリンドリー、フィリップワドラー、ジェレミーヤロップ。

また、私は誰もこのテーマについていくつかのアイデアや文献を提供するかもしれない このリンク に言及しなかったと思います。

3
Petr Pudlák