Haskell関数構成(。)および関数適用($)イディオム:正しい使用


129

私はReal World Haskellを読んでいて、終わりに近づいていますが、スタイルの問題が(.)($)演算子との関係で私を悩ませています。

他の関数の合成である関数を書くときは、次のように書きます。

f = g . h

しかし、これらの関数の最後に何かを適用すると、次のように書きます。

k = a $ b $ c $ value

しかし、本はそれをこのように書きます:

k = a . b . c $ value

今、私にはそれらは機能的に同等に見え、私の目にはまったく同じことをしています。ただし、見れば見るほど、本のように関数を記述している人々が多くなります。(.)最初に構成し、最後にのみ($)値を追加してロットを評価します(多くのドル構成では誰も行いません)。 。

すべての($)記号を使用するよりもはるかに優れている本の方法を使用する理由はありますか?または、私がここで得ていないいくつかのベストプラクティスはありますか?それとも不必要ですか、まったく心配する必要はありませんか?


4
2番目の例は次のように実行できることに注意してくださいk = a $ b $ c value
Thomas Eding

1
はい、できます。Zifreが以下で説明しますが、何も害を及ぼさず、彼の(そして今はあなたの)コメントが意味をなすので、そのままにすることにしました。ありがとう、結構です。+1 :)
ロバートマサイオリ

2
別の一般的なアプローチはですa . b $ c value。これは、3番目の例ほど好きではありませんが、いくつかの文字を節約します。
John L

回答:


153

私は権威からこれに答えることができると思います。

すべての($)記号を使用するよりも本の方法を使用する理由はありますか?

特別な理由はありません。ブライアンと私はどちらも、ラインノイズを減らすことを好みます。.より静かです$。その結果、本はf . g . h $ x構文を使用します。


3
まあ、私はこれより良い答えを期待することはできません。著者自身から。:)そして、それは理にかなっています、私が読んだように、それはページではるかに静かに見えます。質問に直接回答するため、これを回答としてマークします。ご返信ありがとうございます。そして、事実、本。
ロバートマサイオリ

38
著者に反対しないわけではありませんが、何かを使用するのではなく作成することのメンタルモデルには、もう1つの顕著な理由があると思います。Haskellのユーザーは、むしろ新しい賢い作品として考えたいと思う傾向があります。今では、PHPユーザーのように事前に組み込まれた関数呼び出しの大きな辞書をつなぐだけでなく、匿名で作成した新しい関数を呼び出しています。f.g.hf(g(h()))
エヴァンキャロル

1
それは興味深いステートメントです。「ラインノイズを減らす」と「より静か」です。この用語ではプログラミング言語について考えたことはありませんが、それは完全に理にかなっています。
Rabarberski 2013年

53

それらは実際に同等です:$オペレーターは基本的に何もしないことに注意してください。f $ xはに評価されf xます。の目的$は、その固定動作:右連想で優先順位が最小です。$中置優先順位の代わりにグループ化の括弧を削除して使用すると、コードスニペットは次のようになります。

k = a (b (c (value)))

そして

k = (a . b . c) value

好む理由.を超えるバージョン$美観:バージョンは、上記の非常に括弧バージョンの上の両方を好むため、同じ理由です。

ただし、括弧の代わりに中置演算子を使用することは、Lispとの類似の可能性を回避するための潜在意識の衝動に基づいているのではないかと考える人もいます(冗談...と思いますか?)。


38

私はその中を追加したいf . g $ xf . g意味のある構文単位です。

一方、ではf $ g $ xf $ gは意味のある単位ではありません。連鎖$間違いなく、より不可欠である- 最初の結果を得るgのをxそしてやるf、それに続い行いfoo、それに続いなど

一方、チェーン.はおそらくより宣言的であり、ある意味ではデータフロー中心のビューに近い-一連の関数を構成し、最終的にそれらを何かに適用します。


2
私はHaskellを学習しています(意味のあるものは何もコーディングされていません)が、$を一種の「パイプ」演算子として考えるのが好きです。ターミナルとよく似ています| パイプ(データフローは右から左を除く)ですが、ターミナルプロセスと同様に、各ユニットは明示的に独立しています。
LostSalad 14年

1
$オペレータは、と同様に振る舞うように見える<|F#でオペレータ。私はどちらも同じように定義されていると信じています-実際、F#(<|) a b = a bとHaskellではa $ b = a b、本質的に同等です。
トムガルビン、2015年

@Quackmatic メモリが機能する場合はの(<|) b a = a bよう[1; 2; 3; 4; 5] <| List.map ((+) 1)に使用されるため、F#にあることはかなり確実です。
BalinKingOfMoria CMを2016

@BalinKingOfMoria <|演算子は、関数を値に渡すのではなく、関数に値を渡します。コード例では|>、代わりに演算子を使用します。
トムガルビン2016

@Quackmaticああ。複数のバージョンがあることに気付きませんでした(私はF#をあまり使用していません)。
BalinKingOfMoria CMを2016

18

ドンが言ったように、私にとっての答えは(a)清楚だと思います。(b)コードを編集しているときに、関数がポイントフリースタイルになる場合があるため、最後$に戻ってすべてを変更するのではなく、最後を削除するだけで済みます。マイナーなポイントですが、すばらしい点です。


ええ、それはそのように書くのに良い理由です、もしあなたが関数合成で終わりたいなら、それはより速いです。:)キーストロークを節約してくれてありがとう。もっと重要なのは時間です。+1
ロバートマサイオリ

13

この質問については、このhaskell-cafeスレッドで興味深い議論があります。どうやら、正しい連想性$「ただ間違っている」という少数派の見方があり、選択f . g . h $ xすることf $ g $ h $ xは問題を回避する1つの方法です。


ねえ、あなたはその問題についての完全な議論を見つけました。それは素晴らしいです、私は今読んでいます。+1
ロバートマサイオリ


$!は「明白に間違った」正しい関連性があることに注意してください- なぜ$!演算子右連想?
imz-Ivan Zakharyaschev 2015

3

それは単なるスタイルの問題です。しかし、本のやり方は私にはもっと理にかなっています。すべての関数を構成し、それを値に適用します。

あなたの方法は奇妙に見えるだけで、最後$は不要です。

しかし、それは本当に重要ではありません。Haskellでは、通常、同じことを行うための正しい方法が数多くあります。


3
私は最後の$が不要であることに気づかなかったが、私は持っているべきだった。ありがとうございます。このコメントの意味がわかるように、そのままにしておきます。
ロバートマサイオリ

0

これは非常に古い質問だと思いますが、これには言及されていない別の理由があると思います。

新しいポイントフリー関数を宣言する場合f . g . h、渡した値が自動的に適用されます。ただし、と書い f $ g $ hても動作しません。

著者が合成​​法を好む理由は、それが機能を構築する良い実践につながるからだと思います。

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