Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specifying class constraints in value constructors

Is there a way to define a class constraint for a value constructor's parameter?

Something like this:

data Point2D = (Num a) => Point a a

so that Point can take any arguments as long as they belong to the Num class?

like image 408
Nickolay Kolev Avatar asked Jan 26 '11 21:01

Nickolay Kolev


2 Answers

You can use ExistentialQuantification or GADTs, but neither is going to do what you want. You'll never be able to do arithmetic with two Point2D values. All you know about the contents is that they're some instance of Num. You're telling the compiler to discard all other information about them. This means you're telling the compiler to discard any information it may have that a particular pair of Point2D values contain the same type. And without that information, you won't be able to do any arithmetic on values from two Point2Ds together.

This almost certainly isn't what you want. You can't write a distance function, for instance. What possible use could you have for such a limited type? About all you can do with them is convert their contents to a String.

Edit:

I think I see what you're trying to do. You just want to ensure that everything in a Point2D is a number. I don't think you actually want type erasure.

In that case, I'd go with the GADT version, with one really important change:

{-# LANGUAGE GADTs #-}
data Point2D a where
    Point :: (Num a) => a -> a -> Point2D a

The end result of this is that you can only use the Point constructor with two values of the same instance of Num, but you don't lose what the type was. Furthermore, thanks to using GADTs, pattern-matching on the Point constructor recovers the Num context for you, which is basically what you'd expect.

But I think the most important thing here is not throwing away the type of the contents. Doing so makes the type basically impossible to work with.

like image 61
Carl Avatar answered Oct 10 '22 01:10

Carl


Yes, but you'll have to realize that the meaning of your constraint is different from usual generic types.

Usually, generics like in type Foo a = (a, a) mean

for all types a, Foo a consists of two a's

However, in your example, one needs to phrase that differently:

For some type a, Point2D consists of two a's

or

There is a type a that Point2D consists of

Thus, the generic type is not universal (for all types...), but existential (it exists some type...). Under GHC, we can allow this through the extenstion

{-# ExistentialQuantification #-}

as described in this article on the topic. Your code, after all, is

data Point2D = forall a . Num a => Point a a
like image 41
Dario Avatar answered Oct 09 '22 23:10

Dario