我认为,只要$ ...
的权利没有任何内容,我就可以使用(...)
代表...
。
但是,当我尝试重写以下内容时
dist' x y = abs <$> ( (-) <$> x <*> y)
作为
dist x y = abs <$> $ (-) <$> x <*> y
我收到错误:
error: …
parse error on input ‘$’
Perhaps you intended to use TemplateHaskell
|
Compilation failed.
我可以和写括号一起生活,但我只是很好奇。为什么(或何时)$
无法替换()
。
(这是GHC 8.2.2)
答案 0 :(得分:11)
虽然它实际上确实主要用于此目的,但$
运算符在语法上与parenthisation没有任何基本结合 - 它只是另一个中缀运算符(即在参数之间写入的2参数函数) ),例如+
或*
或<$>
。操作员所做的是在其左侧采用一个函数,在右侧采用该函数应该应用的值。因此,例如sqrt (1 - x^2)
可以写成sqrt $ 1 - x^2
,因为sqrt
只是一个函数,而不是将函数应用于其参数而不是平局,您也可以使用$
来做同样的事情,受益于低infixr 0 $
。
该功能也可以是其他内容的组合或部分应用,如
(sum . map (^2)) (0 : xs) ≡ sum . map (^2) $ 0 : xs
但是abs <$> ((-) <$> x <*> y)
不是这种形式:已经是一个中缀表达式。
然而,我们可以利用Haskell允许部分应用中缀运算符这一事实,因为它为您提供了简单函数形式。您需要操作员部分:
abs <$> ((-) <$> x <*> y) ≡ (abs <$>) ((-) <$> x <*> y)
现在这是一个简单的功能申请表,所以你可以做到
(abs <$>) $ (-) <$> x <*> y
当然,这有点失败了,因为操作符部分引入了另一对括号,因此在这种情况下,编写表达式的原始方式可能仍然是最好的。幸运的是,<$>
也有一个简单的函数形式,即
fmap abs $ (-) <$> x <*> y
另一种选择是重写<*>
子表达式:f <$> a <*> b
与liftA2 f a b
相同,无论如何,它作为普通函数绑定比中缀<$>
更紧密。所以
abs <$> ((-) <$> x <*> y) ≡ abs <$> liftA2 (-) x y
或者,完全删除外部fmap
:如果您首先定义
numDist :: Num a => a -> a -> a
numDist a b = abs $ a - b
然后你可以写
dist x y = numDist <$> x <*> y
...或
dist = liftA2 numDist
答案 1 :(得分:10)
$
是一个运算符,与<$>
相同。问题是你有两个二元运算符相互跟随。
$
按照其声明(infixr 0
)的具体方式工作,其中优先级最低。 (见:http://hackage.haskell.org/package/base-4.11.1.0/docs/Prelude.html#v:-36-)
有关详细信息,请参阅https://www.haskell.org/onlinereport/decls.html#fixity。