Извлечение монады из аппликативной (<*>) путаницы - PullRequest
0 голосов
/ 22 января 2019

Прорабатывая Книгу Хаскелла, мой мозг ломается на следующем примереЯ действительно не знаю, что функция flip делает в строке 21

1 class Functor f where
2   fmap :: (a -> b) -> f a -> f b
3
4 class Functor f => Applicative f where
5   pure :: a -> f a
6   (<*>) :: f (a -> b) -> f a -> f b
7
8 class Applicative f => Monad f where
9   return :: a -> f a
10   (>>=) :: f a -> (a -> f b) -> f b
11
12 instance Functor ((->) r) where
13   fmap = (.)
14
15 instance Applicative ((->) r) where
16   pure = const
17   f <*> a = \r -> f r (a r)
18
19 instance Monad ((->) r ) where
20   return = pure
21   ra >>= arb = flip arb <*> ra


-- flip :: (a -> b -> c) -> b -> a -> c

-- ra >>= arb = flip arb <*> ra

. Насколько я понимаю, flip принимает функцию, которая принимает два аргумента, затем два отдельных аргумента и возвращает значение.В примере связывания мы передаем arb как (a -> b -> c), затем <*> как b в подписи сальто и, наконец, ra как a?Я не вижу этого.

Я попытался сделать типы более специфичными для моего реального примера, чтобы вы могли переписать <*> как

(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)

, и я могу сделать то же самое дляbind

(>>=) :: (r -> a) -> (a -> r -> b) -> (r -> b)

, чтобы даже такой дурачок, как я, мог видеть, что если бы мы могли поменяться <*>, мы могли бы выстроиться в линию, как

(<???>) :: (r -> a) -> (r -> a -> b) -> (r -> b)
(>>=) ::   (r -> a) -> (a -> r -> b) -> (r -> b)

, но, глядя на вторые аргументы,первый хочет r в качестве первого аргумента, а bind хочет a

Так что каким-то образом flip - это пример книги, который делает это для нас, но я действительно не понимаю как.Любая помощь будет принята с благодарностью.

Спасибо!

1 Ответ

0 голосов
/ 22 января 2019

Точка путаницы верхнего уровня, я думаю: flip модифицирует arb, а не модифицирует <*>, как вы, кажется, верите. Мы «изменили» <*>, чтобы иметь «правильный» порядок аргументов, просто передав <*> свои аргументы в обратном порядке, мы их получили!

Теперь для деталей. У нас, как вы отметили:

(>>=) :: (r -> a) -> (a -> r -> b) -> (r -> b)

Итак, с левой стороны написано

ra >>= arb = ...

то, что мы имеем в области видимости:

ra :: r -> a
arb :: a -> r -> b

(Обратите внимание, как имена были выбраны, чтобы отразить типы!) Переписав тип, который вы дали для flip, мы имеем

flip :: (a -> b -> c) -> b -> a -> c -- original
flip :: (a -> r -> b) -> r -> a -> b -- rename variables

отсюда:

flip arb :: r -> a -> b

Теперь вспомните тип (<*>), который вы написали:

(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)

Итак, для первого аргумента (<*>) мы хотим что-то типа r -> a -> b. Привет! flip arb имеет такой тип! Для второго аргумента нам нужно что-то типа r -> a. И нам снова повезло, потому что ra имеет такой тип, так что ...

flip arb <*> ra :: r -> b

(Как обычно с инфиксными операторами, это применение оператора (<*>) с аргументами flip arb и ra.) Какой тип мы надеялись получить? Ну, теперь мы возвращаемся к типу (>>=):

(>>=) :: (r -> a) -> (a -> r -> b) -> (r -> b)

После получения двух аргументов предполагается, что он возвращает что-то типа r -> b. Круто, вот что мы построили.

ra >>= arb = flip arb <*> ra
...