Rust为什么没有高阶类型?这是否有内在的原因解释?

25

Rust没有高种类类型。例如,在Rust中无法编写函子(因此也无法编写单子)。我想知道是否有深层次的原因解释了这一点,以及为什么。

例如,我可以理解的原因是:缺乏零成本抽象使HKT成为可能。或者类型推断变得更加困难。当然,我还在寻找一种解释,向我展示这是一个真正的限制。

如果答案已经在其他地方给出,请给我链接?

1个回答

27

时间与优先级.

没有Higher Kinded Types并不是一项设计决策。Rust计划引入某种形式的Higher Kinded Types,目前最受欢迎的候选者是通用关联类型 (2017)

然而,实现这些需要时间,并且相对于其他功能,它们还没有被视为优先级。例如,async/await比HKTs更受重视,同时const泛型也似乎被优先考虑。


例如,functor(因此monad)无法用Rust编写。 实际上,它们可以,虽然有点笨重。 请参阅Edmund's Smith lovely hack,他在https://www.reddit.com/r/rust/comments/cajn09/new_method_for_emulating_higherkinded_types_in/上发布了该方法:
trait Unplug {
    type F; //The representation type of the higher-kinded type
    type A; //The parameter type
}

trait Plug<A> {
    type result_t;
}

pub  struct  Concrete<M: Unplug + Plug<A>,A> {
    pub unwrap: <M as Plug<A>>::result_t
}

impl<M: Unplug + Plug<A>, A> Concrete<M,A> {
    fn of<MA: Unplug<F=M, A=A> + Plug<A>>(x: MA) -> Self
        where M: Plug<A, result_t = MA>
    {
        Concrete { unwrap: x }
    }
}

他们使用一个Functor特质来实现:

pub trait Functor: Unplug + Plug<<Self as Unplug>::A> {
    fn map<B, F>(f: F, s: Self) -> <Self as Plug<B>>::result_t
        where
            Self: Plug<B>,
            F: FnMut(<Self as Unplug>::A) -> B
        ;
}

//  Example impl for a represented Vec
impl<A> Functor for Concrete<Vec<forall_t>, A> {
    //  remember, Self ~ (Vec<_>, A) ~ "f a"
    fn map<B, F>(f: F, s: Self) -> <Self as Plug<B>>::result_t
        where
            F: FnMut(<Self as Unplug>::A) -> B 
    {        
        Concrete::of(s.unwrap.into_iter().map(f).collect())
    }
}

从那时起构建ApplicativeMonad

pub trait Applicative: Functor {
    fn pure(s: <Self as Unplug>::A) -> Self;

    fn app<B, F>(
        f: <Self as Plug<F>>::result_t, //M<F>
        s: Self                         //M<A>
    ) -> <Self as Plug<B>>::result_t   //M<B>
    where
        F: FnMut(<Self as Unplug>::A) -> B + Clone,
        Self: Plug<F> + Plug<B> + Unplug,
        <Self as Plug<F>>::result_t:
            Unplug<F=<Self as Unplug>::F, A=F> +
            Plug<F> +
            Clone,
        <Self as Unplug>::F: Plug<F>
    ;
}

pub trait Monad : Applicative {
    fn bind<F,B>(f: F, s: Self) -> <Self as Plug<B>>::result_t
    where
        Self: Plug<F>+Plug<B>,
        F: FnMut(<Self as Unplug>::A) ->
            <Self as Plug<B>>::result_t + Clone
        ;
}

我确实说过它有点笨重...


好的,非常感谢您的回复!我只是想知道为什么要使用像GAT这样棘手的东西来代替Haskell简单形式的HKT。但我可能漏掉了什么... - abitbol
3
@abitbol 我认为你忽略了一个事实,那就是你不能“只是”将类似 Haskell 的高阶类型(HKTs)添加到 Rust 中——你需要(1)定义该特性如何与语言的其他部分交互,以及(2)实际实现它。广义关联类型(GATs)是一个小而正交的特性,已经被完全定义并正在被实现。这个博客系列(共 4 部分)详细描述了 GATs(当时称为 ATCs)和 HKTs 之间的关系。 - trent
《第3部分》特别强调了Rust和Haskell之间的一些差异,这使得将Haskell的系统(部分)添加到Rust上变得很麻烦。 - trent
是的,我完全同意你的观点:关于Rust的当前状态,添加某种功能可能会很棘手。我只是想知道为什么最初的设计没有包括这个功能的原因。事实上,我只是对了解Rust的需求设计感兴趣,这使得Rust看起来像今天的Rust一样。但是你提供的链接可能会帮助我了解这一点,我会去阅读的。谢谢! - abitbol
我该如何跟踪 Rust 计划中 最终 要添加的内容? - user1857492
1
通过跟进讨论,特别是RFC文档,我会这么说。在这方面,这与Python / Java / C ++非常相似。 - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接