如何定义像“data MyMath = MyNum Num”这样的抽象数据类型?

时间:2012-05-11 14:39:28

标签: class haskell types abstract-data-type

我想定义一个新的抽象数据类型,它是一般的Number或Division构造。我如何在Haskell中做到这一点?

我的第一个方法是:

data MyMath = MyNum Num
            | Div MyMath MyMath

问题是编译器抱怨“Num”不是数据类型而是类型类。所以我的第二个想法是解决这个问题:

data MyMath = MyNum Int
            | MyNum Float
            | Div MyMath MyMath

但是这不会起作用,因为MyNum被使用了两次是不允许的,另外这种方法实际上不是多态的。那么这个问题的解决方案是什么?

EDIT2 :在(再次)阅读答案后,我尝试使用GADT数据构造函数。这是一些人为的示例代码:

 5 data MyMathExpr a where
 6               MyNumExpr :: Num a => a -> MyMathExpr a
 7               MyAddExpr :: MyMathExpr b -> MyMathExpr c -> MyMathExpr (b, c)
 8 deriving instance Show(MyMathExpr a)
 9 deriving instance Eq(MyMathExpr a)
10 
11 data MyMathVal a where 
12                 MyMathVal :: Num a => a -> MyMathVal a
13 deriving instance Show(MyMathVal a)
14 deriving instance Eq(MyMathVal a)
15 
16 foo :: MyMathExpr a -> MyMathVal a
17 foo (MyNumExpr num) = MyMathVal num
18 foo (MyAddExpr num1 num2) = MyMathVal (l + r)
19   where (MyMathVal l) = foo num1
20         (MyMathVal r) = foo num2

但第18行出了点问题:

test.hs:18:40:
Couldn't match type `b' with `(b, c)'
  `b' is a rigid type variable bound by
      a pattern with constructor
        MyAddExpr :: forall b c.
                     MyMathExpr b -> MyMathExpr c -> MyMathExpr (b, c),
      in an equation for `foo'
      at test.hs:18:6
In the first argument of `(+)', namely `l'
In the first argument of `MyMathVal', namely `(l + r)'
In the expression: MyMathVal (l + r)

“c”也是如此。我想这是一个愚蠢的错误,我只是看不到。你呢?

4 个答案:

答案 0 :(得分:3)

这解决了您在代码中解决的问题,但没有涵盖布尔值。 如果你想在数据声明中使用类约束,你可以像使用任何其他函数一样:

data (Num a) => MyMath a = MyMath {x :: a}

答案 1 :(得分:3)

您可以使用存在量化:

> let data MyMath = forall n. Num n => MyNum n
> :t MyNum 3
MyNum 3 :: MyMath
> :t MyNum 3.5
MyNum 3.5 :: MyMath

答案 2 :(得分:1)

有很多方法可以做到这一点。一种方法是使用GADT:

{-# LANGUAGE GADTs #-}

data MyMath where
    MyNum :: Num a => a -> MyMath
    MyBool :: Bool -> MyMath

GADT的另一种方式:

{-# LANGUAGE GADTs #-}

data MyMath a where
    MyNum :: Num a => a -> MyMath a
    MyBool :: Num a => Bool -> MyMath a

答案 3 :(得分:0)

正如已经提到的,你的尝试不涉及任何布尔,但我会改为你的问题文本。

您不必创建此类型,请在Prelude中查看Either。因此,您所需要的是Either a Bool,您希望a成为Num的实例。如果您想要实际执行此操作,请准备好以下编辑。

修改:如果您不想使用Either,则可以执行data MyMath a = MyNum a | MyBool Bool。现在,如果您愿意,可以强制a成为Num的实例,但您可能需要先考虑this SO questionthis answer to it。实际上没有必要为数据类型强制实例;只是为使用它的函数做到这一点。