Haskellの構成(。)とF#のパイプ転送演算子(|>)


100

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


2
lihanseysの回答によると、それ&はHaskellのもの|>です。このスレッドの奥深くに埋もれ、数日かけて発見しました。あなたはコードをフォローするために左から右へ自然に読むので、私はそれをたくさん使います。
itmuckel

回答:


61

私は少し投機的です...

カルチャー|>F#の「カルチャー」で重要な演算子だと思います.。おそらくHaskell でも同様です。F#には関数構成演算子<<がありますが、F#コミュニティはHaskellコミュニティよりもポイントフリースタイルを使用する傾向があると思います。

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

let f = exp

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

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

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

(個人的には、ポイントフリースタイルは判読できませんが、慣れるまでは、すべての新しいものや異なるものは判読できないように思われます。)

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


7
文化の違いに同意します。従来のHaskellは.and $を使用しているため、人々は引き続きそれらを使用しています。
Amok、

9
ポイントフリーは、ポイントポイントより読みやすい場合があります。通常、マップやフィルターなどの関数の引数で使用して、ラムダが混乱するのを防ぎます。私は時々それをトップレベルの関数でも使用しますが、それほど頻繁ではなく、それが単純なものである場合のみです。
ポールジョンソン

2
F#に関する限り、問題の選択肢があまりないという意味で、私はそこに文化をあまり見ていません(あなたとGaneshが言及した理由によります)。つまり、どちらもHaskellでは実行可能だと思いますが、F#はパイプライン演算子を使用するための装備が間違いなく優れています。
Kurt Schelfthout、2009年

2
はい、左から右に読む文化:)数学でさえもそのように教えるべきです...
ニコラ

1
@nicolasの左から右は任意の選択です。右から左に慣れることができます。
Martin Capodici、2015年

86

(|>)左から右への型チェックのため、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、この要件を解決できます。ただし、これは型システムを複雑にします。


1
興味深いのは、Haskellに(。)に似た(<|)演算子があり、データの方向が右から左であることです。しかし、そのための型解決はどのように機能しますか?
The_Ghost 2009

7
(<|)は実際にはHaskellの($)に似ています。左から右への型チェックは、.Valueなどの問題を解決する場合にのみ必要であるため、(<|)は他のシナリオで、または明示的な型注釈を使用する場合に正常に機能します。
GS-

44

より多くの推測、今回は主にHaskell側から...

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

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

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


こんにちはネイサン、「フリップ($)」はHaskellプラットフォームのどこかで事前定義されていますか?「(|>)」という名前は、別の意味でData.Sequenceで既に定義されています。まだ定義されていない場合、それを何と呼びますか?"($>)= flip($)"で行くことを考えています
mattbh

2
@mattbh:Hoogleで見つけることができるわけではありません。については知りませんでしたがData.Sequence.|>$>競合を避けるために妥当なようです。正直なところ、見栄えの良い演算子はそれほど多くないので|>、両方に使用し、ケースバイケースで競合を管理します。(また、私は別名Data.Sequence.|>として単に誘惑されますsnoc
ネイサンShively-Sanders

2
($)および(|>)アプリケーションではありません構成されています。2つは(質問のメモのように)関連していますfcが、同じではありません((Control.Arrow.>>>)関数用です)。
ネイサンShively-Sanders

11
実際には、F#は何よりも|>UNIXを思い出させ|ます。
ケビンカントゥ

3
|>F#のもう1つの利点は、Visual StudioのIntelliSenseに優れたプロパティがあることです。と入力|>すると、左側の値に適用できる関数のリストが表示され.ます。これは、オブジェクトの後に入力した場合と同じです。
クリストファージョンソン

32

混乱していると思います。Haskellの(.)はF#の()と同等>>です。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


2
あなたはそれがHaskellのようなものだと言うよう$: -演算子逆に、あなたも簡単としてそれを定義したことができますa |> b = flip ($)あなたが行うことができます例えばパイプラインのF#に同等になる[1..10] |> map f
VIS

8
.)は()と同じだと思いますが、<<>>)は逆の構成です。それは( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3vs( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
Dobes Vandermeer 2013年

「使用>>とんでもない程度」の場合は-1。(まあ、私はしませんでしたが、あなたは私の主張を理解しました)。F#のFは「機能的」であるため、機能構成は正当です。
フロリアンF

1
確かに、機能構成は合法であることに同意します。「一部のF#の人」とは、私自身のことです。自分のブログ。:P
AshleyF 2016年

.同等ではないと思います>>。F#にあるかどうかはわかりませんが<<、Elmの場合と同じです。
マットジョイナー2018

28

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


Haskellが選択&する理由は何|>ですか?の|>方がはるかに直感的で、Unixパイプ演算子を思い出します。
yeshengm

16

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]

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


いいですね、(>>>)は(|>)とほぼ同等ですか?
Khanzor

@Khanzor正確ではありません。(|>)は引数を適用し、(>>>)は主に関数構成(または類似のもの)です。次に、固定性の違いがあると思います(チェックしませんでした)。
サスタニン

15

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

>>> 実際にはControl.Arrowからのものであり、単なる関数以外にも機能します。


>>>で定義されていControl.Categoryます。
Steven Shaw、

13

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

|>演算子は主にF#で一般的です。この演算子は、主に不純なコードで発生する2つの制限を隠すのに役立つためです。

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

サブタイピングは名義ではなく構造であるため、前の制限はOCamlには存在しないことに注意してください。そのため、型推論が進むにつれて、構造型は統一によって簡単に調整できます。

Haskellは別のトレードオフを取り、これらの制限を解除できる、主に純粋なコードに焦点を当てることを選択します。


8

F#のパイプ転送演算子(|>)は、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 |>データ関数(&)のドキュメントを参照してください

infixl = infix +左結合性

infixr = infix +右結合性


(。)

.)は関数構成を意味します。これは、数学では(fg)(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($)で定義されます。

.)はcreate 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
  • Lua:RxLua
  • Ruby:Rx.rb
  • Python:RxPY
  • Go:RxGo
  • Groovy:RxGroovy
  • JRuby:RxJRuby
  • コトリン:RxKotlin
  • Swift:RxSwift
  • PHP:RxPHP
  • エリクサー:reaxive
  • ダーツ:RxDart

1

これはHaskellを試した最初の日(Rustおよび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
あなたも:)中置演算子中置を定義することができます x |> f = f x
ジェイミー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.