为何/何时不能在Haskell中使用$而不是括号?

时间:2018-04-23 23:59:51

标签: haskell

我认为,只要$ ...的权利没有任何内容,我就可以使用(...)代表...

但是,当我尝试重写以下内容时

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)

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 <*> bliftA2 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