函数应用:这里为啥用$?
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++ 函数以相反的顺序执行?