在Haskell中派生Data.Complex

时间:2014-01-26 04:48:39

标签: haskell deriving

我的代码看起来有点像以下内容:

import Data.Complex

data Foo = N Number
         | C ComplexNum

data Number = Int Integer
            | Real Float
            | Rational Rational
     deriving Show

data ComplexNum = con1 (Complex Integer)
                | con2 (Complex Float)
                | con3 (Complex Rational)
     deriving Show

但这似乎是一种糟糕的方式。我宁愿拥有

data Foo = N Number
         | C (Complex Number)

并构造一个类似于ComplexNumber $ Real 0.0的ComplexNumber。

问题是如何使Complex Number成为可能。由于Number中的所有类型都有相应的Complex个实例,我可以将deriving Complex添加到Number吗?

2 个答案:

答案 0 :(得分:3)

Haskell方法是为Complex FloatComplex Int设置不同的类型,而不是尝试将它们统一为一种类型。使用类型类,您可以一次定义所有这些类型:

data Complex a = C a a

instance Num a => Num (Complex a) where
  (C x y) + (C u v) = C (x+u) (y+v)
  (C x y) * (C u v) = C (x*u-y*v) (x*v+y*u)
  fromInteger n = C (fromInteger n) 0
  ...

这会立即定义Complex IntComplex DoubleComplex Rational等。事实上它甚至定义了Complex (Complex Int)

请注意,这并未定义如何将Complex Int添加到Complex Double。加法(+)仍然具有(+) :: a -> a -> a类型,因此您只能将Complex Int添加到Complex Int,将Complex Double添加到另一个Complex Double

为了添加不同类型的数量,您必须明确地转换它们,例如:

addIntToComplex :: Int -> Complex Double -> Complex Double
addIntToComplex n z = z + fromIntegral n

查看http://www.haskell.org/tutorial/numbers.html第10.3节,了解Haskell数值类型类之间更有用的转换函数。

<强>更新

在回应您的评论时,我建议您更多地关注操作,而不是关注类型。

例如,请考虑以下定义:

onethird = 1 / 3

这表示所有数字类中的通用“1/3”值:

import Data.Ratio

main = do
    putStrLn $ "as a Double: " ++ show (onethird :: Double)
    putStrLn $ "as a Complex Double: " ++ show (onethird :: Complex Double)
    putStrLn $ "as a Ratio Int: " ++ show (onethird :: Ratio Int)
    putStrLn $ "as a Complex (Ratio Int): " ++ show (onethird :: Complex (Ratio Int))
    ...

从某种意义上说,Haskell让“用户”决定表达式应该被评估为什么数字类型。

答案 1 :(得分:0)

这似乎不是合法的Haskell代码。您有三个ComplexNum类型的构造函数,都是Complex。此外,数据类型必须以大写字母开头,因此foo不是有效类型。很难说出你的意思,但我会采取刺:

如果您有类型

data Complex a = (a,a)

您可以保留Number的定义并将foo定义为:

data Foo = N Number
         | C (Complex Number)