简体   繁体   English

参数多态与更高类型的类型有什么区别?

[英]What's the difference between parametric polymorphism and higher-kinded types?

I am pretty sure they are not the same. 我很确定他们不一样。 However, I am bogged down by the common notion that "Rust does not support" higher-kinded types (HKT), but instead offers parametric polymorphism . 但是,我对“ Rust不支持”更高种类的类型(HKT)而是提供参数多态性这一普遍观念感到困惑。 I tried to get my head around that and understand the difference between these, but got just more and more entangled. 我试图绕开它并理解它们之间的区别,但是却越来越纠结。

To my understanding, there are higher-kinded types in Rust, at least the basics. 据我了解, 鲁斯特高kinded类型,至少是基本。 Using the "*"-notation, a HKT does have a kind of eg * -> * . 使用“ *”符号,HKT确实具有例如* -> * For example, Maybe is of kind * -> * and could be implemented like this in Haskell. 例如, Maybe* -> *的一种,可以在Haskell中这样实现。

data Maybe a = Just a | Nothing

Here, 这里,

  • Maybe is a type constructor and needs to be applied to a concrete type to become a concrete type of kind "*". Maybe是类型构造函数,需要将其应用于具体类型以成为类型“ *”的具体类型。
  • Just a and Nothing are data constructors. Just aNothing是数据构造函数。

In textbooks about Haskell, this is often used as an example for a higher-kinded type. 在有关Haskell的教科书中,这通常被用作高种类型的示例。 However, in Rust it can simply be implemented as an enum, which after all is a sum type : 但是,在Rust中,它可以简单地实现为枚举,它毕竟是sum类型

enum Maybe<T> {
    Just(T),
    Nothing,
}

Where is the difference? 区别在哪里? To my understanding this is a perfectly fine example of a higher-kinded type. 以我的理解,这是一种类型较高的很好的例子。

  1. If in Haskell this is used as a textbook example of HKTs, why is it said that Rust doesn't have HKT? 如果在Haskell中将其用作HKT的教科书示例,为什么会说Rust没有HKT? Doesn't the Maybe enum qualify as a HKT? Maybe枚举不符合HKT的资格吗?
  2. Should it rather be said that Rust doesn't fully support HKT? 应该说Rust没有完全支持HKT吗?
  3. What's the fundamental difference between HKT and parametric polymorphism? HKT和参数多态性之间的根本区别是什么?

This confusion continues when looking at functions, I can write a parametric function that takes a Maybe , and to my understanding a HKT as a function argument. 在查看函数时,这种困惑仍在继续,我可以编写一个采用Maybe的参数函数,并据我所知将HKT作为函数参数。

fn do_something<T>(input: Maybe<T>) {
    // implementation
}

again, in Haskell that would be something like 同样,在Haskell中,

do_something :: Maybe a -> ()
do_something :: Maybe a -> ()
do_something _ = ()

which leads to the fourth question. 这导致了第四个问题。

  1. Where exactly does the support for higher-kinded types end? 对更高种类的类型的支持到底在哪里结束? Whats the minimal example to make Rust's type system fail to express HKT? 使Rust的类型系统无法表达HKT的最小示例是什么?

Related Questions: 相关问题:

I went through a lot of questions related to the topic (including links they have to blogposts, etc.) but I could not find an answer to my main questions (1 and 2). 我遇到了许多与该主题相关的问题(包括它们与博客文章的链接等),但找不到主要问题的答案(1和2)。

  1. In Haskell, are "higher-kinded types" *really* types? 在Haskell中,“高级类型”是否真的是类型? Or do they merely denote collections of *concrete* types and nothing more? 还是仅表示* concrete *类型的集合而已?
  2. Generic struct over a generic type without type parameter 没有类型参数的泛型类型的泛型结构
  3. Higher Kinded Types in Scala Scala中的高级类型
  4. What types of problems helps "higher-kinded polymorphism" solve better? 哪些类型的问题可以帮助“更高种类的多态性”更好地解决?
  5. Abstract Data Types vs. Parametric Polymorphism in Haskell Haskell中的抽象数据类型与参数多态性

Update 更新资料

Thank you for the many good answers which are all very detailed and helped a lot. 感谢您提供的许多很好的答案,这些答案非常详细,对您有很大帮助。 I decided to accept Andreas Rossberg's answer since his explanation helped me the most to get on the right track. 我决定接受安德里亚斯·罗斯伯格(Andreas Rossberg)的回答,因为他的解释为我提供了最大的帮助,使其走上正确的道路。 Especially the part about terminology. 特别是有关术语的部分。

I was really locked in the cycle of thinking that everything of kind * -> * ... -> * is higher-kinded . 我真的陷入了那种以为一切* -> * ... -> *都是更高种类的想法的循环中。 The explanation that stressed the difference between * -> * -> * and (* -> *) -> * was crucial for me. 强调* -> * -> *(* -> *) -> *之间差异的解释对我来说至关重要。

Some terminology: 一些术语:

  • The kind * is sometimes called ground . 那种*有时被称为地面 You can think of it as 0th order. 您可以将其视为0阶。
  • Any kind of the form * -> * -> ... -> * with at least one arrow is first-order . * -> * -> ... -> *至少带有一个箭头的任何形式都是一阶的
  • A higher-order kind is one that has a "nested arrow on the left", eg, (* -> *) -> * . 高阶种类是具有“左侧的嵌套箭头”的种类,例如(* -> *) -> *

The order essentially is the depth of left-side nesting of arrows, eg, (* -> *) -> * is second-order, ((* -> *) -> *) -> * is third-order, etc. (FWIW, the same notion applies to types themselves: a second-order function is one whose type has eg the form (A -> B) -> C .) 顺序本质上是箭头左侧嵌套的深度,例如(* -> *) -> *是二阶的, ((* -> *) -> *) -> *是三阶的,依此类推(FWIW,相同的概念也适用于类型本身:二阶函数是其类型具有例如(A -> B) -> C形式的函数(A -> B) -> C

Types of non-ground kind (order > 0) are also called type constructors (and some literature only refers to types of ground kind as "types"). 非地面类型的类型(顺序> 0)也称为类型构造函数 (某些文献仅将地面类型的类型称为“类型”)。 A higher-kinded type (constructor) is one whose kind is higher-order (order > 1). 较高种类的类型(构造函数)是其种类较高的类型(顺序> 1)。

Consequently, a higher-kinded type is one that takes an argument of non-ground kind. 因此,一种类型较高的类型是接受非地面类型的参数的类型。 That would require type variables of non-ground kind, which are not supported in many languages. 这将需要非地面类型的类型变量,许多语言不支持这种类型的变量。 Examples in Haskell: Haskell中的示例:

type Ground = Int
type FirstOrder a = Maybe a  -- a is ground
type SecondOrder c = c Int   -- c is a first-order constructor
type ThirdOrder c = c Maybe  -- c is second-order

The latter two are higher-kinded. 后两个种类更高。

Likewise, higher-kinded polymorphism describes the presence of (parametrically) polymorphic values that abstract over types that are not ground. 同样, 种类较多的多态性描述了(参数)多态性值的存在,这些值在非基础类型上抽象。 Again, few languages support that. 同样,很少有语言支持这一点。 Example: 例:

f : forall c. c Int -> c Int  -- c is a constructor

The statement that Rust supports parametric polymorphism "instead" of higher-kinded types does not make sense. 声明Rust支持“代替”更高种类类型的参数多态是没有道理的。 Both are different dimensions of parameterisation that complement each other. 两者都是相互补充的参数化的不同维度。 And when you combine both you have higher-kinded polymorphism. 当您将两者结合在一起时,您将拥有更高种类的多态性。

A simple example of what Rust can't do is something like Haskell's Functor class. Rust不能做的一个简单示例就是Haskell的Functor类。

class Functor f where
    fmap :: (a -> b) -> f a -> f b

-- a couple examples:
instance Functor Maybe where
    -- fmap :: (a -> b) -> Maybe a -> Maybe b
    fmap _ Nothing  = Nothing
    fmap f (Just x) = Just (f x)

instance Functor [] where
    -- fmap :: (a -> b) -> [a] -> [b]
    fmap _ []     = []
    fmap f (x:xs) = f x : fmap f xs

Note that the instances are defined on the type constructor, Maybe or [] , instead of the fully-applied type Maybe a or [a] . 请注意,实例是在类型构造函数Maybe[] ,而不是在完全应用的类型Maybe a[a]

This isn't just a parlor trick. 这不仅仅是一个客厅的把戏。 It has a strong interaction with parametric polymorphism. 它与参数多态性有很强的交互作用。 Since the type variables a and b in the type fmap are not constrained by the class definition, instances of Functor cannot change their behavior based on them. 由于fmap类型中的类型变量ab不受类定义的约束,因此Functor实例无法基于它们更改其行为。 This is an incredibly strong property in reasoning about code from types, and where a lot of where the strength of Haskell's type system comes from. 在从类型进行代码推理时,这是一个不可思议的强大特性,而Haskell的类型系统的优势在很大程度上来自于此。

It has one other property - you can write code that's abstract in higher-kinded type variables. 它还有另一个属性-您可以在类型较高的类型变量中编写抽象的代码。 Here's a couple examples: 这是几个例子:

focusFirst :: Functor f => (a -> f b) -> (a, c) -> f (b, c)
focusFirst f (a, c) = fmap (\x -> (x, c)) (f a)

focusSecond :: Functor f => (a -> f b) -> (c, a) -> f (c, b)
focusSecond f (c, a) = fmap (\x -> (c, x)) (f a)

I admit, those types are beginning to look like abstract nonsense. 我承认,这些类型开始看起来像抽象的废话。 But they turn out to be really practical when you have a couple helpers that take advantage of the higher-kinded abstraction. 但是,当您有几个使用高级抽象的助手时,它们实际上是非常实用的。

newtype Identity a = Identity { runIdentity :: a }

instance Functor Identity where
    -- fmap :: (a -> b) -> Identity a -> Identity b
    fmap f (Identity x) = Identity (f x)

newtype Const c b = Const { getConst :: c }

instance Functor (Const c) where
    -- fmap :: (a -> b) -> Const c a -> Const c b
    fmap _ (Const c) = Const c

set :: ((a -> Identity b) -> s -> Identity t) -> b -> s -> t
set f b s = runIdentity (f (\_ -> Identity b) s)

get :: ((a -> Const a b) -> s -> Const a t) -> s -> a
get f s = getConst (f (\x -> Const x) s)

(If I made any mistakes in there, can someone just fix them? I'm reimplementing the most basic starting point of lens from memory without a compiler.) (如果我在那里犯了任何错误,那么有人可以解决这些错误吗?我正在重新实现lens最基本起点,而无需编译器。)

The functions focusFirst and focusSecond can be passed as the first argument to either get or set , because the type variable f in their types can be unified with the more concrete types in get and set . 可以将函数focusFirstfocusSecond作为第一个参数传递给getset ,因为它们类型中的类型变量f可以与getset更具体的类型统一。 Being able to abstract over the higher-kinded type variable f allows functions of a particular shape can be used both as setters and getters in arbitrary data types. 能够抽象出类型较高的类型变量f可以将特定形状的函数用作任意数据类型中的设置器和获取器。 This is one of the two core insights that led to the lens library. 这是导致lens库的两个核心见解之一。 It couldn't exist without this kind of abstraction. 没有这种抽象就不可能存在。

(For what it's worth, the other key insight is that defining lenses as a function like that allows composition of lenses to be simple function composition.) (就其价值而言,另一个关键的见解是将镜片定义为类似的功能,从而使镜片的构成成为简单的功能构成。)

So no, there's more to it than just being able to accept a type variable. 因此,不仅仅可以接受类型变量。 The important part is being able to use type variables that correspond to type constructors, rather than some concrete (if unknown) type. 重要的部分是能够使用与类型构造函数相对应的类型变量,而不是某些具体的(如果未知)类型。

Parametric polymorphism just refers to the property that the function cannot make use of any particular feature of a type (or kind) in its definition; 参数多态性只是指函数不能在其定义中使用类型(或种类)的任何特定特征的性质。 it is a complete blackbox. 这是一个完整的黑匣子。 The standard example is length :: [a] -> Int , which only works with the structure of the list, not the particular values stored in the list. 标准示例是length :: [a] -> Int ,它仅适用于列表的结构 ,不适用于列表中存储的特定值。

The standard example of HKT is the Functor class, where fmap :: (a -> b) -> fa -> fb . HKT的标准示例是Functor类,其中fmap :: (a -> b) -> fa -> fb Unlike length , where a has kind * , f has kind * -> * . length不同, a具有*f具有* -> * fmap also exhibits parametric polymorphism, because fmap cannot make use of any property of either a or b in its definition. fmap 表现出参数多态性,因为fmap不能在其定义中使用ab的任何属性。

fmap exhibits ad hoc polymorphism as well, because the definition can be tailored to the specific type constructor f for which it is defined. fmap表现出特殊的多态性,因为该定义可以针对为其定义的特定类型构造函数f进行定制。 That is, there are separate definitions of fmap for f ~ [] , f ~ Maybe , etc. The difference is that f is "declared" as part of the typeclass definition, rather than just being part of the definition of fmap . 也就是说,有单独的定义fmapf ~ [] f ~ Maybe ,等不同的是, f是“申报”为类型类定义的一部分,而不是仅仅是定义的一部分fmap (Indeed, typeclasses were added to support some degree of ad hoc polymorphism. Without type classes, only parametric polymorphism exists. You can write a function that supports one concrete type or any concrete type, but not some smaller collection in between.) (实际上,添加了类型类以支持某种程度的即席多态性。没有类型类,则存在参数多态性。您可以编写一个函数来支持一个具体类型或任何具体类型,但不支持两者之间的某些较小的集合。)

I'm going to resume it: a higher-kinded type is just a type-level higher-order function. 我将继续它:种类繁多的类型只是类型级别的高阶函数。

But take a minute: 但是花一点时间:

Consider monad transformers: 考虑一下monad变压器:

newtype StateT s m a :: * -> (* -> *) -> * -> *

Here, 这里,

- s is the desired type of the state
- m is a functor, another monad that StateT will wrap
- a is the return type of an expression of type StateT s m

What is the higher-kinded type? 什么是上等类型?

m :: (* -> *)

Because takes a type of kind * and returns a kind of type * . 因为采用类型*并返回类型*

It's like a function on types, that is, a type constructor of kind 就像类型上的函数,即类型的类型构造函数

* -> *

In languages like Java, you can't do 在Java之类的语言中,您不能

class ClassExample<T, a> {
    T<a> function()
}

In Haskell T would have kind *->* , but a Java type (ie class) cannot have a type parameter of that kind, a higher-kinded type. 在Haskell T中,类型为*->* ,但是Java类型(即类)不能具有该类型的类型参数,即类型较高的类型。

Also, if you don't know, in basic Haskell an expression must have a type that has kind * , that is, a "concrete type". 另外,如果您不知道,在基本的Haskell中,表达式必须具有带有kind *的类型,即“具体类型”。 Any other type like * -> * . 任何其他类型,例如* -> *

For instance, you can't create an expression of type Maybe . 例如,您不能创建Maybe类型的表达式。 It has to be types applied to an argument like Maybe Int , Maybe String , etc. In other words, fully applied type constructors. 它必须是应用于诸如Maybe IntMaybe String等之类的参数的类型。换句话说,就是完全应用的类型构造函数。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM