且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Scala 中的高级类型是什么?

更新时间:2023-01-13 23:41:28

让我通过消除歧义来弥补开始时的一些困惑.我喜欢用价值水平做类比来解释这一点,因为人们往往更熟悉它.

类型构造函数是一种可以应用于构造"的类型参数的类型.一种类型.

值构造函数是一个值,您可以将其应用于构造"的值参数.一个值.

值构造函数通常称为函数";或方法".这些构造函数"也被称为多态的"(因为它们可用于构建不同形状"的东西"),或抽象".(因为他们抽象了不同多态实例之间的差异).

在抽象/多态的上下文中,一阶是指一次性使用";抽象:你对一个类型进行一次抽象,但该类型本身不能对任何东西进行抽象.Java 5 泛型是一阶的.

上述抽象特征的一阶解释是:

类型构造函数是一种类型,您可以将其应用于构造"的正确类型参数.正确的类型.

值构造函数是一个值,您可以将其应用于适当的值参数以构造";一个合适的值.

强调不涉及抽象(我想你可以称之为零阶",但我没有看到在任何地方使用过),例如值 1 或类型 String,我们通常说的东西是一个适当的"值或类型.

适当的值是立即可用";从某种意义上说,它不等待参数(它不会对它们进行抽象).将它们视为可以轻松打印/检查的值(序列化函数是作弊!).

适当类型是对值进行分类的类型(包括值构造函数),类型构造函数不对任何值进行分类(它们首先需要应用于正确的类型参数以产生适当的类型).要实例化一个类型,它是一个适当的类型是必要的(但不是充分的).(可能是抽象类,也可能是您无权访问的类.)

高阶"只是一个通用术语,表示重复使用多态性/抽象.对于多态类型和值,这意味着同样的事情.具体来说,高阶抽象抽象了抽象事物.对于类型,术语更高级的"不适用.是更通用的高阶"的专用版本.

因此,我们表征的高阶版本变为:

类型构造函数是一种可以应用于类型参数(正确的类型或类型构造函数)以构造"的类型.一个适当的类型(构造函数).

值构造函数是一个值,您可以将其应用于值参数(正确的值或值构造函数)以构造"一个适当的值(构造函数).

因此,高阶"只是意味着当您说对 X 进行抽象"时,您是认真的!被抽象出来的X并没有失去它自己的抽象权":它可以抽象出它想要的一切.(顺便说一句,我在这里使用动词抽象"的意思是:省略对值或类型的定义不是必需的东西,以便抽象的用户可以改变/提供它作为一个论证.)

以下是正确、一阶和高阶值和类型的一些示例(灵感来自 Lutz 通过电子邮件提出的问题):

 适当的一阶高阶值 10 (x: Int) =>x(f:(Int => Int)) =>f(10)类型(类)字符串列表函子类型字符串 ({type λ[x] = x})#λ ({type λ[F[x]] = F[String]})#λ

所使用的类定义为:

class 字符串类列表[T]类函子[F[_]]

为了避免通过定义类的间接性,您需要以某种方式表达匿名类型函数,这些函数在 Scala 中无法直接表达,但您可以使用结构类型而无需太多语法开销(-style 是由于 https://***.com/users/160378/retronym afaik):

在支持匿名类型函数的某些假设的 Scala 未来版本中,您可以将示例中的最后一行缩短为:

类型(非正式地)字符串 [x] =>x [F[x]] =>F[String])//我再说一遍,这不是有效的 Scala,可能永远不会

(就个人而言,我很遗憾曾经谈论过高级类型",它们毕竟只是类型!当您绝对需要消除歧义时,我建议您说类型构造函数参数"之类的东西,类型构造函数成员"或类型构造函数别名",以强调您不是在谈论正确的类型.)

ps:更复杂的是,多态"以不同的方式是模棱两可的,因为多态类型有时意味着通用量化类型,例如 Forall T, T =>;T,这是一个适当的类型,因为它对多态值进行分类(在Scala中,这个值可以写成结构类型{def apply[T](x: T): T = x})

You can find the following on the web:

  1. Higher kinded type == type constructor?

    class AClass[T]{...} // For example, class List[T]
    

    Some say this is a higher kinded type, because it abstracts over types which would be compliant with the definition.

    Higher kinded types are types which take other types and construct a new type

    These though are also known as type constructor. (For example, in Programming in Scala).

  2. Higher kinded type == type constructor which takes type constructor as a type parameter?

    In the paper Generics of a Higher Kind, you can read

    ... types that abstract over types that abstract over types ('higher-kinded types') ..."

    which suggests that

    class XClass[M[T]]{...} // or
    
    trait YTrait[N[_]]{...} // e.g. trait Functor[F[_]]
    

    is a higher kinded type.

So with this in mind, it is difficult to distinguish between type constructor, higher kinded type and type constructor which takes type constructors as type parameter, therefore the question above.

Let me make up for starting some of this confusion by pitching in with some disambiguation. I like to use the analogy to the value level to explain this, as people tend to be more familiar with it.

A type constructor is a type that you can apply to type arguments to "construct" a type.

A value constructor is a value that you can apply to value arguments to "construct" a value.

Value constructors are usually called "functions" or "methods". These "constructors" are also said to be "polymorphic" (because they can be used to construct "stuff" of varying "shape"), or "abstractions" (since they abstract over what varies between different polymorphic instantiations).

In the context of abstraction/polymorphism, first-order refers to "single use" of abstraction: you abstract over a type once, but that type itself cannot abstract over anything. Java 5 generics are first-order.

The first-order interpretation of the above characterizations of abstractions are:

A type constructor is a type that you can apply to proper type arguments to "construct" a proper type.

A value constructor is a value that you can apply to proper value arguments to "construct" a proper value.

To emphasize there's no abstraction involved (I guess you could call this "zero-order", but I have not seen this used anywhere), such as the value 1 or the type String, we usually say something is a "proper" value or type.

A proper value is "immediately usable" in the sense that it is not waiting for arguments (it does not abstract over them). Think of them as values that you can easily print/inspect (serializing a function is cheating!).

A proper type is a type that classifies values (including value constructors), type constructors do not classify any values (they first need to be applied to the right type arguments to yield a proper type). To instantiate a type, it's necessary (but not sufficient) that it be a proper type. (It might be an abstract class, or a class that you don't have access to.)

"Higher-order" is simply a generic term that means repeated use of polymorphism/abstraction. It means the same thing for polymorphic types and values. Concretely, a higher-order abstraction abstracts over something that abstracts over something. For types, the term "higher-kinded" is a special-purpose version of the more general "higher-order".

Thus, the higher-order version of our characterization becomes:

A type constructor is a type that you can apply to type arguments (proper types or type constructors) to "construct" a proper type (constructor).

A value constructor is a value that you can apply to value arguments (proper values or value constructors) to "construct" a proper value (constructor).

Thus, "higher-order" simply means that when you say "abstracting over X", you really mean it! The X that is abstracted over does not lose its own "abstraction rights": it can abstract all it wants. (By the way, I use the verb "abstract" here to mean: to leave out something that is not essential for the definition of a value or type, so that it can be varied/provided by the user of the abstraction as an argument.)

Here are some examples (inspired by Lutz's questions by email) of proper, first-order and higher-order values and types:

                   proper    first-order           higher-order

values             10        (x: Int) => x         (f: (Int => Int)) => f(10)
types (classes)    String    List                  Functor
types              String    ({type λ[x] = x})#λ   ({type λ[F[x]] = F[String]})#λ

Where the used classes were defined as:

class String
class List[T]
class Functor[F[_]]

To avoid the indirection through defining classes, you need to somehow express anonymous type functions, which are not expressible directly in Scala, but you can use structural types without too much syntactic overhead (the -style is due to https://***.com/users/160378/retronym afaik):

In some hypothetical future version of Scala that supports anonymous type functions, you could shorten that last line from the examples to:

types (informally) String    [x] => x              [F[x]] => F[String]) // I repeat, this is not valid Scala, and might never be

(On a personal note, I regret ever having talked about "higher-kinded types", they're just types after all! When you absolutely need to disambiguate, I suggest saying things like "type constructor parameter", "type constructor member", or "type constructor alias", to emphasize that you're not talking about just proper types.)

ps: To complicate matters further, "polymorphic" is ambiguous in a different way, since a polymorphic type sometimes means a universally quantified type, such as Forall T, T => T, which is a proper type, since it classifies polymorphic values (in Scala, this value can be written as the structural type {def apply[T](x: T): T = x})