如何实现扁平化(ffa:F〈F< A>>)->< A> rust 中的F?

a2mppw5e  于 2023-02-08  发布在  其他
关注(0)|答案(1)|浏览(132)

问题和我的目标

我的目标是让this (playground)编译。下面是代码的核心。这是尝试实现所有F<F<A>>FlatMap,如Option<Option<i32>>

trait HKT1 {
    type Unwrapped;
    type Wrapped<T>;
}

trait FlatMap: HKT1 + Sized {
    fn flat_map<B, F>(self, f: F) -> Self::Wrapped<B>
    where
        F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>;

    // Below cannot compile
    fn flatten<A>(ffa: Self::Wrapped<Self::Wrapped<A>>) -> Self::Wrapped<A>
    // How to set generic bound correctly?
    where
        Self::Wrapped<Self::Wrapped<A>>: FlatMap
    {
        ffa.flat_map(|x| x)
    }
}
error[E0308]: mismatched types
  --> src/main.rs:15:26
   |
15 |         ffa.flat_map(|x| x)
   |                          ^ expected HKT1::Wrapped, found HKT1::Unwrapped
   |
   = note: expected associated type `<<Self as HKT1>::Wrapped<<Self as HKT1>::Wrapped<A>> as HKT1>::Wrapped<_>`
              found associated type `<<Self as HKT1>::Wrapped<<Self as HKT1>::Wrapped<A>> as HKT1>::Unwrapped`

error[E0308]: mismatched types
  --> src/main.rs:15:9
   |
6  | trait FlatMap: HKT1 + Sized {
   | --------------------------- this type parameter
...
11 |     fn flatten<A>(ffa: Self::Wrapped<Self::Wrapped<A>>) -> Self::Wrapped<A>
   |                                                            ---------------- expected `<Self as HKT1>::Wrapped<A>` because of return type
...
15 |         ffa.flat_map(|x| x)
   |         ^^^^^^^^^^^^^^^^^^^ expected type parameter `Self`, found associated type
   |
   = note: expected associated type `<Self as HKT1>::Wrapped<A>`
              found associated type `<<Self as HKT1>::Wrapped<<Self as HKT1>::Wrapped<A>> as HKT1>::Wrapped<_>`
   = note: you might be missing a type parameter or trait bound

HKT1是一种模拟F<_>的方法,允许所有基于它的traits分别访问F_,我从this blog学到了这一点。

我想要做的

我正在尝试构建一个函数式编程箱,它可以将Scala lib cats转换为Rust.(当前的工作是here)。为了实现更高级的类型,我遵循this blog并定义HKT1 trait。它非常优雅,直到我想实现flatten(ffa: F<F<A>>) -> F<A>,但无法弄清楚如何正确设置泛型边界。
如果我在trait中将其留空,似乎很容易实现。例如:

trait FlatMap: HKT1 + Sized {
    fn flat_map<B, F>(self, f: F) -> Self::Wrapped<B>
    where
        F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>;
        
    fn flatten<A>(ffa: Self::Wrapped<Self::Wrapped<A>>) -> Self::Wrapped<A>;
}

struct MyF<T>(pub T);

impl<A> HKT1 for MyF<A> {
    type Unwrapped = A;
    type Wrapped<T> = MyF<T>;
}

impl<T> FlatMap for MyF<T> {
    fn flat_map<B, F>(self, f: F) -> MyF<B>
    where
        F: FnOnce(Self::Unwrapped) -> MyF<B>,
    {
        f(self.0)
    }
    
    fn flatten<A>(ffa: MyF<MyF<A>>) -> MyF<A> {
        ffa.flat_map(|x| x)
    }
}

但是我希望通过flat_map有一个默认的实现,我该如何实现呢?

vuktfyat

vuktfyat1#

通过EqT的解决方案

我刚刚从the implementation of crate functional中学习了如何实现这个功能。(好吧,实现与上面的有点不同)
关键思想是添加一个trait EqT,其目的是Assert两个类型是相等的。

trait HKT1 {
    type Unwrapped;
    type Wrapped<T>;
}

trait FlatMap: HKT1 + Sized {
    fn flat_map<B, F>(self, f: F) -> Self::Wrapped<B>
    where
        F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>;
        
    fn flatten<A>(self) -> Self::Wrapped<A>
    where
        Self::Unwrapped: EqT<Self::Wrapped<A>>,
    {
        self.flat_map(|x| x.cast())
    }
}

trait EqT<T> {
    fn cast(self) -> T;
}
impl<T> EqT<T> for T {
    fn cast(self) -> T {
        self
    }
}

这样我们就可以轻松地实现和使用flatten

struct MyF<T>(pub T);

impl<A> HKT1 for MyF<A> {
    type Unwrapped = A;
    type Wrapped<T> = MyF<T>;
}

impl<T> FlatMap for MyF<T> {
    fn flat_map<B, F>(self, f: F) -> MyF<B>
    where
        F: FnOnce(Self::Unwrapped) -> MyF<B>,
    {
        f(self.0)
    }
}

let ffa = MyF(MyF(1));
let fa = ffa.flatten();
println!("{:?}", fa); // Expect MyF(1)

相关问题