![](/img/trans.png)
[英]Why is `[1, "a"] :: [forall a. Show a => a]` not allowed?
[英]What is the difference between forall a. [a] and [forall a. a]?
标题和标签应该充分解释这个问题。
标题和标签应该充分解释这个问题。
呃,不是真的。 您使用了标记existential-type
,但您提供的类型都不存在。
这里已经有了一些很好的答案,所以我会采取不同的方法并且更加正式。 多态值本质上是类型上的函数,但Haskell的语法使得类型抽象和类型应用都隐含,这掩盖了问题。 我们将使用System F的符号,它具有显式类型抽象和类型应用程序。
例如,将编写熟悉的map
功能
map :: ∀a. ∀b. (a → b) → [a] → [b]
map = Λa. Λb. λ(f :: a → b). λ(xs :: [a]). case xs of
[] → []
(y:ys) → f y : map @a @b f ys
map
现在是四个参数的函数:两种类型( a
和b
),一个函数和一个列表。 我们使用Λ(大写lambda)在类型上编写函数,并且像往常一样使用λ函数。 包含Λ的项产生包含∀的类型,就像包含λ的项一样,产生包含→的类型。 我使用符号@a
(如在GHC Core中)来表示类型参数的应用。
因此,多态类型的值就像从类型到值的函数。 多态函数的调用者可以选择一个类型参数,该函数必须符合。
那么,我们如何写一个类型为∀a. [a]
的术语∀a. [a]
∀a. [a]
? 我们知道包含∀的类型来自包含Λ的术语:
term1 :: ∀a. [a]
term1 = Λa. ?
身体内有标记?
我们必须提供[a]
类型的术语。 也就是说,类型为∀a. [a]
的术语∀a. [a]
∀a. [a]
表示“给定任何类型a
,我会给你一个类型[a]
的列表”。
但是,我们对a
没有具体了解,因为它是从外部传入的一个论点。 所以我们可以返回一个空列表
term1 = Λa. []
或未定义的值
term1 = Λa. undefined
或仅包含未定义值的列表
term1 = Λa. [undefined, undefined]
但没有其他的。
怎么样[∀a. a]
[∀a. a]
,那么? 如果∀表示类型的函数,那么[∀a. a]
[∀a. a]
是一系列功能。 我们可以尽可能少地提供:
term2 :: [∀a. a]
term2 = []
或者尽可能多:
term2 = [f, g, h]
但是我们对f
, g
和h
选择是什么?
f :: ∀a. a
f = Λa. ?
现在我们真的被困了。 我们必须提供类型a
的值,但我们对类型a
一无所知。 所以我们唯一的选择是
f = Λa. undefined
所以我们对term2
的选择看起来像
term2 :: [∀a. a]
term2 = []
term2 = [Λa. undefined]
term2 = [Λa. undefined, Λa. undefined]
等等,让我们不要忘记
term2 = undefined
通用(∀)类型的值是从类型到值的函数。 存在(∃)类型的值是一对类型和值。
更具体地说:类型的值
∃x. T
是一对
(S, v)
其中S是一个类型,其中v :: T
,假设你将类型变量x
绑定到T
S
这是一个存在类型签名和一些类型的术语:
term3 :: ∃a. a
term3 = (Int, 3)
term3 = (Char, 'x')
term3 = (∀a. a → Int, Λa. λ(x::a). 4)
换句话说,我们可以将任何我们喜欢的价值放入∃a. a
∃a. a
,只要我们将该值与其类型配对。
类型为∀a. a
的值的用户 ∀a. a
∀a. a
处于有利位置; 他们可以使用类型应用程序@T
选择他们喜欢的任何特定类型,以获得类型为T
的术语。 类型为∀a. a
的生产者 ∀a. a
∀a. a
处于一个可怕的位置:他们必须准备好生产任何要求的类型,所以(如上一节所述)唯一的选择是Λa. undefined
Λa. undefined
。
类型为∃a. a
的值的用户 ∃a. a
∃a. a
处于可怕的位置; 里面的值是某种未知的特定类型,而不是灵活的多态值。 类型为∃a. a
的生产者 ∃a. a
∃a. a
处于有利位置; 正如我们上面所看到的那样,他们可以将他们喜欢的任何价值都粘在一对
那么什么是一个不那么无用的存在主义? 如何将值与二元运算配对:
type Something = ∃a. (a, a → a → a, a → String)
term4_a, term4_b :: Something
term4_a = (Int, (1, (+) @Int , show @Int))
term4_b = (String, ("foo", (++) @Char, λ(x::String). x))
使用它:
triple :: Something → String
triple = λ(a, (x :: a, f :: a→a→a, out :: a→String)).
out (f (f x x) x)
结果:
triple term4_a ⇒ "3"
triple term4_b ⇒ "foofoofoo"
我们打包了一种类型和一些类型的操作。 用户可以应用我们的操作但不能检查具体值 - 我们不能在triple
中对x
进行模式匹配,因为它的类型(因此构造函数集)是未知的。 这有点像面向对象的编程。
使用∃和类型 - 值对的存在性的直接语法将非常方便。 UHC部分支持这种直接语法。 但GHC没有。 要在GHC中引入存在性,我们需要定义新的“包装”类型。
翻译上面的例子:
{-# LANGUAGE ExistentialQuantification #-}
data Something = forall a. MkThing a (a -> a -> a) (a -> String)
term_a, term_b :: Something
term_a = MkThing 1 (+) show
term_b = MkThing "foo" (++) id
triple :: Something -> String
triple (MkThing x f out) =
out (f (f x x) x)
我们的理论处理有两点不同。 类型应用程序,类型抽象和类型对再次是隐式的。 此外,包装器使用forall
而不是exists
而令人困惑地写入。 这引用了我们声明一个存在类型的事实,但数据构造函数具有通用类型:
MkThing :: forall a. a -> (a -> a -> a) -> (a -> String) -> Something
通常,我们使用存在量化来“捕获”类型类约束。 我们可以在这里做类似的事情:
data SomeMonoid = forall a. (Monoid a, Show a) => MkMonoid a
有关该理论的介绍,我强烈推荐Pierce的类型和编程语言 。 有关GHC中存在类型的讨论,请参阅GHC手册和Haskell维基 。
类型forall a. [a]
forall a. [a]
表示对于任何单一类型,它都是包含该类型的列表。 这也是普通[a]
意思,是[]
的类型,空列表数据构造函数。
类型[forall a. a]
[forall a. a]
意味着你有一个具有多态类型的值列表,也就是说,每个值都是任何可能类型的值,不一定与列表中的其他元素相同。 没有可能的值可以具有类型forall a. a
forall a. a
,所以这也必须是一个空列表。
不同的是,虽然第一个可以用作任何类型的列表(根据定义,基本上),但后者不能用作任何具体类型的列表,因为没有办法固定它到任何一种类型。
为了解决标记 - 存在类型是在某个范围内将被实例化为某种未知的具体类型的类型。 它可以是任何东西,所以用forall a. a
forall a. a
的上方。 为了确保具有存在类型的任何内容仅在实际类型可用的范围内使用,编译器会阻止存在类型“转义”。
将forall
量词视为lambda表达式可能有所帮助 - 它引入了一个新的类型变量并在某个范围内绑定了该标识符。 在该范围之外,标识符没有意义,这就是为什么forall a. a
forall a. a
是好看不中用。
当用于类型时, forall
意味着交叉。 所以forall a. a
forall a. a
是所有类型或类似于Int ∩ String ∩ ...
的交集,它似乎给出了空集,但每个类型在Haskell中有一个名为bottom或⊥或undefined
的额外元素。 从此我们得到了那个forall a. a = {⊥}
forall a. a = {⊥}
。 实际上我们可以定义一个只包含底部的类型:
data Zero
在此设置之后,让我们看一下以[forall a. a]
开头]开头的类型[forall a. a]
[forall a. a]
。 它定义的是底部列表或[Zero]
,其中包含元素[], [undefined], [undefined, undefined], ...
让我们在ghci中检查它:
> let l = [undefined, undefined]::[Zero]
> :t l
l :: [Zero]
以类似的方式发展forall a. [a]
forall a. [a]
是所有列表类型的交集,因为∩[a] = [∩a]
这又是[Zero]
。
要做最后的检查,我们来定义:
type L = forall a. [a]
type U = [forall a. a]
并在ghci:
> let l2 = [undefined, undefined]::L
> let l3 = [undefined, undefined]::U
> :t l2
l2 :: [a]
> :t l3
l3 :: U
注意l2::[a]
,解释是Haskell在所有多态类型之前放置了一个隐含的forall
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.