思いつくこと(.) . (.)
は実際にはかなり簡単です、それはそれが何をするかの背後にある直感であり、理解するのは非常に難しいです。
(.)
式を「パイプ」スタイルの計算に書き直すときに、非常に遠くまで到達します(|
シェルで考えてみてください)。ただし、1つだけを取る関数で複数の引数を取る関数を作成しようとすると、使用するのが面倒になります。concatMap
例として、次の定義を考えてみましょう。
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f xs = concat (map f xs)
取り除くことxs
は単なる標準的な操作です:
concatMap f = concat . map f
ただし、を取り除く「良い」方法はありませんf
。これは、2つの引数が必要であり、最終結果map
に適用したいという事実が原因です。concat
もちろん、いくつかのポイントフリーのトリックを適用して、次のことだけで逃げることができます(.)
:
concatMap f = (.) concat (map f)
concatMap f = (.) concat . map $ f
concatMap = (.) concat . map
concatMap = (concat .) . map
しかし、残念ながら、このコードの可読性はほとんどなくなっています。代わりに、必要なことを正確に実行する新しいコンビネータを導入します。最初の関数の最終結果に2番目の関数を適用します。
-- .: is fairly standard name for this combinator
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(f .: g) x y = f (g x y)
concatMap = concat .: map
いいでしょう、それはやる気のためです。ポイントフリービジネスに取り掛かりましょう。
(.:) = \f g x y -> f (g x y)
= \f g x y -> f ((g x) y)
= \f g x y -> f . g x $ y
= \f g x -> f . g x
さて、ここに興味深い部分があります。これは、行き詰まったときに通常役立つポイントフリーのトリックのもう1つです.
。プレフィックス形式に書き直して、そこから続行しようとします。
= \f g x -> (.) f (g x)
= \f g x -> (.) f . g $ x
= \f g -> (.) f . g
= \f g -> (.) ((.) f) g
= \f -> (.) ((.) f)
= \f -> (.) . (.) $ f
= (.) . (.)
直感に関しては、あなたが読むべきこのとても素晴らしい記事があります。についての部分を言い換えます(.)
:
私たちのコンビネータが何をすべきかをもう一度考えてみましょう:それはの結果の結果f
に適用されるべきです(私は意図的に前の部分で最終結果を使用していました、それはあなたが完全に適用したときに実際に得られるものです-モジュロ統一型変数を別のものと関数型-関数、ここでの結果は、いくつかのアプリケーションにすぎません)。g
g
g x
x
結果に適用f
することはどういう意味ですか?さて、ある値に適用したら、結果を取得して適用します。おなじみのように聞こえます:それが何をするかです。g
g
f
(.)
result :: (b -> c) -> ((a -> b) -> (a -> c))
result = (.)
さて、これらのコンビネータの合成(私たちの言葉)は単なる関数合成であることがわかります。つまり、次のようになります。
(.:) = result . result -- the result of result