Подтвердить что ты не робот

Как и почему ap определяется как идентификатор liftM2 в Haskell

В то время как вы пытаетесь лучше понять Аппликативный, я рассмотрел определение < * > , которое имеет тенденцию быть определено как 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

Я не понимаю, как просто переходя в id, соответствующая часть сигнатуры типа преобразуется от (a1 -> a2 -> r) -> m a1 в m (a -> b). Что мне здесь не хватает?

4b9b3361

Ответ 1

Тип переменной a из 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.

Ответ 2

Самый верный ответ определенно правильный и работает быстро и эффективно от самих типов. Как только вы хорошо разбираетесь в Haskell (отказ от ответственности: я не знаю), тогда это гораздо более эффективный способ понять это, чем проложить определение функций.

Но поскольку мне недавно пришлось бороться именно с этой проблемой с ap, пока я работал над The Monad Challenges, я решил поделиться мой опыт, потому что он может предоставить некоторую дополнительную интуицию.

Во-первых, так же, как спрашивает 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.

Предположим, что мы должны были передать функциональную Monad как первую часть выше, часть ma. Это может быть что-то вроде Just (+3) или [(+3), (*2)] - некоторая "функция", живущая внутри контекста Монады.

И мы снабжаем аргументированную Monad как вторую часть, часть mb, такую ​​как Just 5 или [6,7,8] - некоторое "значение", живущее в контексте Monad, которое может служить аргументом для функция, живущая внутри 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: он имитирует извлечение значение из контекста Monad по модулю любой специальной обработки, которая уникальна для этой Monad.

Итак, окончательная строка действительно станет

return $ f someFunction someArgument

Теперь отбросьте назад и запомните, что наша цель при создании ap состоит в вызове someFunction после someArgument внутри контекста Monad. Итак, что бы ни производило использование return, оно должно быть результатом применения функции someFunction someArgument.

Итак, как мы можем сделать два выражения равными

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, возвращая результат внутри контекста Monad.

Это похоже на то, что мы вынудили liftM2 иметь смещение наблюдателя.

И для того, чтобы это получилось, a должен иметь тип функции, который идет от "TypeOfb" до "SomeReturnType" или TypeOfb -> SomeReturnType, потому что b - a ожидаемый аргумент. И, конечно, b должен иметь TypeOfb.

Если вы разрешите мне одно злоупотребление нотацией, тогда произвольно позвольте просто использовать символ "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