我想创建一个复杂类型来表示复数。
以下作品:
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}}
我不确定该怎么做这个错误。任何帮助表示赞赏。
答案 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
,则可以启用一些启用它的扩展,即ExistentialQuantification
或GADTs
,正如编译器所说。为此,请将此行添加到文件的最开头:
{-# 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