如何在haskell中创建通用复杂类型?

ksc*_*ath 7 haskell

我想创建一个复杂类型来表示复数.

以下作品:

Prelude> data Complex = Complex Int Int

Prelude> :t Complex
Complex :: Int -> Int -> Complex
Run Code Online (Sandbox Code Playgroud)

如何更改此接受任何Num类型,而不仅仅是Int.

我试过以下:

Prelude> data Complex a = Num a => Complex a a
Run Code Online (Sandbox Code Playgroud)

但得到了这个:

* Data constructor `Complex' has existential type variables, a context, or a specialised result type
    Complex :: forall a. Num a => a -> a -> Complex a
    (Use ExistentialQuantification or GADTs to allow this)
* In the definition of data constructor `Complex'
  In the data type declaration for `Complex'
Run Code Online (Sandbox Code Playgroud)

我不确定该怎么做这个错误.任何帮助表示赞赏.

lef*_*out 11

传统data的Haskell就是:数据.它不需要知道关于其字段属性的任何信息,只需要能够存储它们.因此,没有必要在那时限制字段; 只是做到了

data Complex a = Complex !a !a
Run Code Online (Sandbox Code Playgroud)

(!因为严格的字段对性能更好).

当然,当您实现Num实例时,您需要一个约束:

instance (Num a) => Num (Complex a) where
  fromInteger = (`Complex`0) . fromInteger
  Complex r i + Complex ? ? = Complex (r+?) (i+?)
  ...
Run Code Online (Sandbox Code Playgroud)

......事实上,你需要更强大的约束RealFloat a来实现abs,至少标准版本是如何实现.(这意味着,Complex Int实际上是不可用的,不是标准的Num层次结构;你需要例如Complex Double.)

也就是说,也可以将约束烘焙到数据类型本身.您尝试过的ExistentialTypes语法虽然非常有限,但并不适用于此; 你想要的是GADT

data Complex a where
   Complex :: Num a => a -> a -> Complex a
Run Code Online (Sandbox Code Playgroud)

有了这个,您就可以实现例如添加而不提及签名中的任何约束

cplxAdd :: Complex a -> Complex a -> Complex a
cplxAdd (Complex r i) (Complex ? ?) = Complex (r+?) (i+?)
Run Code Online (Sandbox Code Playgroud)

现在,您将需要满足Num ,只要你试图建立一个Complex虽然值.这意味着,您仍然需要在Num实例中使用显式约束.

此外,此版本可能要慢得多,因为Num字典实际上需要存储在运行时表示中.