我很确定他们不一样 . 然而,我陷入了一种常见的观念,即"Rust does not support"更高级的类型(HKT),而是提供参数多态性 . 我试图了解这一点并理解它们之间的区别,但却变得越来越纠结 .
根据我的理解,Rust中有更高级的类型,至少是基础知识 . 使用"*" -notation,HKT确实有一种例如 * -> *
. 例如, Maybe
属于 * -> *
,可以在Haskell中实现 .
data Maybe a = Just a | Nothing
这里,
-
Maybe
是一个类型构造函数,需要应用于具体类型才能成为具体的类型"*" . -
Just a
和Nothing
是数据构造函数 .
在关于Haskell的教科书中,这通常被用作高级类型的示例 . 但是,在Rust中它可以简单地实现为枚举,毕竟它是一个和类型:
enum Maybe<T> {
Just(T),
Nothing,
}
区别在哪里?据我所知,这是一个更好的类型的完美的例子 .
-
如果在Haskell中这被用作HKT的教科书示例,为什么说Rust没有
Maybe
枚举资格作为HKT? -
是否应该说Rust不完全支持HKT?
-
HKT和参数多态之间的根本区别是什么?
在查看函数时,这种混淆仍然存在,我可以编写一个参数函数,它可以使用 Maybe
,并且我可以将HKT理解为函数参数 .
fn do_something<T>(input: Maybe<T>) {
// implementation
}
再次,在Haskell中会是这样的
do_something :: Maybe a -> ()
do_something :: Maybe a -> ()
do_something _ = ()
这导致了第四个问题 .
- 对高等级类型的支持到底在哪里?什么是使Rust的类型系统无法表达HKT的最小例子?
相关问题:
我经历了很多与该主题相关的问题(包括他们对博客文章的链接等)但我找不到我的主要问题(1和2)的答案 .
更新
感谢您提供了许多非常详细且有很多帮助的好答案 . 我决定接受Andreas Rossberg的回答,因为他的解释帮助我走上了正确的轨道 . 特别是关于术语的部分 .
我真的陷入了这样一种思维循环,即所有类型的东西都更高 . 强调 * -> * -> *
和 (* -> *) -> *
之间差异的解释对我来说至关重要 .
4 回答
一些术语:
那种
*
有时被称为地面 . 您可以将其视为第0个订单 .任何一种形式
* -> * -> ... -> *
,其中至少有一个箭头是一阶的 .更高阶的类型是具有"nested arrow on the left"的类型,例如
(* -> *) -> *
.顺序本质上是箭头左侧嵌套的深度,例如,
(* -> *) -> *
是二阶,((* -> *) -> *) -> *
是三阶等 . (FWIW,相同的概念适用于类型本身:二阶函数是其类型的一个有例如形式(A -> B) -> C
. )非地类的类型(order> 0)也称为类型构造函数(有些文献仅将地类的类型称为"types") . 较高级的类型(构造函数)是其类型为高阶(order> 1)的类型 .
因此,较高级的类型是采用非地类的论证 . 这将需要非地面类型的类型变量,这在许多语言中是不受支持的 . Haskell中的示例:
后两者是较高的 .
同样,更高通道的多态性描述了(参数化)多态值的存在,这些值抽象了非基础类型 . 同样,很少有语言支持这一点 . 例:
Rust支持参数多态“而不是更高级别的类型”的说法没有意义 . 两者都是参数化的不同维度,彼此互补 . 当你将两者结合起来时,你就拥有了更高的多态性 .
Rust的一个简单例子't do is something like Haskell' s
Functor
class .请注意,实例是在类型构造函数
Maybe
或[]
上定义的,而不是完全应用的类型Maybe a
或[a]
.这不仅仅是一个客厅伎俩 . 它与参数多态性有很强的相互作用 . 由于
fmap
类型中的类型变量a
和b
不受类约束定义,Functor
的实例不能根据它们改变它们的行为 . 在推理类型代码时,这是一个非常强大的属性,而Haskell类型系统的优势来自于很多 .它还有另一个属性 - 你可以编写在高级类型变量中抽象的代码 . 这里有几个例子:
我承认,这些类型开始看起来像抽象的废话 . 但是当你有几个利用高级抽象的帮助者时,它们变得非常实用 .
(如果我在那里犯了任何错误,有人可以修复它们吗?我在没有编译器的情况下从内存中重新实现
lens
的最基本起点 . )函数
focusFirst
和focusSecond
可以作为第一个参数传递给get
或set
,因为它们类型中的类型变量f
可以与get
和set
中更具体的类型统一 . 能够在较高的kinded类型变量f
上进行抽象,允许特定形状的函数既可以用作任意数据类型中的setter,也可以用作getter . 这是导致lens
库的两个核心见解之一 . 没有这种抽象,它就不可能存在 .(对于它的 Value ,另一个关键的见解是将镜片定义为这样的功能,使镜片的组合成为简单的功能组合 . )
所以不,除了能够接受类型变量之外,还有更多内容 . 重要的部分是能够使用对应于类型构造函数的类型变量,而不是某些具体(如果未知)类型 .
参数多态只是指函数不能在其定义中使用类型(或种类)的任何特定特征的属性;它是一个完整的黑盒子 . 标准示例是
length :: [a] -> Int
,它仅适用于列表的结构,而不是列表中存储的特定值 .HKT的标准示例是
Functor
类,其中fmap :: (a -> b) -> f a -> f b
. 与length
不同,其中a
有*
,f
有类* -> *
.fmap
还展示了参数多态,因为fmap
无法在其定义中使用a
或b
的任何属性 .fmap
也展示了ad hoc多态性,因为定义可以根据定义它的特定类型构造函数_2934753来定制 . 也就是说,对于f ~ []
,f ~ Maybe
等,fmap
有单独的定义 . 区别在于f
是"declared"作为类型类定义的一部分,而不仅仅是fmap
的定义的一部分 . (实际上,添加了类型类以支持某种程度的特殊多态性 . 没有类型类,只存在参数多态 . 您可以编写一个支持一种具体类型或任何具体类型的函数,但不能支持一些较小的集合 . )我要恢复它:一个更高级的类型只是一个类型级的高阶函数 .
但是花点时间:
考虑
monad
变形金刚:这里,
什么是更高级的类型?
因为类型
*
并返回一种类型*
.它就像是类型上的函数,即类型的类型构造函数
在像Java这样的语言中,你无法做到
在Haskell中,T会有类型
*->*
,但是Java类型(即类)不能具有这种类型的类型参数,即更高级的类型 .另外,如果您不知道,在基本的Haskell中,表达式必须具有类型
*
的类型,即"concrete type" . 任何其他类型,如* -> *
.例如,您无法创建
Maybe
类型的表达式 . 它必须是应用于Maybe Int
,Maybe String
等参数的类型 . 换句话说,完全应用类型构造函数 .