理解pointfree代码的帮助

17

在尝试使用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,但我仍然不知道它是如何将每个值“复制”以使(*)获得两个参数的。这已经在困扰我大约半个小时了,而且我主要是对自己感到恼火,因为我觉得我已经有所有的拼图,但似乎无法将它们拼接起来...
附言:别担心,我并不会真的使用这个pointfree版本,这只是出于纯好奇和试图更好地理解Haskell。

join :: m (m a) -> m a 并不是真正意义上的“去除”单子层,更准确地说,它将两个层压缩成一个。因此得名,确实如此... - Ben Millwood
1个回答

21

join 使用了 Control.Monad.Instances 中定义的对于 (->) aMonad 实例。此实例类似于 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


5
你也可以从参数泛化得出这个结论。在单子 (->) a 中,join 的类型是 ((->) a) ((->) a b) -> ((->) a b),简化后变成了 (a -> a -> b) -> a -> b。很明显,join 唯一能做的就是将 a 参数传递给函数两次来获得一个 b 值。(当然,除非使用底部值)。 - hammar
2
@hammar,有没有一种程序可以通过查看类型签名来找出函数的唯一合理定义呢?(如果可能的话) - fuz
8
我已上传了 Djinn 的新版本。 - augustss
1
谢谢,这正是我所缺少的! :-) 我实际上玩了一下 ((->) a) 的 monad 实例,但没有走远,可能因为这里已经接近凌晨 2 点了,而且我很累。再次感谢! - Michael Kohl
1
这就是了,我之前只是接受了,没有点赞(不小心)。 - Michael Kohl
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接