功能应用:为什么在这里使用$?
题
不久前,我问了一个关于$ 的问题,并得到了有用的答案 - 事实上,我认为我理解了如何使用它。
看来我错了:(
此示例显示在教程中:
instance Monad [] where
xs >>= f = concat . map f $ xs
我不能为我的生活看到为什么在那里使用$; ghci也没有帮助我,因为我在那里进行的测试似乎表明与简单省略$的版本等效。有人可以为我澄清一下吗?
解决方案
此处使用$
,因为它的优先级低于普通函数应用程序。
编写此代码的另一种方法是:
instance Monad [] where
xs >>= f = (concat . map f) xs
这里的想法是先构造一个函数(concat . map f
),然后将其应用于它的参数(xs
)。如图所示,这也可以通过简单地在第一部分周围加上括号来完成。
请注意,无法在原始定义中省略.
,这将导致类型错误。这是因为函数组合运算符(<=>)的优先级低于普通函数应用程序,有效地将表达式转换为:
instance Monad [] where
xs >>= f = concat . (map f xs)
这没有意义,因为函数组合运算符的第二个参数根本不是函数。虽然以下定义确实有意义:
instance Monad [] where
xs >>= f = concat (map f xs)
顺便说一下,这也是我更喜欢的定义,因为在我看来它更清晰。
其他提示
我想解释为什么恕我直言这不是那里使用的风格:
instance Monad [] where
xs >>= f = concat (map f xs)
concat . map f
是所谓的无点式写作的一个例子;其中pointfree意味着<!>“;没有应用点<!>”;请记住,在数学中,在表达式y=f(x)
中,我们说f
适用于点x
。在大多数情况下,您实际上可以做最后一步,替换:
f x = something $ x
带
f = something
喜欢f = concat . map f
,这实际上是无点风格。
哪个更清楚是有争议的,但是无点样式给出了一个不同的观点,这也是有用的,所以有时甚至在不完全需要时也会使用。
这里使用$的原因是(。)的类型签名:
(.) :: (b -> c) -> (a -> c) -> a -> c
我们有
map f :: [a] -> [[b]]
和
concat :: [[b]] -> [b]
所以我们最终得到了
concat . map f :: [a] -> [b]
和(。)的类型可以写成
(。)::([[b]] - <!> gt; [b]) - <!> gt; ([a] - <!> gt; [[b]]) - <!> gt; [a] - <!> gt;并[b]
如果我们使用concat . map f xs
,我们会看到
map f xs :: [[b]]
因此不能与(。)一起使用。 (类型必须是(。)::(a - <!> gt; b) - <!> gt; a - <!> gt; b