Помощь в понимании кода pointfree - PullRequest
17 голосов
/ 23 июля 2011

При игре с Pointfree мне подарили фрагмент кода, который я не могу понять.

:pl map (\x -> x * x) [1..10]
-- map (join (*)) [1..10]

Моя главная проблема в том, что я не понимаю, как join работает здесь. Я понимаю, что он «удаляет» один слой монадической обертки (m (m a) до m a). Я полагаю, что это сводится к чему-то вроде [1..10] >>= (\x -> [x * x]), но я не совсем понимаю, как вводится «дополнительный слой». Я получаю это join x = x >>= id, но затем я все еще застрял на том, как это «дублирует» каждое значение, так что (*) получает два аргумента. Это беспокоит меня уже около получаса, и я в основном раздражен собой, потому что я чувствую, что у меня есть все кусочки головоломки, но я не могу совмещать их вместе ...

P.S. Не волнуйтесь, я бы не стал использовать эту бессмысленную версию, это просто любопытство и попытка лучше понять Хаскелл.

1 Ответ

21 голосов
/ 23 июля 2011

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.

...