哈斯克尔帮助.和$
举个例子,如下:
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 中删除 $,它会停止工作吗?
我已经向自己解释过几次了,但它从来没有坚持过,而且我花了一些时间来推理到底发生了什么。我知道 $ 基本上确保 $ 的每一侧都单独评估,因为 $ 的优先级最低,但是如果我将 $ 拉出,为什么会中断?
谢谢
As an example, take the following
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)
Why, if I remove the $ from notTable, does it stop working?
I have explained this to myself a few times, but it never sticks and it takes me awhile to reason through whats going on. I know the $ basically makes sure that each side of the $ gets evaluated separately because the $ has the lowest precedence but why does this break if I pull the $ out?
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您对优先级的看法是正确的:
.
是 infixr 9 (9 最高),而$
是 infixr 0 (0 最低)。请参阅Haskell 报告了解运算符固定性表。但是,函数应用程序的优先级高于任何运算符,甚至高于
.
。因此:变为:
而
变为:
You're right about precedence:
.
is infixr 9 (9 is highest), while$
is infixr 0 (0 is lowest). See the Haskell Report for the operator fixity table.However, function application has higher precedence than any operator, even
.
. Hence:becomes:
while
becomes:
关于您作为对 Joey Adams 答案的评论提出的问题:“为什么
map .map $ not = (map .map) $ 不起作用
,而map .map not = map . (地图没有)
没有吗?”让我们首先考虑什么是
map 。地图
确实如此。首先,map
接受一个函数f :: a ->; b
和一个[a]
类型的列表,给出一个[b]
类型的列表,其中f
应用于每个元素的原始列表。map
的类型为(a -> b) -> [一]-> [b]
。回想一下,在 Haskell 中,这意味着map
实际上是一个接受函数a ->; 的函数。 b
并返回一个接受[a]
并给出[b]
的函数。我们通常喜欢将其视为map
是两个变量的函数,但稍后的区别将很重要。现在让我们考虑一下组合运算符
(.)
的作用。回想一下,它被定义为,即它需要两个函数
f
和g
(具有合适的域/输入和目标/输出),并为您提供一个由第一个定义的新函数应用g
,然后将f
应用到g
输出的内容上。我将类型变量称为a1
、b1
和c1
以避免稍后混淆。好的,现在我们可以弄清楚
map 是什么。地图
是。为了清楚起见,现在,
“两个变量的函数”在 Haskell 中的编码方式变得很重要。由于 Haskell 中的函数实际上只有一个输入,因此我们必须小心,如上所述。因此,
mapright
的域/输入实际上只是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 的类型了。右图 = 地图 .地图:是的
。 GHCi 证实了这一点:
现在您谈论的两个函数为何不同就清楚了。显然,
(map .map) not
的类型为[[Bool]] -> [[Bool]]
,这正是您想要的。另一方面,map not
的类型为[Bool] -> [布尔]
。将map not
的输出输入到map
的(first)输入中,甚至不会进行类型检查:的第一个输入map
必须是函数,而map not
的输出是[Bool]
。Re the question you asked as a comment to Joey Adams' answer: "Why does
map . map $ not = (map . map) $ not
work, whilemap . map not = map . (map not)
doesn't?"Let us first consider what
map . map
does. First of all,map
takes a functionf :: a -> b
and a list with type[a]
, giving a list with type[b]
wheref
is applied to each element of the original list. The type ofmap
is(a -> b) -> [a] -> [b]
. Recall that in Haskell, this means thatmap
really is a function that takes a functiona -> b
and returns a function taking an[a]
and giving a[b]
. We often like to think of this asmap
being a function of two variables, but the distinction will be important later on.Now let us consider what the composition operator
(.)
does. Recall that it is defined as, i.e. it takes two functions
f
andg
(with suitable domains/inputs and targets/outputs), and gives you a new function defined by first applyingg
and then applyingf
to whateverg
spits out. I've called the type variablesa1
,b1
, andc1
to avoid confusion later on.OK, now we're in a position to figure out what
map . map
is. For the sake of clarity,let us write the two (identical) maps as
Now the way "functions of two variables" are encoded in Haskell becomes important. Since functions in Haskell really just have one input, we have to be careful, as discussed above. Thus, the domain/input of of
mapright
is really just of typea -> b
, while the output is really of type[a] -> [b]
. Going back to the signature of(.)
, this means we've fixed the right hand operand's type,a1 -> b1
above, to be(a -> b) -> ([a] -> [b])
. Thus,a1 = a -> b
andb1 = [a] -> [b]
.Proceding in the same way for the left hand operand, we see that
[a] -> [b] = b1 = c -> d
, soc = [a]
andd = [b]
. The same reasoning givesc1 = [c] -> [d] = [[a]] -> [[b]]
.And we're done, we can now read off the type of
leftmap . rightmap = map . map
: It is. This is confirmed by GHCi:
Now it will become clear why the two functions you talk about are different. Clearly,
(map . map) not
has type[[Bool]] -> [[Bool]]
, which is exactly what you want.map not
, on the other hand, has type[Bool] -> [Bool]
. Taking the output ofmap not
and feeding it into the (first) input ofmap
will not even typecheck: The first input ofmap
has to be a function, while the output ofmap not
is a[Bool]
.函数应用程序绑定非常紧密,因此如果没有
$
,它会解析为map 。 (map not)
而不是(map .map) not
,这是您想要的解释。Function application binds very tightly, so without
$
it parses asmap . (map not)
rather than(map . map) not
, which is the interpretation you want.