Question

I want to define a new abstract data type which is either a general Number or a Division construct. How would I do that in Haskell?

My first approach was:

data MyMath = MyNum Num
            | Div MyMath MyMath

The problem is that the compiler complains about "Num" which is not a data type but a type class. So my second thought would be to solve the problem like this:

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

But this would not work either as MyNum is used twice which is not allowed, additionaly this approach would not really be polymorphic. So what is the solution to this problem?

EDIT2: After (again) reading the answers I tried to use GADT data constructors. This is some artificial example code:

 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

But something is wrong with line number 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)

The same goes for `c'. I guess it's a stupid mistake which I just don't see. Do you?

Was it helpful?

Solution

This solves the problem you're addressing in the code, but doesn't cover for the boolean. If you want to use a class constraint in a data declaration, you do it the way that you would with any other function:

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

OTHER TIPS

You can use existential quantification for that:

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

There are many ways to do it. One way is with GADTs:

{-# LANGUAGE GADTs #-}

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

Another way with GADTs:

{-# LANGUAGE GADTs #-}

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

As has been mentioned, your attempts don't involve any booleans, but I'll go with the text of your question instead.

You don't have to invent this type, check out Either in the Prelude. What you're looking for is thus Either a Bool, where you want a to be an instance of Num. If you want to actually enforce this, ready the edit below.

Edit: If you don't want to use Either, you can do data MyMath a = MyNum a | MyBool Bool. Now you can enforce a being an instance of Num if you want to, but you may want to consider this SO question and this answer to it first. There's really no need to enforce the instance for the data type; just do it for the functions using it intsead.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top