一番の答えは間違いなく正しく、タイプだけですばやく効率的に機能します。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
、これがどのように機能するかを考えてみましょう。ap
f
上記の最初の部分である部分として、関数値のモナドを渡すと仮定し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
を知っています-それが何をするかです:それは、そのモナドに固有の特別な処理を法として、モナドコンテキストからの値の抽出をシミュレートします。a
someFunction
b
someArgument
bind
そのため、最終行は実際に
return $ f someFunction someArgument
さて、一歩下がって、作成の目標はモナドコンテキストの内部をap
呼び出すことであることを思い出してください。したがって、yieldの使用が何であれ、関数適用の結果である必要があります。someFunction
someArgument
return
someFunction 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)
a
b
id
a
b
それは、私たちliftM2
が傍観者の偏見を強いられたようなものです。
そして、それがうまくいくためにa
は、「TypeOfb」から「SomeReturnType」に変わる関数型、またはTypeOfb -> SomeReturnType
、b
がa
期待される引数である必要があります。そしてもちろん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