函数应用:这里为啥用$?

Posted

技术标签:

【中文标题】函数应用:这里为啥用$?【英文标题】:Function application: Why is $ used here?函数应用:这里为什么用$? 【发布时间】:2010-09-30 19:42:22 【问题描述】:

前段时间,我问了一个question about $,得到了有用的答案——其实我以为我知道怎么用了。

看来我错了:(

此示例显示在教程中:

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

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

【问题讨论】:

【参考方案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

【讨论】:

【参考方案2】:

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

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 替换了 pointless 并修复了一些示例。

【讨论】:

我同意,无意义的样式可能很有用,但是我不喜欢在您实际将参数应用于函数时使用它,就像在这种情况下一样。 另外,如果你不小心你可能会做得过火,这是我昨天写的一些代码 ;-) maybe id (((sp . ("string " ++)) .) . shows) mx 'concat . map f $ xs" 不是无点的,以无点样式编写的列表 monad 的绑定将是 '(>>=) = flip concatMap' 或类似的。这是一个无点样式实际上非常清晰的示例。请参阅@987654321 @ 我知道这不是point-free,但它类似于pointfree 风格;因为 >>= 已经反转了参数,所以也需要翻转(我忘记了使用 flip 的可能性,这是真的)。​​【参考方案3】:

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

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)

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

【讨论】:

谢谢。我没有意识到 $ 的优先级甚至低于 (.);我一直在精神上将带有 (.) 的行解析为单独的“块”,但情况并非总是如此!我同意按照您的方式编写它会更清晰,或者: concat $ map f xs 我自己更喜欢 "xs >​​>= f = concat $ map f xs"。 我也是,或者在这种情况下:xs >>= f = concatMap f xs "xs >​​>= f = flip (join.fmap)" :) 添加噪声:(>>=) = 翻转 concatMap -- =)

以上是关于函数应用:这里为啥用$?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Rust 函数和 FFI C++ 函数以相反的顺序执行?

SQL中的WHERE子句中为啥不允许应用聚集函数呢?请通俗的解释一下或者谈谈自己的见解!

为啥调用“应用”而不是直接调用函数?

如果应用了 css 过渡,为啥动画回调函数会立即触发?

为啥应用 tranwrd 函数后最后一个字符被删除

为啥我不能将 clickhouse json 函数应用于“FORMAT JSON”查询结果?