哈斯克尔帮助.和$

发布于 2024-10-30 03:37:22 字数 383 浏览 1 评论 0原文

举个例子,如下:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

夜访吸血鬼 2024-11-06 03:37:22

您对优先级的看法是正确的: . 是 infixr 9 (9 最高),而 $ 是 infixr 0 (0 最低)。请参阅Haskell 报告了解运算符固定性表。

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

map . map $ (not)

变为:

(map . map) $ (not)

map . map (not)

变为:

map . (map not)

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:

map . map $ (not)

becomes:

(map . map) $ (not)

while

map . map (not)

becomes:

map . (map not)
洋洋洒洒 2024-11-06 03:37:22

关于您作为对 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 是两个变量的函数,但稍后的区别将很重要。

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

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

,即它需要两个函数 fg (具有合适的域/输入和目标/输出),并为您提供一个由第一个定义的新函数应用g,然后将f应用到g输出的内容上。我将类型变量称为 a1b1c1 以避免稍后混淆。

好的,现在我们可以弄清楚 map 是什么。地图是。为了清楚起见,
现在,

mapleft :: (c -> d) -> [c] -> [d]
mapleft = map
mapright :: (a -> b) -> [a] -> [b]
mapright = 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 的类型了。右图 = 地图 .地图:是的

a1 -> c1 = (a -> b) -> [[a]] -> [[b]]

。 GHCi 证实了这一点:

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

现在您谈论的两个函数为何不同就清楚了。显然,(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, while map . map not = map . (map not) doesn't?"

Let us first consider what map . map does. First of all, map takes a function f :: a -> b and a list with type [a], giving a list with type [b] where f is applied to each element of the original list. The type of map is (a -> b) -> [a] -> [b]. Recall that in Haskell, this means that map really is a function that takes a function a -> b and returns a function taking an [a] and giving a [b]. We often like to think of this as map 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

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

, i.e. it takes two functions f and g (with suitable domains/inputs and targets/outputs), and gives you a new function defined by first applying g and then applying f to whatever g spits out. I've called the type variables a1, b1, and c1 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

mapleft :: (c -> d) -> [c] -> [d]
mapleft = map
mapright :: (a -> b) -> [a] -> [b]
mapright = map

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 type a -> 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 and b1 = [a] -> [b].

Proceding in the same way for the left hand operand, we see that [a] -> [b] = b1 = c -> d, so c = [a] and d = [b]. The same reasoning gives c1 = [c] -> [d] = [[a]] -> [[b]].

And we're done, we can now read off the type of leftmap . rightmap = map . map: It is

a1 -> c1 = (a -> b) -> [[a]] -> [[b]]

. This is confirmed by GHCi:

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

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 of map not and feeding it into the (first) input of map will not even typecheck: The first input of map has to be a function, while the output of map not is a [Bool].

梦里泪两行 2024-11-06 03:37:22

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

Function application binds very tightly, so without $ it parses as map . (map not) rather than (map . map) not, which is the interpretation you want.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文