8

前段时间,我问了一个关于 $的问题,得到了有用的答案——事实上,我以为我理解了如何使用它。

看来我错了:(

此示例显示在教程中:

instance Monad [] where
   xs >>= f = concat . map f $ xs

我一生都无法理解为什么在那里使用 $ ;ghci 也没有帮助我,因为即使我在那里进行的测试似乎也显示出与简单地省略 $ 的版本等价。有人可以为我澄清一下吗?

4

3 回答 3

11

$这里使用 是因为它的优先级低于普通函数应用程序。编写此代码的另一种方法是:

instance Monad [] where
   xs >>= f = (concat . map f) xs

这里的想法是首先构造一个函数 ( concat . map f),然后将其应用于它的参数 ( xs)。如图所示,这也可以通过简单地在第一部分周围加上括号来完成。

请注意,$在原始定义中省略 是不可能的,这将导致类型错误。这是因为函数组合运算符 (the .) 的优先级低于普通函数应用程序,从而有效地将表达式转换为:

instance Monad [] where
  xs >>= f = concat . (map f xs)

这没有意义,因为函数组合运算符的第二个参数根本不是函数。尽管以下定义确实有意义:

instance Monad [] where
  xs >>= f = concat (map f xs)

顺便说一句,这也是我更喜欢的定义,因为在我看来它更清晰。

于 2009-01-11T21:08:17.343 回答
3

我想解释一下为什么恕我直言,这不是那里使用的样式:

instance Monad [] where
  xs >>= f = concat (map f xs)

concat . map f是所谓的pointfree-style写作的一个例子;其中 pointfree 的意思是“没有应用点”。请记住,在数学中,在表达式y=f(x)中,我们说那f是应用在点上x。在大多数情况下,您实际上可以执行最后一步,替换:

f x = something $ x

f = something

喜欢f = concat . map f,这实际上是无点风格。哪个更清楚是有争议的,但无点风格给出了不同的观点,这也很有用,所以有时即使在不完全需要时也会使用。

编辑:在我应该感谢 Alasdair 的评论之后,我用 pointfree 替换了无意义的并修复了一些示例。

于 2009-01-11T22:43:27.057 回答
1

这里使用 $ 的原因是 (.) 的类型签名:

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

在这里我们有

map f :: [a] -> [[b]]

concat :: [[b]] -> [b]

所以我们最终得到

concat . map f :: [a] -> [b]

(.) 的类型可以写成

(.) :: ([[b]] -> [b]) -> ([a] -> [[b]]) -> [a] -> [b]

如果我们使用concat . map f xs,我们会看到

map f xs :: [[b]]

因此不能与 (.) 一起使用。(类型必须是 (.) :: (a -> b) -> a -> b

于 2009-01-22T10:46:20.547 回答