我正在从 Hutton 的书《Programming in Haskell》(第二版)中学习 Haskell。在第 1 章的开头。 7、《高阶函数》,函数'twice'定义为:
twice :: (a -> a) -> a -> a
twice f x = f (f x)
然后给出一些使用示例,例如:
> twice reverse [1,2,3]
[1,2,3]
然后作者说,“将(有限)列表反转两次没有效果的事实可以通过等式两次反转= id来捕获......” 当我启动 ghci 并写入时:
GHCi, version 8.4.4: http://www.haskell.org/ghc/ :? for help
Prelude> :type twice
<interactive>:1:1: error: Variable not in scope: twice
Prelude> twice reverse = id
Prelude> :type twice
twice :: p -> a -> a
Prelude> twice reverse [1,2,3]
[1,2,3]
这实际上是一个函数定义,因为两次之前没有定义。现在,我的问题是它是如何运作的? 两次反向是否只是定义为id的别名?但如果是这样,为什么我可以单独查询两次的类型? Haskell语言报告中有哪些相关部分用于解释这种函数定义语法?另外,为什么使用类型变量p?通常使用a、b等。
编辑
感谢到目前为止提供的有用回答和评论!
我只是想提供一些关于我提出的尚未得到解决的其他问题的更多背景信息。当然,最重要的问题已经得到解答。
为什么其中一个类型变量名为 p?
让我们看一下下面的 ghci session :
GHCi, version 8.4.4: http://www.haskell.org/ghc/ :? for help
Prelude> twice asdf fsda = id
Prelude> :type twice
twice :: p1 -> p2 -> a -> a
Prelude> mySum a b c d = a + c
Prelude> :type mySum
mySum :: Num a => a -> p1 -> a -> p2 -> a
似乎p或p1、p2等被用作未使用的变量的泛型类型。也许它们还用于其他目的。我在 article 中找到了提示在另一个问答论坛上。用于泛型类型变量的名称甚至可能是特定于实现的。重要的信息是,使用哪些字母并不重要,重要的是它们不同。
语言规范的相关部分是什么?
可能是第 1 章。 4.4.3,“函数和模式绑定(bind)”,2010 年 Haskell 语言报告。但现在这对我来说信息太多了。毕竟我正在阅读一本介绍性 Haskell 书的前半部分。
最佳答案
让我们逐步了解一下您在这里所做的事情。首先,您定义:
twice :: (a -> a) -> a -> a
twice f x = f (f x)
这是一个正常的函数定义,并且按照您的预期工作:两次 (+1) 2 = 3
、两次反转 "abc"= "abc"
等。当 Hutton 说“方程两次反向= id
捕获了两次(有限)列表反转没有效果的事实”时,他只是说两次反向
与执行 id
具有相同的效果。
但是然后,你定义:
twice reverse = id
这并没有达到您的预期!您可能希望这可以验证等式“两次反向 = id”或类似的内容是否成立。相反,它定义了一个全新的函数 twice
,将其唯一参数命名为 reverse
,然后忽略该参数并返回 id
函数。在这里,您正在使用遮蔽:您正在定义一个与旧标识符同名的新标识符(在本例中为反向
)。事实上,如果您使用 -Wall
启用所有警告,GHC 会警告您:
Prelude> :set -Wall
Prelude> twice reverse = id
<interactive>:2:7: warning: [-Wname-shadowing]
This binding for `reverse' shadows the existing binding
imported from `Prelude' (and originally defined in `GHC.List')
因此,定义两次reverse = id
与定义一个函数两次myArg = id
没有什么不同,只是参数具有不同的名称。
关于haskell - Haskell 中具有 LHS 和 RHS 函数的函数定义如何/为什么起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62492875/