本文翻译自:What does the `forall` keyword in Haskell/GHC do?
I'm beginning to understand how the forall
keyword is used in so-called "existential types" like this: 我开始了解在这样的所谓“现有类型”中如何使用forall
关键字:
data ShowBox = forall s. Show s => SB s
This is only a subset, however, of how forall
is used and I simply cannot wrap my mind around its use in things like this: 但是,这只是forall
用法的一个子集,我根本无法在这样的事情上forall
地使用它:
runST :: forall a. (forall s. ST s a) -> a
Or explaining why these are different: 或解释为什么这些不同:
foo :: (forall a. a -> a) -> (Char, Bool)
bar :: forall a. ((a -> a) -> (Char, Bool))
Or the whole RankNTypes
stuff... 或整个RankNTypes
东西...
I tend to prefer clear, jargon-free English rather than the kinds of language which are normal in academic environments. 我倾向于使用清晰,无术语的英语,而不是学术环境中通常使用的那种语言。 Most of the explanations I attempt to read on this (the ones I can find through search engines) have these problems: 我尝试阅读的大多数解释(通过搜索引擎可以找到的解释)都存在以下问题:
runST
, foo
and bar
above). 他们解释了该关键字使用的一部分(例如“ existential types”),这使我感到高兴,直到我阅读了以完全不同的方式使用它的代码(例如上述的runST
, foo
和bar
)。 So... 所以...
On to the actual question. 关于实际问题。 Can anybody completely explain the forall
keyword in clear, plain English (or, if it exists somewhere, point to such a clear explanation which I've missed) that doesn't assume I'm a mathematician steeped in the jargon? 任何人都可以用清晰,简洁的英语完全解释forall
关键字(或者,如果它存在于某处,则指向我错过的如此清晰的解释),而不假定我是一个专业的数学家?
Edited to add: 编辑添加:
There were two stand-out answers from the higher-quality ones below, but unfortunately I can only choose one as best. 以下是较高质量的答案中有两个突出的答案,但是不幸的是,我只能选择其中一个作为最佳答案。 Norman's answer was detailed and useful, explaining things in a way that showed some of the theoretical underpinnings of forall
and at the same time showing me some of the practical implications of it. 诺曼的答案是详尽而有用的,它以某种方式解释了事物,从而显示了“ forall
一些理论基础,同时也向我展示了它的一些实际含义。 yairchu's answer covered an area nobody else mentioned (scoped type variables) and illustrated all of the concepts with code and a GHCi session. yairchu的答案涵盖了一个没人提到的领域(作用域类型变量),并通过代码和GHCi会话说明了所有概念。 Were it possible to select both as best, I would. 我会尽可能选择两者。 Unfortunately I can't and, after looking over both answers closely, I've decided that yairchu's slightly edges out Norman's because of the illustrative code and attached explanation. 不幸的是,我不能这样做,在仔细查看了两个答案之后,由于示例性代码和附加的解释,我认为yairchu的语言略微超出了Norman的语言。 This is a bit unfair, however, because really I needed both answers to understand this to the point that forall
doesn't leave me with a faint sense of dread when I see it in a type signature. 但是,这有点不公平,因为我确实需要两个答案才能理解这一点,以至于当我在类型签名中看到它时, forall
不会让我感到恐惧。
参考:https://stackoom.com/question/CswS/Haskell-GHC中的-forall-关键字有什么作用
Here is a quick and dirty explanation in plain terms that you're likely to be already familiar with. 这是您可能已经熟悉的简单明了的简要说明。
The forall
keyword is really only used in one way in Haskell. 实际上, forall
关键字仅在Haskell中以一种方式使用。 It always means the same thing when you see it. 当您看到它时,它总是意味着同一件事。
Universal quantification 通用量化
A universally quantified type is a type of the form forall a. fa
通用量化类型是形式为a的类型forall a. fa
forall a. fa
. forall a. fa
。 A value of that type can be thought of as a function that takes a type a
as its argument and returns a value of type fa
. 该类型的值可以被认为是采用一个类型 的函数 a
作为它的参数和返回类型的值 fa
。 Except that in Haskell these type arguments are passed implicitly by the type system. 除了在Haskell中,这些类型参数是由类型系统隐式传递的。 This "function" has to give you the same value no matter which type it receives, so the value is polymorphic . 无论接收到哪种类型,此“函数”都必须为您提供相同的值,因此该值是多态的 。
For example, consider the type forall a. [a]
例如,考虑所有类型forall a. [a]
forall a. [a]
. forall a. [a]
。 A value of that type takes another type a
and gives you back a list of elements of that same type a
. 该类型的值采用另一种类型a
,并返回给您相同类型a
的元素列表。 There is only one possible implementation, of course. 当然,只有一种可能的实现。 It would have to give you the empty list because a
could be absolutely any type. 它必须给您一个空列表,因为a
绝对可以是任何类型。 The empty list is the only list value that is polymorphic in its element type (since it has no elements). 空列表是唯一在元素类型上是多态的列表值(因为它没有元素)。
Or the type forall a. a -> a
或类型为forall a. a -> a
forall a. a -> a
. forall a. a -> a
。 The caller of such a function provides both a type a
and a value of type a
. 这样的函数的调用者同时提供了类型a
和类型的值a
。 The implementation then has to return a value of that same type a
. 然后,实现必须返回相同类型a
的值。 There's only one possible implementation again. 再次只有一种可能的实现。 It would have to return the same value that it was given. 它必须返回与给定的相同值。
Existential quantification 存在量化
An existentially quantified type would have the form exists a. fa
存在量化类型将具有以下形式: exists a. fa
exists a. fa
, if Haskell supported that notation. exists a. fa
,如果Haskell支持该表示法。 A value of that type can be thought of as a pair (or a "product") consisting of a type a
and a value of type fa
. 可以将这种类型的值视为由类型a
和类型fa
的值组成的一对 (或“乘积”)。
For example, if you have a value of type exists a. [a]
例如,如果您有一个类型exists a. [a]
a的值exists a. [a]
exists a. [a]
, you have a list of elements of some type. exists a. [a]
,您具有某种类型的元素的列表。 It could be any type, but even if you don't know what it is there's a lot you could do to such a list. 它可以是任何类型,但是即使您不知道它是什么,也可以对此类列表进行很多操作。 You could reverse it, or you could count the number of elements, or perform any other list operation that doesn't depend on the type of the elements. 您可以反转它,也可以计算元素的数量,或者执行任何其他不依赖于元素类型的列表操作。
OK, so wait a minute. 好,等一下。 Why does Haskell use forall
to denote an "existential" type like the following? 为什么Haskell使用forall
来表示“存在”类型,如下所示?
data ShowBox = forall s. Show s => SB s
It can be confusing, but it's really describing the type of the data constructor SB
: 这可能会造成混淆,但实际上是在描述数据构造函数 SB
的类型 :
SB :: forall s. Show s => s -> ShowBox
Once constructed, you can think of a value of type ShowBox
as consisting of two things. 构造完成后,您可以认为ShowBox
类型的值由两部分组成。 It's a type s
together with a value of type s
. 这是一个类型的s
与类型的值一起s
。 In other words, it's a value of an existentially quantified type. 换句话说,它是一个存在量化类型的值。 ShowBox
could really be written as exists s. Show s => s
ShowBox
实际上可以写为exists s. Show s => s
exists s. Show s => s
, if Haskell supported that notation. 如果Haskell支持该表示法,请exists s. Show s => s
。
runST
and friends runST
和朋友
Given that, how are these different? 鉴于此,这些有什么不同?
foo :: (forall a. a -> a) -> (Char,Bool)
bar :: forall a. ((a -> a) -> (Char, Bool))
Let's first take bar
. 我们先bar
。 It takes a type a
and a function of type a -> a
, and produces a value of type (Char, Bool)
. 它需要一个类型a
和类型的函数a -> a
,并产生类型的值(Char, Bool)
。 We could choose Int
as the a
and give it a function of type Int -> Int
for example. 我们可以选择Int
作为a
并为其提供一个Int -> Int
类型的函数。 But foo
is different. 但是foo
是不同的。 It requires that the implementation of foo
be able to pass any type it wants to the function we give it. 它要求foo
实现能够将所需的任何类型传递给我们提供的函数。 So the only function we could reasonably give it is id
. 因此,我们可以合理地给它的唯一函数是id
。
We should now be able to tackle the meaning of the type of runST
: 现在,我们应该能够解决runST
类型的runST
:
runST :: forall a. (forall s. ST s a) -> a
So runST
has to be able to produce a value of type a
, no matter what type we give as a
. 所以runST
必须能够生产类型的值a
,无论何种类型,我们给出的a
。 To do so, it uses an argument of type forall s. ST sa
为此,它使用类型为forall s. ST sa
参数forall s. ST sa
forall s. ST sa
which certainly must somehow produce the a
. 当然必须以某种方式产生a
forall s. ST sa
。 What's more, it must be able to produce a value of type a
no matter what type the implementation of runST
decides to give as s
. 更重要的是,它必须能够生产类型的值a
不管是什么类型的实现runST
决定放弃为s
。
OK, so what? 好那怎么了 The benefit is that this puts a constraint on the caller of runST
in that the type a
cannot involve the type s
at all. 好处是,这对runST
的调用者施加了约束,因为类型a
根本不能涉及类型s
。 You can't pass it a value of type ST s [s]
, for example. 例如,您不能将其传递为ST s [s]
类型的值。 What that means in practice is that the implementation of runST
is free to perform mutation with the value of type s
. 实际上,这意味着runST
的实现可以自由地执行类型为s
突变。 The type guarantees that this mutation is local to the implementation of runST
. 该类型保证此突变是runST
实现的runST
。
The type of runST
is an example of a rank-2 polymorphic type because the type of its argument contains a forall
quantifier. runST
的类型是rank-2多态类型的示例,因为其参数的类型包含forall
量词。 The type of foo
above is also of rank 2. An ordinary polymorphic type, like that of bar
, is rank-1, but it becomes rank-2 if the types of arguments are required to be polymorphic, with their own forall
quantifier. 上面的foo
的类型也为等级2。普通的多态类型(如bar
类型)为等级1,但如果要求参数的类型必须是多态的,则具有自己的forall
量词,它将变为等级2。 And if a function takes rank-2 arguments then its type is rank-3, and so on. 并且,如果函数接受等级2参数,则其类型为等级3,依此类推。 In general, a type that takes polymorphic arguments of rank n
has rank n + 1
. 通常,采用等级n
多态参数的类型的等级为n + 1
。
With Existential-Quantification,
forall
s indata
definitions mean that, the value contained can be of any suitable type, not that it must be of all suitable types. 与生存-定量,forall
S INdata
的定义是指,该值包含可以是任何合适的类型,而不是它必须 全部适当类型的。 -- yachiru's answer -yachiru的答案
An explanation of why forall
in data
definitions are isomorphic to (exists a. a)
(pseudo-Haskell) can be found in wikibooks's "Haskell/Existentially quantified types" . 在Wikibooks的“ Haskell /现有量化类型”中,可以找到关于为什么data
定义中的forall
与(exists a. a)
(伪Haskell)同构的解释。
The following is a brief verbatim summary: 以下是逐字摘要:
data T = forall a. MkT a -- an existential datatype
MkT :: forall a. a -> T -- the type of the existential constructor
When pattern-matching/deconstructing MkT x
, what is the type of x
? 当模式匹配/解构MkT x
,是什么类型x
?
foo (MkT x) = ... -- -- what is the type of x?
x
can be any type (as stated in the forall
), and so it's type is: x
可以是任何类型(如forall
中所述),因此其类型为:
x :: exists a. a -- (pseudo-Haskell)
Therefore, the following are isomorphic: 因此,以下是同构的:
data T = forall a. MkT a -- an existential datatype
data T = MkT (exists a. a) -- (pseudo-Haskell)
My simple interpretation of all this, is that " forall
really means 'for all'". 我的这一切简单的解释,就是“ forall
真正意味着‘所有’”。 An important distinction to make is the impact of forall
on the definition versus function application . 要做出的一个重要区别是forall
对定义与功能应用程序的影响 。
A forall
means the definition of the value or function must be polymorphic. forall
表示值或函数的定义必须是多态的。
If the thing being defined is a polymorphic value , then it means that the value must be valid for all suitable a
, which is quite restrictive. 如果要定义的事物是一个多态值 ,则意味着该值对于所有合适的a
都必须是有效a
,这是非常严格的。
If the thing being defined is a polymorphic function , then it means that the function must be valid for all suitable a
, which isn't that restrictive because just because the function is polymorphic doesn't mean the parameter being applied have to be polymorphic. 如果要定义的事物是多态函数 ,则意味着该函数必须对所有合适的a
有效,这不是限制性的,因为仅仅因为该函数是多态的,并不意味着所应用的参数必须是多态的。 That is, if the function is valid for all a
, then conversely any suitable a
can be applied to the function. 也就是说,如果函数对所有a
有效,则可以将任何合适的a
应用于该函数。 However, the type of the parameter can only be chosen once in the function definition. 但是,参数的类型只能在函数定义中选择一次。
If a forall
is inside the function parameter's type (ie, a Rank2Type
) then it means the applied parameter must be truly polymorphic, to be consistent with the idea of forall
means definition is polymorphic. 如果forall
在函数参数的类型(即Rank2Type
)内,则意味着所应用的参数必须是真正多态的,以与forall
的思想一致,即定义是多态的。 In this case, the type of the parameter can be chosen more than once in the function definition ( "and is chosen by the implementation of the function", as pointed out by Norman ) 在这种情况下,可以在函数定义中多次选择参数的类型( “并且由函数的实现选择”,如Norman所指出 )
Therefore, the reason why existential data
definitions allows any a
is because the data constructor is a polymorphic function : 因此,存在data
定义允许任何 a
的原因是因为数据构造函数是一个多态函数 :
MkT :: forall a. a -> T
kind of MkT :: a -> *
MkT的种类:: a- a -> *
Which means any a
may be applied to the function. 这意味着可以将任何a
应用于该函数。 As opposed to, say, a polymorphic value : 相对于多态值 :
valueT :: forall a. [a]
kind of valueT :: a
一种值T :: a
Which means that the definition of valueT must be polymorphic. 这意味着valueT的定义必须是多态的。 In this case, valueT
can be defined as empty list []
of all types. 在这种情况下,可以将valueT
定义为所有类型的空列表[]
。
[] :: [t]
Even though the meaning for forall
is consistent in ExistentialQuantification
and RankNType
, existentials has a difference since the data
constructor can be used in pattern matching. 即使forall
的含义在ExistentialQuantification
和RankNType
是一致的, RankNType
由于data
构造函数可用于模式匹配,因此存在存在差异。 As documented in the ghc user guide : 如ghc用户指南中所述 :
When pattern matching, each pattern match introduces a new, distinct, type for each existential type variable. 模式匹配时,每个模式匹配都会为每个存在的类型变量引入一个新的独特类型。 These types cannot be unified with any other type, nor can they escape from the scope of the pattern match. 这些类型不能与任何其他类型统一,也不能脱离模式匹配的范围。
Can anybody completely explain the forall keyword in clear, plain English? 有人能用清晰,简洁的英语完全解释forall关键字吗?
No. (Well, maybe Don Stewart can.) 不 (嗯,也许唐·斯图尔特可以。)
Here are the barriers to a simple, clear explanation or forall
: 以下是简单明了的解释或forall
解释的障碍:
It's a quantifier. 这是一个量词。 You have a to have at least a little logic (predicate calculus) to have seen a universal or existential quantifier. 您必须至少有一点逻辑(谓词演算)才能看到通用或存在量词。 If you've never seen predicate calculus or are not comfortable with quantifiers (and I have seen students during PhD qualifying exams who are not comfortable), then for you, there's no easy explanation of forall
. 如果您从未见过谓语演算或对量词不满意(并且我在博士学位资格考试中见过不满意的学生),那么对您来说,没有关于forall
的简单解释。
It's a type quantifier. 这是一个类型量词。 If you haven't seen System F and gotten some practice writing polymorphic types, you're going to find forall
confusing. 如果你还没有看到系统F ,并得到一些练习写多态类型,你会发现forall
混乱。 Experience with Haskell or ML is not enough, because normally these languages omit the forall
from polymorphic types. 哈斯克尔或ML经验是不够的,因为通常这些语言省略forall
从多态类型。 (In my mind, this is a language-design mistake.) (在我看来,这是一个语言设计错误。)
In Haskell in particular, forall
is used in ways that I find confusing. 特别是在Haskell中, forall
的使用方式令我感到困惑。 (I'm not a type theorist, but my work brings me in contact with a lot of type theory, and I'm quite comfortable with it.) For me, the main source of confusion is that forall
is used to encode a type that I myself would prefer to write with exists
. (我不是类型理论家,但是我的工作使我接触了很多类型理论,我对此很满意。)对我而言,造成混淆的主要原因是, forall
用于编码类型我本人更愿意与之exists
。 It's justified by a tricky bit of type isomorphism involving quantifiers and arrows, and every time I want to understand it, I have to look things up and work out the isomorphism myself. 涉及到量词和箭头的棘手的类型同构是有道理的,每次我想了解它时,我都必须查找事物并自己解决同构。
If you are not comfortable with the idea of type isomorphism, or if you don't have any practice thinking about type isomorphisms, this use of forall
is going to stymie you. 如果您对类型同构的概念不满意,或者没有任何关于类型同构的想法,那么使用forall
将使您感到沮丧。
While the general concept of forall
is always the same (binding to introduce a type variable), the details of different uses can vary significantly. 尽管forall
的一般概念始终是相同的(绑定以引入类型变量),但是不同用途的细节可能会有很大差异。 Informal English is not a very good tool for explaining the variations. 非正式英语不是一个很好的解释变化的工具。 To really understand what's going on, you need some mathematics. 要真正了解正在发生的事情,您需要一些数学。 In this case the relevant mathematics can be found in Benjamin Pierce's introductory text Types and Programming Languages , which is a very good book. 在这种情况下,相关的数学可以在本杰明·皮尔斯的介绍性文本《 类型和编程语言》中找到 ,这是一本非常好的书。
As for your particular examples, 至于你的例子
runST
should make your head hurt. runST
应该使您的头部受伤。 Higher-rank types (forall to the left of an arrow) are rarely found in the wild. 在野外很少发现高等级的类型(箭头左侧的全部)。 I encourage you to read the paper that introduced runST
: "Lazy Functional State Threads" . 我鼓励您阅读介绍runST
: “惰性功能状态线程” 。 This is a really good paper, and it will give you a much better intuition for the type of runST
in particular and for higher-rank types in general. 这是一篇非常好的论文,它将使您对runST
的类型和runST
对更高级别的类型有更好的直觉。 The explanation take several pages, it's very well done, and I'm not going to try to condense it here. 解释需要几页,并且做得很好,在这里我不会尝试进行压缩。
Consider 考虑
foo :: (forall a. a -> a) -> (Char,Bool) bar :: forall a. ((a -> a) -> (Char, Bool))
If I call bar
, I can simply pick any type a
that I like, and I can pass it a function from type a
to type a
. 如果我电话bar
,我可以简单地选择任何类型的a
,我喜欢,我可以通过它的类型的函数a
键入a
。 For example, I can pass the function (+1)
or the function reverse
. 例如,我可以传递函数(+1)
或函数reverse
。 You can think of the forall
as saying "I get to pick the type now". 您可以将forall
都说成“我现在就选择类型”。 (The technical word for picking the type is instantiating .) (用于选择类型的技术字是实例化的 。)
The restrictions on calling foo
are much more stringent: the argument to foo
must be a polymorphic function. 调用foo
的限制要严格得多: foo
的参数必须是多态函数。 With that type, the only functions I can pass to foo
are id
or a function that always diverges or errors, like undefined
. 在这种类型下,我只能传递给foo
的函数是id
或总是发散或出错的函数,例如undefined
。 The reason is that with foo
, the forall
is to the left of the arrow, so as the caller of foo
I don't get to pick what a
is—rather it's the implementation of foo
that gets to pick what a
is. 其原因在于,与foo
的forall
是箭头的左侧,这样的来电foo
我不明白选择什么样的a
是-而它的实施 foo
是得到选择什么样的a
是。 Because forall
is to the left of the arrow, rather than above the arrow as in bar
, the instantiation takes place in the body of the function rather than at the call site. 因为forall
在箭头的左侧,而不是箭头在bar
上方,所以实例化发生在函数的主体中,而不是在调用位置。
Summary: A complete explanation of the forall
keyword requires math and can be understood only by someone who has studied the math. 简介: forall
关键字的完整说明需要数学,只有经过数学研究的人才能理解。 Even partial explanations are hard to understand without math. 没有数学就连部分解释都很难理解。 But maybe my partial, non-math explanations help a little. 但是也许我的部分非数学解释会有所帮助。 Go read Launchbury and Peyton Jones on runST
! 在runST
上阅读Launchbury和Peyton Jones!
Addendum: Jargon "above", "below", "to the left of". 附录:行话在“上方”,“下方”,“左侧”。 These have nothing to do with the textual ways types are written and everything to do with abstract-syntax trees. 这些与文本类型的编写方式无关,与抽象语法树无关。 In the abstract syntax, a forall
takes the name of a type variable, and then there is a full type "below" the forall. 在抽象语法中,一个forall
使用类型变量的名称,然后在该allall的下面有一个完整类型。 An arrow takes two types (argument and result type) and forms a new type (the function type). 箭头采用两种类型(参数和结果类型)并形成新类型(函数类型)。 The argument type is "to the left of" the arrow; 参数类型是箭头的“左侧”; it is the arrow's left child in the abstract-syntax tree. 它是箭头在抽象语法树中的左子元素。
Examples: 例子:
In forall a . [a] -> [a]
总的来说forall a . [a] -> [a]
forall a . [a] -> [a]
, the forall is above the arrow; forall a . [a] -> [a]
,最上方是箭头; what's to the left of the arrow is [a]
. 箭头左侧是[a]
。
In 在
forall nfex . (forall ex . nex -> f -> Fact xf) -> Block nex -> f -> Fact xf
the type in parentheses would be called "a forall to the left of an arrow". 括号中的类型将被称为“箭头左侧的全部”。 (I'm using types like this in an optimizer I'm working on.) (我正在使用的优化器中使用这种类型。)
They're densely packed with assumptions that I've read the latest in whatever branch of discrete math, category theory or abstract algebra is popular this week. 他们充满了这样的假设:我在本周流行的离散数学,范畴论或抽象代数的任何分支中都读过最新的文章。 (If I never read the words "consult the paper whatever for details of implementation" again, it will be too soon.) (如果我再也没有读过“请咨询论文以了解实施细节”这句话,那就太早了。)
Er, and what about simple first-order logic? 嗯,那么简单的一阶逻辑呢? forall
is pretty clearly in reference to universal quantification , and in that context the term existential makes more sense as well, though it would be less awkward if there were an exists
keyword. forall
是相当清楚的参考全称量化 ,并在这方面术语生存更有意义为好,尽管它会少些尴尬,如果有一个exists
的关键字。 Whether quantification is effectively universal or existential depends on the placement of the quantifier relative to where the variables are used on which side of a function arrow and it's all a bit confusing. 量化究竟是通用的还是存在的,取决于量化器相对于在功能箭头的哪一侧使用变量的位置的位置,这一切都有些混乱。
So, if that doesn't help, or if you just don't like symbolic logic, from a more functional programming-ish perspective you can think of type variables as just being (implicit) type parameters to the function. 因此,如果这无济于事,或者您只是不喜欢符号逻辑,那么从更具功能性的编程角度来看,您可以将类型变量视为只是(隐式)函数的类型参数。 Functions taking type parameters in this sense are traditionally written using a capital lambda for whatever reason, which I'll write here as /\\
. 从某种意义上讲,采用类型参数的函数通常出于某种原因使用大写lambda编写,在此将其写为/\\
。
So, consider the id
function: 因此,请考虑id
函数:
id :: forall a. a -> a
id x = x
We can rewrite it as lambdas, moving the "type parameter" out of the type signature and adding inline type annotations: 我们可以将其重写为lambda,将“类型参数”移出类型签名并添加内联类型注释:
id = /\a -> (\x -> x) :: a -> a
Here's the same thing done to const
: 这与const
相同:
const = /\a b -> (\x y -> x) :: a -> b -> a
So your bar
function might be something like this: 因此,您的bar
函数可能是这样的:
bar = /\a -> (\f -> ('t', True)) :: (a -> a) -> (Char, Bool)
Note that the type of the function given to bar
as an argument depends on bar
's type parameter. 请注意,作为bar
赋予参数的函数类型取决于bar
的type参数。 Consider if you had something like this instead: 考虑一下是否有这样的事情:
bar2 = /\a -> (\f -> (f 't', True)) :: (a -> a) -> (Char, Bool)
Here bar2
is applying the function to something of type Char
, so giving bar2
any type parameter other than Char
will cause a type error. 在这里, bar2
将函数应用于Char
类型的对象,因此,给bar2
除Char
bar2
任何类型参数都会导致类型错误。
On the other hand, here's what foo
might look like: 另一方面,下面是foo
样子:
foo = (\f -> (f Char 't', f Bool True))
Unlike bar
, foo
doesn't actually take any type parameters at all! 与bar
不同, foo
实际上根本没有任何类型参数! It takes a function that itself takes a type parameter, then applies that function to two different types. 它使用一个本身带有类型参数的函数,然后将该函数应用于两种不同的类型。
So when you see a forall
in a type signature, just think of it as a lambda expression for type signatures . 因此,当您在类型签名中看到一个forall
时,只需将其视为类型签名的Lambda表达式即可 。 Just like regular lambdas, the scope of forall
extends as far to the right as possible, up to enclosing parenthesis, and just like variables bound in a regular lambda, the type variables bound by a forall
are only in scope within the quantified expression. 就像常规lambda一样, forall
范围会尽可能向右扩展,直至括起括号,并且就像常规lambda中绑定的变量一样, forall
所绑定的类型变量仅在量化表达式内。
Post scriptum : Perhaps you might wonder--now that we're thinking about functions taking type parameters, why can't we do something more interesting with those parameters than put them into a type signature? 邮政scriptum:也许你可能想知道-现在,我们关于采取类型参数函数的思想,我们为什么不能做一些更有趣的与那些参数不是把它们放入一个类型的签名? The answer is that we can! 答案是我们可以!
A function that puts type variables together with a label and returns a new type is a type constructor , which you could write something like this: 将类型变量和标签放在一起并返回新类型的函数是类型构造函数 ,您可以编写以下内容:
Either = /\a b -> ...
But we'd need completely new notation, because the way such a type is written, like Either ab
, is already suggestive of "apply the function Either
to these parameters". 但是我们需要全新的符号,因为这样的类型的写入方式(例如Either ab
)已经暗示“将函数Either
应用于这些参数”。
On the other hand, a function that sort of "pattern matches" on its type parameters, returning different values for different types, is a method of a type class . 另一方面,在其类型参数上进行某种“模式匹配”并为不同类型返回不同值的函数是类型类的方法 。 A slight expansion to my /\\
syntax above suggests something like this: 对我上面的/\\
语法稍作扩展,就可以看到以下内容:
fmap = /\ f a b -> case f of
Maybe -> (\g x -> case x of
Just y -> Just b g y
Nothing -> Nothing b) :: (a -> b) -> Maybe a -> Maybe b
[] -> (\g x -> case x of
(y:ys) -> g y : fmap [] a b g ys
[] -> [] b) :: (a -> b) -> [a] -> [b]
Personally, I think I prefer Haskell's actual syntax... 就个人而言,我认为我更喜欢Haskell的实际语法...
A function that "pattern matches" its type parameters and returns an arbitrary, existing type is a type family or functional dependency --in the former case, it even already looks a great deal like a function definition. “模式匹配”其类型参数并返回任意现有类型的函数是类型族或函数依赖关系 -在前一种情况下,它甚至看上去已经非常像函数定义。
The reason why there are different uses of this keyword is that it's actually used in at least two different type system extensions: higher-rank types, and existentials. 此关键字之所以有不同的用法,是因为它实际上在至少两个不同的类型系统扩展中使用:更高级别的类型和存在。
It's probably best just to read about and understand those two things separately, rather than trying to get an explanation of why 'forall' is an appropriate bit of syntax in both at the same time. 最好是分别阅读和理解这两件事,而不是试图同时解释为什么“ forall”是合适的语法。