Haskell帮忙。和$

时间:2011-04-03 06:25:35

标签: function haskell composition

举个例子,请参考以下内容

type Row a = [a]
type Table a = [Row a]

mapTable :: (a -> b) -> Table a -> Table b
mapTable = map . map

notTable :: Table Bool -> Table Bool
notTable = map . map $ (not)

为什么,如果我从notTable中删除$,它会停止工作吗?

我已经对自己解释了几次,但它永远不会坚持下去,我需要一段时间才能推断出最新情况。我知道$基本上确保$的每一边都被单独评估,因为$具有最低的优先级,但是如果我把$拉出来,为什么会中断呢?

由于

3 个答案:

答案 0 :(得分:10)

你的优先权是正确的:.是infixr 9(9是最高的),而$是infixr 0(0是最低的)。有关操作员固定性表,请参阅Haskell Report

但是,函数应用程序的优先级高于任何运算符,甚至.。因此:

map . map $ (not)

变为:

(map . map) $ (not)

,而

map . map (not)

变为:

map . (map not)

答案 1 :(得分:3)

回答你问的问题,作为对Joey Adams回答的评论:“为什么map . map $ not = (map . map) $ not有效,而map . map not = map . (map not)没有?”

让我们首先考虑map . map的作用。首先,map采用函数f :: a -> b和类型为[a]的列表,提供类型为[b]的列表,其中f应用于每个元素原始清单。 map的类型为(a -> b) -> [a] -> [b]。回想一下,在Haskell中,这意味着map实际上是一个函数,它接受函数a -> b并返回一个函数[a]并给出[b]。我们经常喜欢将其视为map是两个变量的函数,但这种区别在以后会很重要。

现在让我们考虑一下合成运算符(.)的作用。回想一下它被定义为

(.)  :: (b1 -> c1) -> (a1 -> b1) -> (a1 -> c1)
f . g = \ x -> f (g x)

,即它需要两个函数fg(具有合适的域/输入和目标/输出),并为您提供一个新函数,该函数首先应用g然后应用f g吐出的a1。我调用了类型变量b1c1map . map以避免以后混淆。

好的,现在我们可以找出mapleft :: (c -> d) -> [c] -> [d] mapleft = map mapright :: (a -> b) -> [a] -> [b] mapright = map 是什么了。为清楚起见, 让我们把两个(相同的)地图写成

mapright

现在,在Haskell中编码“两个变量的函数”的方式变得很重要。由于Haskell中的函数实际上只有一个输入,我们必须小心,如上所述。因此,a -> b的域/输入实际上只是[a] -> [b]类型,而输出实际上是(.)类型。回到a1 -> b1的签名,这意味着我们已将上面的右手操作数类型(a -> b) -> ([a] -> [b])修改为a1 = a -> b。因此,b1 = [a] -> [b][a] -> [b] = b1 = c -> d

以左手操作数的相同方式进行,我们看到c = [a]d = [b]c1 = [c] -> [d] = [[a]] -> [[b]]。同样的推理给出leftmap . rightmap = map . map

我们已经完成了,我们现在可以读出a1 -> c1 = (a -> b) -> [[a]] -> [[b]] 的类型:它是

Prelude> :t (map . map)
(map . map) :: (a -> b) -> [[a]] -> [[b]]

。这一点得到了GHCi的证实:

(map . map) not

现在很清楚为什么你谈到的两个功能是不同的。显然,[[Bool]] -> [[Bool]]的类型为map not,这正是您想要的。另一方面,[Bool] -> [Bool]的类型为map not。获取map的输出并将其输入map的(第一个)输入甚至不会进行类型检查:map not的第一个输入必须是< em> function ,而[Bool]的输出是{{1}}。

答案 2 :(得分:2)

函数应用程序绑定非常紧密,因此如果没有$它会解析为map . (map not)而不是(map . map) not,这就是您想要的解释。