46

Learn You a Haskellの第 11 章では、次の定義が紹介されています。

instance Applicative ((->) r) where
    pure x = (\_ -> x)
    f <*> g = \x -> f x (g x)

ここで、著者は特徴のない手を振っています (「<*> のインスタンスの実装は少し不可解なので、[説明せずに実際に表示する] だけにするのが最善です」)。ここの誰かが私がそれを理解するのを手伝ってくれることを願っています。

適用可能なクラス定義によると、(<*>) :: f (a -> b) -> f a -> f b

((->)r)インスタンスでは、次のように置き換えますfr->(a->b)->(r->a)->(r->b)

最初の質問は、その型から にどうやって取得するf <*> g = \x -> f x (g x)かということです。

しかし、最後の式を当然のことと思っていても、GHCi に与えた例と一致させるのに苦労しています。例えば:

Prelude Control.Applicative> (pure (+5)) <*> (*3) $ 4
17

代わりに、この式はf <*> g = \x -> f (g x)(このバージョンでは のx後に表示されないことに注意してくださいf

ごちゃごちゃしていると思いますので、よろしくお願いします。

4

4 に答える 4

39

まず、fmapが applicative に対してどのように定義されているかを思い出してください。

fmap f x = pure f <*> x

これは、例が と同じであることを意味します(fmap (+ 5) (* 3)) 4。関数のfmap関数は単なる構成であるため、正確な式は と同じ((+ 5) . (* 3)) 4です。

ここで、インスタンスがそのように記述されている理由について考えてみましょう。基本的に、ファンクター内の関数を<*>ファンクター内の値に適用します。に特殊化すると、関数 from によって返される関数を関数 from によって返される値に(->) r適用することを意味します。関数を返す関数は、2 つの引数の単なる関数です。したがって、本当の問題は次のとおりです。からの関数によって返される値に、2 つの引数 (およびを返す)の関数をどのように適用しますか?rrrabar

最初に注意すべきことは、 type の値を返す必要があることです(->) r。つまり、結果も からの関数でなければなりませんr。参考までに、<*>関数は次のとおりです。

f <*> g = \x -> f x (g x)

型の値を取る関数を返したいのでrx :: r. 返す関数には type が必要r -> bです。type の値を取得するにはどうすればよいbでしょうか? さて、関数がありますf :: r -> a -> b。は結果関数の引数になるのでr、無料で取得できます。これで、 からの関数ができましたa -> b。したがって、 type の値がある限り、 typeaの値を取得できますb。しかし、どのようにして type の値を取得するのaでしょうか? さて、別の機能がありますg :: r -> a。したがって、 type の値r(パラメーターx) を取得し、それを使用して type の値を取得できますa

したがって、最終的なアイデアは単純です。パラメータを使用して、最初に type の値をaにプラグインして取得しますg。パラメータには type rgtyper -> aがあるため、a. 次に、パラメータと新しい値の両方を に接続しますfftype があるため、両方が必要r -> a -> bです。と の両方を接続するrと、aができb1ます。パラメーターはラムダにあるため、結果には typer -> bがあり、これが必要です。

于 2012-08-04T18:28:24.653 に答える
31

あなたの最初の質問を見て、あなたが見逃したかもしれない微妙だが非常に重要なポイントが1つあると思います. LYAH の元の例を使用します。

(+) <$> (+3) <*> (*100) $ 5

これは次と同じです。

pure (+) <*> (+3) <*> (*100) $ 5

ここで重要なのはpurebeforeで、Applicative として(+)ボクシングの効果があります。(+)がどのようpureに定義されているかを見ると、ボックス化を解除するには、追加の引数を指定する必要があることがわかります。引数は何でもかまいません。に適用する<*>(+) <$> (+3)

\x -> (pure (+)) x ((+3) x)

に注意してください、私たちはunbox(pure (+)) xに適用xしています。だから私たちは今持っていますpure(+)

\x -> (+) ((+3) x)

(*100)get(+) <$> (+3) <*> (*100)と applyに追加すると、 get<*>が得られます

\y -> (\x -> (+) ((+3) x)) y ((*100) y) {Since f <*> g = f x (g x)}

5  -> (\x -> (+) ((+3) x)) 5 ((*100) 5)

(\x -> (+) ((+3) x)) 5 (500)

5 -> (+) ((+3) 5) (500)

(+) 8 500

508

結論として、xafterfは二項演算子の最初の引数ではなく、 内の演算子を UNBOX するために使用されpureます。

于 2015-06-14T08:16:29.497 に答える
19

「例では、 の代わり((->)r)f: r->(a->b)->(r->a)->(r->b)

なぜ、それは正しくありません。それは実際に(r->(a->b)) -> (r->a) -> (r->b)は であり、それは と同じ(r->a->b) -> (r->a) -> r -> bです。つまり、中置記号と中置記号の右側の引数を返す関数を、中置記号の LHS だけを取り、その結果を返す関数にマップします。例えば、

Prelude Control.Applicative> (:) <*> (\x -> [x]) $ 2
[2,2]
于 2012-08-04T18:23:40.443 に答える