13

Applicativeをよりよく理解しようとしているときに、<*>の定義を調べました。これは、apとして定義される傾向があり、次のように定義されます。

ap                :: (Monad m) => m (a -> b) -> m a -> m b
ap                =  liftM2 id

liftM2とidの型シグネチャを見ると、次のようになります。

liftM2  :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
id                      :: a -> a

(a1 -> a2 -> r) -> m a1idを渡すだけで、型アノテーションの関連部分がからに変換されるように見える方法がわかりませんm (a -> b)。ここで何が欠けていますか?

4

2 に答える 2

17

afromの型変数idは任意の型でインスタンス化できます。この場合、その型はa -> bです。

したがって、でインスタンス化idしています(a -> b) -> (a -> b)a1ここで、からの型変数liftM2はでインスタンス化され(a -> b)a2でインスタンス化されa、でインスタンスr化されていますb

すべてをまとめるとliftM2、、、でインスタンス化され((a -> b) -> (a -> b)) -> m (a -> b) -> m a -> m bますliftM2 id :: m (a -> b) -> m a -> m b

于 2011-03-19T00:05:48.343 に答える
3

一番の答えは間違いなく正しく、タイプだけですばやく効率的に機能します。Haskellが得意になったら(免責事項:私はそうではありません)、これは関数定義をざっと読むよりもはるかに効率的にこれを理解する方法です。

しかし、最近、モナドチャレンジapに取り組んでいる間、まさにこの問題に苦労しなければならなかったので、それがいくつかの追加の直感を提供するかもしれないので、私は私の経験を共有することにしました。

まず、Monad Challengesが尋ねるのと同じように、この名前bindを使用してプライマリMonad演算子を参照し>>=ます。これは大いに役立つと思います。

独自のバージョンを定義すると、次のliftM2ことができます。

liftM2 :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
liftM2 f ma mb = 
    ma `bind` \a -> 
    mb `bind` \b -> 
    return $ f a b

apこれを使って作成したいと思います。関数をしばらくそのままにして、に適切な関数を選択したと仮定してf、これがどのように機能するかを考えてみましょう。apf

上記の最初の部分である部分として、関数値のモナドを渡すと仮定しmaます。Just (+3)それは、または[(+3), (*2)]-モナドコンテキスト内に存在するいくつかの「関数」のようなものである可能性があります。

そして、引数値のモナドを2番目の部分として提供しますmb。たとえば、Just 5または[6,7,8]-モナドコンテキストに存在する「値」は、内部に存在する関数の引数として機能しますma

その後、私たちは持っているだろう

liftM2 f (m someFunction) (m someArgument) = 
    (m someFunction) `bind` \a ->
    (m someArgument) `bind` \b ->
    return $ (f a b)

そして、に続くラムダ関数の内部では、それがそうなること、そしてそうなることbindを知っています-それが何をするかです:それは、そのモナドに固有の特別な処理を法として、モナドコンテキストからの値の抽出をシミュレートします。asomeFunctionbsomeArgumentbind

そのため、最終行は実際に

return $ f someFunction someArgument

さて、一歩下がって、作成の目標はモナドコンテキストの内部をap呼び出すことであることを思い出してください。したがって、yieldの使用が何であれ、関数適用の結果である必要があります。someFunctionsomeArgumentreturnsomeFunction someArgument

では、どうすれば2つの式を等しくすることができますか

f someFunction someArgument ==? someFunction someArgument

さて、それなら私たちは次のようなx = (someFunction someArgument)関数を探していますf

f x = x

fしたがって、それが必要であることがわかりますid

最初に戻ると、これは私たちが探していることを意味しますliftM2 id

基本的に、が操作可能な関数である場合はそうするliftM2 id ma mbつもりだと言っていますが、モナドコンテキスト内で結果を返しながら、「そのままにして」、その処理を実行します。m (id a b)abidab

それは、私たちliftM2が傍観者の偏見を強いられたようなものです。

そして、それがうまくいくためにaは、「TypeOfb」から「SomeReturnType」に変わる関数型、またはTypeOfb -> SomeReturnTypeba期待される引数である必要があります。そしてもちろんb持っている必要がありTypeOfbます。

表記の乱用を1回許可する場合は、任意に記号「a」を使用して「TypeOfb」を表し、記号「b」を使用して「SomeReturnType」を表します。

`b` --> "a" is its type
`a` --> "a -> b" is its type

その場合、の型アノテーションは次のようにapなります。

ap :: Monad m => m (TypeOfB -> SomeReturnType) -> m TypeOfB -> m SomeReturnType
==>
ap :: Monad m => m (a -> b) -> m a -> m b  
于 2016-01-29T04:38:21.390 に答える