web-dev-qa-db-ja.com

Haskellコンポジション(。)vs F#のパイプフォワード演算子(|>)

F#では、パイプフォワード演算子|>の使用は非常に一般的です。ただし、Haskellでは、(.)という関数構成が使用されているのを見ただけです。私はそれらが related であることを理解していますが、Haskellではパイプフォワードが使用されていないか、それとも何か他のものであるという言語上の理由がありますか?

94
Ben Lings

私は少し推測しています...

Culture|>はF#の「カルチャ」の重要な演算子であり、おそらくHaskellの.と同様だと思います。 F#には関数構成演算子<<がありますが、F#コミュニティではHaskellコミュニティよりも points-free style が少ない傾向があります。

言語の違い:両方の言語について比較するのに十分な知識はありませんが、letバインディングを一般化するためのルールは、これに影響するほど十分に異なっています。たとえば、私はF#で時々書くことを知っています

let f = exp

コンパイルされず、明示的なイータ変換が必要です:

let f x = (exp) x   // or x |> exp

コンパイルします。これはまた、人々をポイントフリー/合成スタイルから遠ざけ、パイプラインスタイルへと導きます。また、F#型の推論ではパイプラインが必要になることがあるため、既知の型が左側に表示されます( here を参照)。

(個人的には、ポイントフリーのスタイルは読めないと思いますが、あなたがそれに慣れるまで、すべての新しい/異なるものは読めないと思われます。)

どちらもどちらの言語でも潜在的に実行可能であり、歴史/文化/事故は、各コミュニティが異なる「アトラクター」に定住した理由を定義するかもしれません。

61
Brian

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は proposal を作成してHaskellに同様の名前解決を追加しましたが、代わりにローカル制約を使用して型が特定の操作をサポートするかどうかを追跡することを提案しています。したがって、最初のサンプルでは、​​xValueプロパティを必要とするという要件は、xsが見つかるまで繰り越され、この要件を解決できます。ただし、これは型システムを複雑にします。

82

今回は主にHaskell側からの推測...

_($)_は_(|>)_の反転であり、ポイントフリーコードを書くことができない場合、その使用は非常に一般的です。そのため、_(|>)_がHaskellで使用されない主な理由は、その場所がすでに_($)_に取って代わっているためです。

また、少しのF#の経験から言えば、_(|>)_はOOのSubject.Verb(Object)構造に似ているため、F#コードで非常に人気があると思います。 F#はスムーズな機能/ OO統合を目指しているため、_Subject |> Verb Object_は新しい機能プログラマーにとって非常にスムーズな移行です。

個人的には、左から右にも考えるのが好きなので、Haskellでは_(|>)_を使用していますが、他の多くの人はそうは思わないでしょう。

私たちは物事を混乱させていると思います。ハスケルズ(.)はF#の(>>)。 F#の(|>)これは単なる逆関数アプリケーションであり、Haskellの($)-逆順:

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

30
AshleyF

HaskellでF#の|>を使用する場合、- Data.Function&演算子です(base 4.8.0.0以降)。

22
jhegedus

Haskellの左から右への構成

Haskellでも左から右(メッセージの受け渡し)のスタイルを使用する人もいます。たとえば、Hackageの mps libraryを参照してください。例:

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]

時々左から右への合成を好む正当な理由があります:評価の順序は読みの順序に従います。

16
sastanin

_>>>_がflip (.)に使用されているのを見てきましたが、特に左から右に最もよく理解される長いチェーンの場合は、自分でよく使用します。

_>>>_は実際にはControl.Arrowからのものであり、単なる関数以上のもので動作します。

15
spookylukey

スタイルと文化は別として、これは、純粋なコードまたは不純なコードのいずれかのために言語設計を最適化することに要約されます。

|>演算子は、主に不純なコードで現れる2つの制限を隠すのに役立つため、F#で一般的に使用されます。

  • 構造サブタイプのない左から右への型推論。
  • 値の制限。

サブタイプはノミナルではなく構造的であるため、OCamlには前者の制限が存在しないことに注意してください。したがって、構造推論は、型推論の進行に合わせて統合によって容易に改良されます。

Haskellは、これらの制限を取り除くことができる主に純粋なコードに集中することを選択する、別のトレードオフを取ります。

13
Jon Harrop

F#のパイプフォワード演算子(|>)vs haskellで。

// pipe operator example in haskell

factorial :: (Eq a, Num a) =>  a -> a
factorial x =
  case x of
    1 -> 1
    _ -> x * factorial (x-1)

// terminal
ghic >> 5 & factorial & show

&)演算子が気に入らない場合は、F#やElixirのようにカスタマイズできます。

(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 1 |>
ghci>> 5 |> factorial |> show

なぜinfixl 1 |>Data-Function(&) のドキュメントを参照してください

infixl = infix +左結合性

infixr = infix +右結合性


(。)

.)は関数合成を意味します。 Math (f.g)(x f(g(x)))==を意味します。

foo = negate . (*3)
// ouput -3
ghci>> foo 1
// ouput -15
ghci>> foo 5

それは等しい

// (1)
foo x = negate (x * 3) 

または

// (2)
foo x = negate $ x * 3 

$)演算子も Data-Function($) で定義されています。

.)は、Hight Order Functionまたはclosure in jsの作成に使用されます。例を参照してください:


// (1) use lamda expression to create a Hight Order Function
ghci> map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]  
[-5,-3,-6,-7,-3,-2,-19,-24]


// (2) use . operator to create a Hight Order Function
ghci> map (negate . abs) [5,-3,-6,7,-3,2,-19,24]  
[-5,-3,-6,-7,-3,-2,-19,-24]

うわー、少ない(コード)の方が良いです。


|>.を比較します

ghci> 5 |> factorial |> show

// equals

ghci> (show . factorial) 5 

// equals

ghci> show . factorial $ 5 

left —> rightright —> leftの違いです。 ⊙﹏⊙|||

人間化

|>および&.よりも優れています

なぜなら

ghci> sum (replicate 5 (max 6.7 8.9))

// equals

ghci> 8.9 & max 6.7 & replicate 5 & sum

// equals

ghci> 8.9 |> max 6.7 |> replicate 5 |> sum

// equals

ghci> (sum . replicate 5 . max 6.7) 8.9

// equals

ghci> sum . replicate 5 . max 6.7 $ 8.9

オブジェクト指向言語で関数型プログラミングする方法は?

http://reactivex.io/ にアクセスしてください

サポートします:

  • Java:RxJava
  • JavaScript:RxJS
  • C#:Rx.NET
  • C#(Unity):UniRx
  • Scala:RxScala
  • Clojure:RxClojure
  • C++:RxCpp
  • ルア:RxLua
  • Ruby:Rx.rb
  • Python:RxPY
  • Go:RxGo
  • Groovy:RxGroovy
  • JRuby:RxJRuby
  • コトリン:RxKotlin
  • スイフト:RxSwift
  • PHP:RxPHP
  • エリクサー:リラックス
  • ダーツ:RxDart
2
lihansey

Haskellを試す最初の日(Rust and F#)の後)で、F#の|>演算子を定義できました。

(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 0 |>

そしてそれはうまくいくようです:

factorial x =
  case x of
    1 -> 1
    _ -> x * factorial (x-1)

main =     
    5 |> factorial |> print

Haskellのエキスパートがあなたにもっと良い解決策を提供できると思います。

1
tib