Haskell 构造类似于 Rust 特征对象

Laj*_*agy 7 haskell

Haskell 支持类型类,例如相等:

class Eq a where 
  (==)                  :: a -> a -> Bool
Run Code Online (Sandbox Code Playgroud)

Rust 对类型特征做了同样的事情:

pub trait Draw {
    fn draw(&self);
}
Run Code Online (Sandbox Code Playgroud)

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

chi*_*chi 9

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

class Draw a where
   -- whatever the methods are

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

type MyList = [SomeDraw]
Run Code Online (Sandbox Code Playgroud)

但是,请注意,这通常是矫枉过正,并导致已知的反模式

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

class Draw a where
   draw :: a -> String
Run Code Online (Sandbox Code Playgroud)

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

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

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]
Run Code Online (Sandbox Code Playgroud)

  • 另请注意,您将看到一些有关存在类型的旧文档,这些文档使用尴尬的原始语法“data SomeDraw = forall a”。画出 a =&gt; SD a` (是的,“forall”一词用于存在量词)。这个答案使用了我强烈推荐的更新且更清晰的 GADT 语法。但您应该意识到旧方法的存在。 (5认同)