Haskell 构造类似于 Rust trait 对象

Haskell construct analogous to Rust trait objects

Haskell支持类型classes,如等式:

class Eq a where 
  (==)                  :: a -> a -> Bool

Rust 对 type traits:

做同样的事情
pub trait Draw {
    fn draw(&self);
}

现在,可以在 Haskell 中声明一个列表,其元素必须属于相等类型 class:Eq a => [a](我相信 a 被称为 约束类型 in Haskell)。但是,列表的元素仍然必须是同一类型!比如说,全部 Integer 或全部 Float 之类的。然而,在 Rust 中,可以有一个值列表(向量),其中每个值都实现了给定的特征,但它们不一定是相同的具体类型:Vec<Box<dyn Draw>>。有没有办法在 Haskell 中做同样的事情?比如,我想要一个值列表,但我只关心每个值都属于某种类型 class 但不一定是相同的具体类型。

在Haskell中,您可以使用存在类型来表达“这种类型的一些未知类型class”。 (在旧版本的 GHC 中,您将需要一些标准扩展。)

class Draw a where
   -- whatever the methods are

data SomeDraw where
   SD :: Draw a => a -> SomeDraw

type MyList = [SomeDraw]

但是,请注意,这通常是矫枉过正,会导致 known anti-pattern

例如,如果我们有一个 class 如下:

class Draw a where
   draw :: a -> String

那么上面的类型 MyList[String] 是同构的(或者至少在道德上是这样的)。与直接存储字符串相比,存储未知的“可绘制”对象没有任何优势,该对象的唯一方法是将其转换为字符串。另请注意 Haskell 是惰性的,因此您可以“存储尚未评估的字符串”,可以这么说。

无论如何,类型classes的存在量化也可以用通用的方式定义:

import Data.Kind

-- Ex has the same role of "dyn" in Rust here
data Ex (c :: Type -> Constraint) where
    Ex :: c a => a -> Ex c

type MyList = [Ex Draw]