简体   繁体   English

为什么是forall a。 一个不被认为是Int的子类型,而我可以使用类型forall a的表达式。 预计会有任何一种类型的Int?

[英]Why is forall a. a not considered a subtype of Int while I can use an expression of type forall a. a anywhere one of type Int is expected?

Consider the following pair of function definitions, which pass the type checker: 考虑以下一对函数定义,它们传递类型检查器:

a :: forall a. a
a = undefined

b :: Int
b = a

Ie an expression of type forall a. a 即表达类型forall a. a forall a. a can be used where one of type Int is expected. forall a. a可以在期望Int类型之一的地方使用。 This seems to me a lot like subtyping but it is claimed the type system of Haskell lacks subtyping. 这在我看来很像子类型,但据称Haskell的类型系统缺少子类型。 How do these forms of substitutability differ? 这些形式的可替代性有何不同?

This question is not specific to forall a. a 这个问题不是特定于forall a. a forall a. a . forall a. a Other examples include: 其他例子包括:

id :: forall a. a -> a
id x = x

idInt :: Int -> Int
idInt = id

In typed lambda calculi, we have the typing relation, usually denoted as : or in Haskell as :: . 在类型化的lambda calculi中,我们有类型关系,通常表示为:或者在Haskell中表示为:: In general the relation is "many to many", so a type can contain multiple values and also a value can have many types. 通常,关系是“多对多”,因此类型可以包含多个值,并且值可以有多种类型。

In particular in polymorphic type systems a value can have multiple types. 特别是在多态类型系统中,值可以具有多种类型。 For example 例如

map :: (a -> b) -> [a] -> [b]

but also 但是也

map :: (Int -> Int) -> [Int] -> [Int].

In such type systems it's (sometimes) possible to define a relation on types with the meaning "more general type than", a type order . 在这种类型系统中,(有时)可以定义关于类型的关系,其含义是“比通用类型更多”, 类型顺序 If t ⊑ s then t is more general than s , meaning that if M : t then also M : s , and the typing rules of such a type system allow to infer exactly that. 如果t ⊑ sts更通用,这意味着如果M : t也是M : s ,并且这种类型系统的输入规则允许准确​​地推断出。 Or we say that s is a specialization of t . 或者我们说st So in this sense, there is a subtyping relation on types. 所以在这个意义上, 类型有一个子类型关系。

However, when we talk about subtyping in object oriented languages, we usually mean nominal subtyping , that is, we declare which types are subtypes of what, like when we define class inheritance. 但是,当我们谈论面向对象语言中的子类型时,我们通常意味着名义上的子类型 ,也就是说,我们声明哪些类型是什么的子类型,就像我们定义类继承时一样。 While in Haskell, it's a property of types, independent of any declarations. 在Haskell中,它是类型的属性,独立于任何声明。 For example any type is a specialization of forall a . a 例如,任何类型都是forall a . a forall a . a . forall a . a

For the Hindley-Milner type system , which allows type inference and which is the base for most functional languages, there is the notion of principal type : If an expression M has a (any) type, then it also has its principal type, and the principal type is the most general type of all the possible types of M . 对于允许类型推断并且是大多数函数式语言的基础的Hindley-Milner类型系统 ,存在主体类型的概念:如果表达式M具有(任何)类型,那么它也具有其主要类型,并且主要类型是所有可能类型的M的最一般类型。 The key feature is that the HM type inference algorithm always finds the most general type. 关键特征是HM类型推断算法总是找到最通用的类​​型。 So the most general, inferred principal type can be specialized to any valid type of M . 因此,最常见的推断主体类型可以专用于任何有效类型的M

With a question like this, I would take a step back and say that, fundamentally, the mathematical theories that underlie Haskell's design are System F variants that do not have the concept of subtyping. 有了这样的问题,我会退一步说,从根本上说,构成Haskell设计基础的数学理论是没有子类型概念的System F变体。

Yes, it is possible to look at Haskell's surface syntax and notice that there are cases like what you bring up, where an expression of some type T can be used in any context where T' is expected. 是的,可以查看Haskell的表面语法,并注意到有些情况就像你提出的那样,某些类型T的表达式可以在任何需要T'上下文中使用。 But this doesn't arise because Haskell was designed to support subtypes. 但这并不会出现,因为Haskell旨在支持子类型。 Rather, it arises as an accident of the fact that Haskell was designed to be more user-friendly than a faithful rendering of System F would be. 相反,它出现的事实是,Haskell被设计为比系统F的忠实渲染更加用户友好。

In this case, it has to do with the fact that type-level quantifiers are generally not written explicitly in Haskell code, and type level lambdas and applications never are. 在这种情况下,它与以下事实有关:类型级量词通常不是在Haskell代码中显式编写的,类型级lambda和应用程序永远不会 If you look at the type forall a. a 如果你看一下类型forall a. a forall a. a from a System F angle, the substitutability into Int contexts goes away. forall a. a从系统F角度,可替代性进入Int语境消失。 a :: forall a. a a :: forall a. a is a type level function and cannot be used in a context that expects Int —you need to first apply it to Int to get a Int :: Int , which is then what you can actually use in an Int context. a :: forall a. a是类型级别函数,不能在期望Int的上下文中使用 - 您需要首先将它应用于Int以获取a Int :: Int ,然后您可以在Int上下文中实际使用它。 Haskell's syntax hides that in the name of user-friendliness, but it's there in the underlying theory. Haskell的语法以用户友好的名义隐藏,但它存在于基础理论中。

So in short, while you could analyze Haskell by tabulating which expression types can be substituted into which context types and demonstrate that there is some sort of crypto-subtype relation, it's just not fruitful because it yields analyses that swim against the current of the design. 简而言之,虽然您可以通过将可以将哪些表达式类型替换为哪种上下文类型并且证明存在某种加密子类型关系来分析Haskell,但它只是没有成效,因为它会产生与设计当前游泳相关的分析。 And it's not so much a matter of technicalities as it is of intent and other human factors. 它不是技术问题,而是意图和其他人为因素。

You are correct that the types a value of type forall a. a 你是正确的类型forall a. a类型的值forall a. a forall a. a can be used wherever an Int is expected and that this implies a subtype relationship between the two types. forall a. a可以在期望Int任何地方使用,这意味着两种类型之间的子类型关系。 The other answers above try to convince you that this "more-polymorphic-than"-relation is not subtyping. 上面的其他答案试图说服你,这种“多态多于”的关系不是子类型。 However, while it is certainly different from the forms of subtyping found in typical object-oriented languages, this does not mean that the "more-polymorphic-than"-relation cannot be seen as a (different) form of subtyping. 然而,虽然它与典型的面向对象语言中的子类型形式肯定不同,但这并不意味着“更多 - 多态”的关系不能被视为(不同的)子类型形式。 In fact, some formalisations of polymorphic type systems model exactly this relation in their subtype relation. 实际上,多态类型系统的一些形式化在它们的子类型关系中精确地建模了这种关系。 This is the case, for example, in the type system in Odersky and Läufer's paper "Putting type annotations to work" . 例如,在Odersky和Läufer的论文“将类型注释投入工作”中的类型系统就是这种情况。

By :: a we mean "Any type", but not a subtype. 通过:: a我们的意思是“任何类型”,但不是子类型。 a could be Int , or Bool , or IO (Maybe Ordering) , but none in particular. a 可以Int ,或Bool ,或IO (Maybe Ordering) ,但没有特别的。 a is not a type exactly, but a type variable. a不是一个完全类型,而是一个类型变量。

Let's say we have a function like this: 假设我们有这样的函数:

id x = x

The compiler understands that there is no specific type for our argument x . 编译器理解我们的参数x没有特定的类型。 We can use any type for x , just as long as it's equivalent to whatever comes out of id. 我们可以使用x 任何类型,只要它等同于id的任何类型。 So, we write the signature as so: 所以,我们写签名如下:

--    /- Any type in...
--    |    /- ...same type out.
--    V    V
id :: a -> a

Remember that types begin with a capital letter in Haskell. 请记住,类型以Haskell中的大写字母开头。 This is not a type: it is a type variable! 这不是一个类型:它是一个类型变量!

We use polymorphism because it's easier to do so. 我们使用多态,因为它更容易实现。 For instance, composition is a useful idea: 例如,合成是一个有用的想法:

(>>>) :: (a -> b) -> (b -> c) -> (a -> c)
(>>>) f g a = g (f a)

So we can write things like: 所以我们可以这样写:

plusOneTimesFive :: Int -> Int
plusOneTimesFive = (+1) >>> (* 5)

reverseHead :: [Bool] -> Bool
reverseHead = reverse >>> head

But what if we had to write every type out like this: 但是,如果我们必须像这样编写每种类型:

(>>>) :: (Bool -> Int) -> (Int -> String) -> (Bool -> String)
(>>>) f g a = g (f a)

(>>>') :: (Ordering -> Double) -> (Double -> IO ()) -> (Ordering -> IO ())
(>>>') f g a = g (f a)

(>>>'') :: (Int -> Int) -> (Int -> Bool) -> (Int -> Bool)
(>>>'') f g a = g (f a)

-- ...and so on.

That'd just be silly. 那只是愚蠢的。

So the compiler infers the type using type unification like so: 所以编译器使用类型统一来推断类型,如下所示:

Let's say I input this into GHCi. 假设我把它输入GHCi。 Let's say 6 in an Int for simplicity here. 为简单起见,我们在Int6

id 6

The compiler thinks: " id :: a -> a , and it's being passed an Int , so a = Int , so id 6 :: Int . 编译器认为:“ id :: a -> a ,它正在传递一个Int ,所以a = Int ,所以id 6 :: Int

This is not subtyping. 不是子类型。 Subtyping could be captured using typeclasses, but this is basic polymorphism at play. 可以使用类型类捕获子类型,但这是基本的多态性。

It's not subtyping, it's type unification! 它不是子类型,它是类型统一!

a :: forall a. a
a = undefined

b :: Int
b = a

In b = a , we're constraining b and a to be of the same type, so the compiler checks that that is possible. b = a ,我们将ba约束为相同的类型,因此编译器会检查是否可能。 a has type forall a. a a有类型forall a. a forall a. a , which, by definition, unifies with every type, so the compiler oks our constraint. forall a. a ,根据定义,它与每个类型统一,因此编译器会提出我们的约束。

Type unification also lets us do things like: 类型统一还允许我们执行以下操作:

f :: (a -> Int) -> a
g :: (String -> b) -> b
h :: String -> Int
h = f g

Walking through the unification, f :: (a -> Int) -> a means g must have type a -> Int , which means a -> Int must unify with (String -> b) -> b , so b must b must be Int , which gives g the concrete type (String -> Int) -> Int , which means a is String -> Int . 通过统一, f :: (a -> Int) -> a表示g必须有类型a -> Int ,这意味着a -> Int必须与(String -> b) -> b统一,所以b必须b必须是Int ,这给g的具体类型(String -> Int) -> Int ,这意味着aString -> Int

Neither a -> Int nor (String -> b) -> b is a subtype of the other, but they can be unified as (String -> Int) -> Int . a -> Int(String -> b) -> b都不是另一个的子类型,但它们可以统一为(String -> Int) -> Int

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么id的类型不能专门用于(forall a.a - > a) - >(forall b.b - > b)? - Why can't the type of id be specialised to (forall a. a -> a) -> (forall b. b -> b)? 为什么是 `[1, "a"] :: [forall a. 不允许显示 a => a]`? - Why is `[1, "a"] :: [forall a. Show a => a]` not allowed? forall a之间有什么区别? [a]和[forall a。一个]? - What is the difference between forall a. [a] and [forall a. a]? 如何投射`forall a。 a -> a` 回到 `a -> a`? - How to cast `forall a. a -> a` back to `a -> a`? 是否存在“ forall v。Int-> v-> IO v”类型的有用居民? - Is there a useful inhabitant for the type `forall v. Int -> v -> IO v`? 使用量化约束导出 Ord(forall a.Ord a => Ord (fa)) - Derive Ord with Quantified Constraints (forall a. Ord a => Ord (f a)) 翻译/编码Haskell的`data Obj = forall a。 (显示a)=> Scala中的Obj a` - Translate/encode Haskell's `data Obj = forall a. (Show a) => Obj a` in Scala 类型类函数的显式forall - Explicit forall on a type class function 为什么`forall`需要在数据定义中具有多态类型? - Why is `forall` required to have a polymorphic type in data definition? Haskell显式forall在lhs上有“缺失”类型参数 - Haskell explicit forall with “missing” type parameter on lhs
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM