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

时间:2017-10-19 09:25:56

标签: haskell

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

以下作品:

Prelude> data Complex a = Num a => Complex a a

如何更改此选项以接受任何Num类型,而不仅仅是Int。

我试过以下:

* 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'

但得到了这个:

{{1}}

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

3 个答案:

答案 0 :(得分:11)

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

data Complex a = Complex !a !a

!,因为严格的字段对性能更好。)

当然,当您实施Num实例时,需要约束:

instance (Num a) => Num (Complex a) where
  fromInteger = (`Complex`0) . fromInteger
  Complex r i + Complex ρ ι = Complex (r+ρ) (i+ι)
  ...

...实际上,您需要更强大的约束RealFloat a才能实现abs,至少the standard version does it就是这样。 (这意味着Complex Int实际上无法使用,而不是标准的Num层次结构;您需要Complex Double。)

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

data Complex a where
   Complex :: Num a => a -> a -> Complex a

有了这个,你可以实现例如另外没有提到签名中的任何约束

cplxAdd :: Complex a -> Complex a -> Complex a
cplxAdd (Complex r i) (Complex ρ ι) = Complex (r+ρ) (i+ι)

当您尝试构建 Num值时,您现在需要履行Complex 。这意味着,您仍然需要Num实例中的显式约束。

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

答案 1 :(得分:3)

类型构造函数不能在纯Haskell中约束,只有函数才能。所以你应该声明

data Complex a = Complex a a

然后约束函数,比如

conjugate :: (Num a) => Complex a -> Complex a
conjugate (Complex x y) = Complex x (-y)

实际上,conjugate的类型和约束可以由编译器派生,因此您只需定义实现:

conjugate (Complex x y) = Complex x (-y)

但是,如果您真的希望约束类型构造函数Complex,则可以启用一些启用它的扩展,即ExistentialQuantificationGADTs,正如编译器所说。为此,请将此行添加到文件的最开头:

{-# LANGUAGE ExistentialQuantification #-}

{-# LANGUAGE GADTs #-}

这些被称为pragmas

答案 2 :(得分:2)

虽然您可以像编译器消息所指示的那样使用ExistentialQuantification,但您也可以像这样定义类型:

data Complex a = Complex a a deriving (Show, Eq)

它是一个完全无约束的类型,所以也许另一个名称更合适......这种类型似乎经常被称为Pair ......

但是,在编写函数时,可以约束类型中包含的值:

myFunction :: Num a => Complex a -> a
myFunction (Complex x y) = x + y