Помощь в понимании кода без кода
При игре с Pointfree мне был представлен фрагмент кода, который я не могу понять.
:pl map (\x -> x * x) [1..10]
-- map (join (*)) [1..10]
Моя основная проблема заключается в том, что я не понимаю, как работает join
. Я понимаю, что он "удаляет" один слой монадической упаковки (m (m a)
to m a
). Я полагаю, что это сводится к чему-то вроде [1..10] >>= (\x -> [x * x])
, но я действительно не понимаю, как вводится "дополнительный слой". Я получаю этот join x = x >>= id
, но затем я все еще зацикливаюсь на том, как это "дублирует" каждое значение, так что (*)
получает два аргумента. Меня это беспокоило примерно полчаса, и я в основном раздражаюсь, потому что чувствую, что у меня есть все части головоломки, но, похоже, они не подходят друг к другу...
P.S. Не волнуйтесь, я бы действительно не использовал эту версию с плавающей точкой, это чисто любопытство и попытка лучше понять Haskell.
Ответы
Ответ 1
join
использует экземпляр Monad
для (->) a
, как определено в Control.Monad.Instances
. Экземпляр похож на Reader
, но без явной оболочки. Он определяется следующим образом:
instance Monad ((->) a) where
-- return :: b -> (a -> b)
return = const
-- (>>=) :: (a -> b) -> (b -> a -> c) -> (a -> c)
f >>= g = \x -> g (f x) x
Если теперь уменьшить join
с помощью этого экземпляра:
join
(>>= id)
flip (\f g x -> g (f x) x) (\a -> a)
(\f x -> (\a -> a) (f x) x)
(\f x -> f x x)
Как вы можете видеть, экземпляр для (->) a
делает join
для функции, которая дважды применяет аргумент. Из-за этого join (*)
просто \x -> x * x
.