3

我想定义一个新的抽象数据类型,它可以是通用数字或除法构造。我将如何在 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

4 回答 4

3

这解决了您在代码中解决的问题,但不包括布尔值。如果您想在数据声明中使用类约束,您可以像使用任何其他函数一样进行操作:

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

于 2012-05-11T14:49:33.597 回答
3

您可以为此使用存在量化:

> let data MyMath = forall n. Num n => MyNum n
> :t MyNum 3
MyNum 3 :: MyMath
> :t MyNum 3.5
MyNum 3.5 :: MyMath
于 2012-05-11T14:50:21.060 回答
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
于 2012-05-11T17:28:55.697 回答
0

如前所述,您的尝试不涉及任何布尔值,但我将使用您的问题文本。

您不必发明这种类型,请查看Prelude 中的EitherEither a Bool因此,您正在寻找的是您想要a成为Num. 如果您想实际执行此操作,请准备好下面的编辑。

编辑:如果你不想使用Either,你可以做data MyMath a = MyNum a | MyBool Bool。现在,如果您愿意,您可以强制a成为一个实例,但您可能需要先考虑这个 SO question这个答案。确实没有必要为数据类型强制执行实例;只需为使用它的功能做它intsead。Num

于 2012-05-11T14:44:18.957 回答