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

Помощь в понимании кода без кода

При игре с 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.

4b9b3361

Ответ 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.