首页 文章

参数多态与高等级类型有什么区别?

提问于
浏览
34

我很确定他们不一样 . 然而,我陷入了一种常见的观念,即"Rust does not support"更高级的类型(HKT),而是提供参数多态性 . 我试图了解这一点并理解它们之间的区别,但却变得越来越纠结 .

根据我的理解,Rust中有更高级的类型,至少是基础知识 . 使用"*" -notation,HKT确实有一种例如 * -> * . 例如, Maybe 属于 * -> * ,可以在Haskell中实现 .

data Maybe a = Just a | Nothing

这里,

  • Maybe 是一个类型构造函数,需要应用于具体类型才能成为具体的类型"*" .

  • Just aNothing 是数据构造函数 .

在关于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 回答

  • 9

    一些术语:

    • 那种 * 有时被称为地面 . 您可以将其视为第0个订单 .

    • 任何一种形式 * -> * -> ... -> * ,其中至少有一个箭头是一阶的 .

    • 更高阶的类型是具有"nested arrow on the left"的类型,例如 (* -> *) -> * .

    顺序本质上是箭头左侧嵌套的深度,例如, (* -> *) -> * 是二阶, ((* -> *) -> *) -> * 是三阶等 . (FWIW,相同的概念适用于类型本身:二阶函数是其类型的一个有例如形式 (A -> B) -> C . )

    非地类的类型(order> 0)也称为类型构造函数(有些文献仅将地类的类型称为"types") . 较高级的类型(构造函数)是其类型为高阶(order> 1)的类型 .

    因此,较高级的类型是采用非地类的论证 . 这将需要非地面类型的类型变量,这在许多语言中是不受支持的 . 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
    

    后两者是较高的 .

    同样,更高通道的多态性描述了(参数化)多态值的存在,这些值抽象了非基础类型 . 同样,很少有语言支持这一点 . 例:

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

    Rust支持参数多态“而不是更高级别的类型”的说法没有意义 . 两者都是参数化的不同维度,彼此互补 . 当你将两者结合起来时,你就拥有了更高的多态性 .

  • 15

    Rust的一个简单例子't do is something like Haskell' s Functor class .

    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
    

    请注意,实例是在类型构造函数 Maybe[] 上定义的,而不是完全应用的类型 Maybe a[a] .

    这不仅仅是一个客厅伎俩 . 它与参数多态性有很强的相互作用 . 由于 fmap 类型中的类型变量 ab 不受类约束定义, Functor 的实例不能根据它们改变它们的行为 . 在推理类型代码时,这是一个非常强大的属性,而Haskell类型系统的优势来自于很多 .

    它还有另一个属性 - 你可以编写在高级类型变量中抽象的代码 . 这里有几个例子:

    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)
    

    我承认,这些类型开始看起来像抽象的废话 . 但是当你有几个利用高级抽象的帮助者时,它们变得非常实用 .

    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)
    

    (如果我在那里犯了任何错误,有人可以修复它们吗?我在没有编译器的情况下从内存中重新实现 lens 的最基本起点 . )

    函数 focusFirstfocusSecond 可以作为第一个参数传递给 getset ,因为它们类型中的类型变量 f 可以与 getset 中更具体的类型统一 . 能够在较高的kinded类型变量 f 上进行抽象,允许特定形状的函数既可以用作任意数据类型中的setter,也可以用作getter . 这是导致 lens 库的两个核心见解之一 . 没有这种抽象,它就不可能存在 .

    (对于它的 Value ,另一个关键的见解是将镜片定义为这样的功能,使镜片的组合成为简单的功能组合 . )

    所以不,除了能够接受类型变量之外,还有更多内容 . 重要的部分是能够使用对应于类型构造函数的类型变量,而不是某些具体(如果未知)类型 .

  • 31

    参数多态只是指函数不能在其定义中使用类型(或种类)的任何特定特征的属性;它是一个完整的黑盒子 . 标准示例是 length :: [a] -> Int ,它仅适用于列表的结构,而不是列表中存储的特定值 .

    HKT的标准示例是 Functor 类,其中 fmap :: (a -> b) -> f a -> f b . 与 length 不同,其中 a*f 有类 * -> * . fmap 还展示了参数多态,因为 fmap 无法在其定义中使用 ab 的任何属性 .

    fmap 也展示了ad hoc多态性,因为定义可以根据定义它的特定类型构造函数_2934753来定制 . 也就是说,对于 f ~ []f ~ Maybe 等, fmap 有单独的定义 . 区别在于 f 是"declared"作为类型类定义的一部分,而不仅仅是 fmap 的定义的一部分 . (实际上,添加了类型类以支持某种程度的特殊多态性 . 没有类型类,只存在参数多态 . 您可以编写一个支持一种具体类型或任何具体类型的函数,但不能支持一些较小的集合 . )

  • 8

    我要恢复它:一个更高级的类型只是一个类型级的高阶函数 .

    但是花点时间:

    考虑 monad 变形金刚:

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

    这里,

    - 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
    

    什么是更高级的类型?

    m :: (* -> *)
    

    因为类型 * 并返回一种类型 * .

    它就像是类型上的函数,即类型的类型构造函数

    * -> *
    

    在像Java这样的语言中,你无法做到

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

    在Haskell中,T会有类型 *->* ,但是Java类型(即类)不能具有这种类型的类型参数,即更高级的类型 .

    另外,如果您不知道,在基本的Haskell中,表达式必须具有类型 * 的类型,即"concrete type" . 任何其他类型,如 * -> * .

    例如,您无法创建 Maybe 类型的表达式 . 它必须是应用于 Maybe IntMaybe String 等参数的类型 . 换句话说,完全应用类型构造函数 .

相关问题