F#では、pipe-forward演算子の使用|>
が非常に一般的です。しかし、Haskellでは(.)
、関数の合成が使用されているのを見たことがあります。それらが関連していることは理解していますが、Haskellでパイプフォワードが使用されていないという言語上の理由はありますか、それとも他の何かですか?
10 に答える
F#では(|>)
、左から右へのタイプチェックのために重要です。例えば:
List.map (fun x -> x.Value) xs
xs
の型がわかっていても、タイプチェッカーがラムダの引数の型を認識した時点では不明であるため、通常は型チェックを行いません。x
そのため、解決方法がわかりませんx.Value
。
対照的に
xs |> List.map (fun x -> x.Value)
タイプが既知xs
のタイプにつながるため、正常に動作します。x
のような構文には名前解決が含まれるため、左から右への型チェックが必要ですx.Value
。Simon Peyton Jonesは、Haskellに同様の種類の名前解決を追加する提案を書いていますが、代わりに、タイプが特定の操作をサポートするかどうかを追跡するためにローカル制約を使用することを提案しています。したがって、最初のサンプルでx
は、プロパティを必要とする要件が確認され、この要件を解決できるValue
まで繰り越されます。xs
ただし、これは型システムを複雑にします。
ちょっと憶測で…。
文化: |>
F# の「文化」において重要な演算子であり、おそらく.
Haskell の場合も同様です。F# には関数合成演算子がありますが、F# コミュニティは Haskell コミュニティよりもポイントフリー スタイル<<
を使用しない傾向があると思います。
言語の違い: 私は両方の言語を比較するのに十分な知識はありませんが、let バインディングを一般化するための規則は、これに影響するほど十分に異なっている可能性があります。たとえば、私はF#で時々書くことを知っています
let f = exp
コンパイルされず、明示的な eta 変換が必要です。
let f x = (exp) x // or x |> exp
コンパイルできるようにします。これはまた、人々をポイントフリー/構成スタイルから遠ざけ、パイプラインスタイルに向けさせます. また、F# の型推論ではパイプライン処理が必要になる場合があるため、既知の型が左側に表示されます (こちらを参照)。
(個人的には、ポイントフリー スタイルは読みにくいと思いますが、新しい/異なるものはすべて、慣れるまでは読みにくいと思います。)
両方ともどちらの言語でも実行可能である可能性があり、歴史/文化/事故が、各コミュニティが異なる「アトラクター」に定住した理由を定義する可能性があります.
今回は主にHaskell側からの推測です...
($)
は の反転であり(|>)
、ポイントフリー コードを記述できない場合によく使用されます。したがって、 Haskell で が使用されない主な理由(|>)
は、その場所が既に($)
.
また、F# の経験から言えば、OO(|>)
の構造に似ているため、F# コードで非常に人気があると思います。Subject.Verb(Object)
F# はスムーズな関数型/OO 統合を目指しているため、Subject |> Verb Object
新しい関数型プログラマーにとっては非常にスムーズな移行です。
個人的には、左から右に考えるのも好きなので(|>)
、Haskell で使用していますが、他の多くの人はそうではないと思います。
私たちは物事を混乱させていると思います。Haskellの(.
)はF#の()と同等>>
です。|>
逆関数適用であり、Haskellの()のようなF#()と混同しないでください$
-逆:
let (>>) f g x = g (f x)
let (|>) x f = f x
Haskellプログラマーは$
よく使うと思います。おそらく、F#プログラマーが使用する傾向があるほど頻繁ではありません|>
。一方、一部のF#の人>>
はばかげた程度に使用します:http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx
If you want to use F#'s |>
in Haskell then in Data.Function is the &
operator (since base 4.8.0.0
).
Haskell での左から右への合成
Haskell で左から右 (メッセージを渡す) スタイルを使用する人もいます。たとえば、 Hackage のmpsライブラリを参照してください。例:
euler_1 = ( [3,6..999] ++ [5,10..999] ).unique.sum
このスタイルは状況によっては見栄えが良いと思いますが、読みにくいです (ライブラリとそのすべての演算子を知る必要があり、再定義(.)
も厄介です)。
基本パッケージの一部であるControl.Categoryには、左から右および右から左の合成演算子もあります。>>>
と<<<
それぞれを比較します。
ghci> :m + Control.Category
ghci> let f = (+2) ; g = (*3) in map ($1) [f >>> g, f <<< g]
[9,5]
場合によっては左から右への構成を好むのには十分な理由があります。評価順序は読み取り順序に従います。
スタイルと文化は別として、これは純粋なコードまたは純粋でないコードの言語設計を最適化することに要約されます。
この|>
演算子は、主に不純なコードで現れる 2 つの制限を隠すのに役立つため、F# で一般的です。
- 構造サブタイプのない左から右への型推論。
- 値の制限。
サブタイピングは名目上のものではなく構造的なものであるため、OCaml には前者の制限が存在しないことに注意してください。そのため、構造型は型推論が進むにつれて統合によって簡単に洗練されます。
Haskell は別のトレードオフを取り、これらの制限を解除できる主に純粋なコードに焦点を当てることを選択します。