以前、Parsecの適用可能なファンクターインスタンスのみを使用するようにモナディックコードを変換することについて質問しました。残念ながら、私は文字通り尋ねた質問に答えるいくつかの返信を受け取りましたが、実際には多くの洞察を与えませんでした。では、もう一度試してみましょう...
これまでの私の知識を要約すると、適用可能なファンクターは、モナドよりもいくらか制限されているものです。「少ないほど多い」という伝統では、コードが実行できることを制限すると、狂ったコード操作の可能性が高まります。とにかく、多くの人は、モナドの代わりにアプリケーションを使用することが、可能な場合は優れたソリューションであると信じているようです。
Applicative
クラスはで定義されています。Control.Applicative
そのHaddockのリストは、クラスメソッドとユーティリティ関数をそれらの間にある膨大な数のクラスインスタンスでうまく分離し、画面上のすべてを一度にすばやく表示することを困難にします。しかし、適切な型署名は
pure :: x -> f x
<*> :: f (x -> y) -> f x -> f y
*> :: f x -> f y -> f y
<* :: f x -> f y -> f x
<$> :: (x -> y) -> f x -> f y
<$ :: x -> f y -> f x
完全に理にかなっていますよね?
さて、Functor
すでに私たちに与えますfmap
、それは基本的に<$>
です。x
つまり、からへの関数が与えられた場合、y
をにマップすることができf x
ますf y
。Applicative
2つの本質的に新しい要素を追加します。1つはpure
、とほぼ同じタイプですreturn
(およびさまざまな圏論クラスの他のいくつかの演算子)。もう1つは<*>
、関数のコンテナーと入力のコンテナーを取得して、出力のコンテナーを生成する機能を提供するです。
上記の演算子を使用すると、次のようなことを非常にうまく行うことができます
foo <$> abc <*> def <*> ghi
これにより、N-ary関数を取得し、任意のNに簡単に一般化できる方法でN個のファンクターからその引数を取得できます。
これだけ私はすでに理解しています。私がまだ理解していない2つの主要なことがあります。
まず、関数*>
、、<*
および<$
。それらのタイプから、、、<* = const
および*> = flip const
は<$
類似したものである可能性があります。おそらく、これはこれらの関数が実際に何をするかを説明していません。(??!)
次に、Parsecパーサーを作成する場合、通常、各解析可能エンティティは次のようになります。
entity = do
var1 <- parser1
var2 <- parser2
var3 <- parser3
...
return $ foo var1 var2 var3...
アプリケーションファンクターでは、このように中間結果を変数にバインドできないため、最終段階でそれらを収集する方法に戸惑います。これを行う方法を理解するのに十分なほど、アイデアに頭を悩ませることはできませんでした。