Haskell - Creating constraints and applying to datatypes

Viewed 68

I'm trying out the creation of class constraints and applying to new data types, but I could not get this code to type check. Could someone help? Where did I go wrong?

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
class NewConstraint a where
  getTrue :: a -> Bool
newtype Identity a = Identity a
  deriving (Eq, Show, Num)
instance (Num a, NewConstraint a) =>
  NewConstraint (Identity a) where
    getTrue x = True
test :: Bool
test = getTrue (Identity 1)
1 Answers

There are two problems here: missing instance constraints and polymorphic numeric literals.


First up: missing instance constraints.

When you write code like this:

instance NewConstraint a => NewConstraint (Identity a) where

It means "every type of the shape Identity a, for every possible a, has an instance of NewConstraint; but the a in question must also have an instance of NewConstraint"

So when you write getTrue (Identity 1), the compiler looks for an instance of NewConstraint and finds one for Identity a. But what is a? Well, it's an integer number, so let's say it's Int.

But your instance declaration says that a must have an instance of NewConstraint. So the compiler now looks for an instance of NewConstraint Int. And doesn't find it. So it gives you an error.

To fix this, either add an instance NewConstraint Int, like this:

instance NewConstraint Int where getTrue _ = True

Or remove the NewConstraint constraint from your Identity a instance, like this:

instance Num a => NewConstraint (Identity a) where

Second: polymorphic numeric literals.

In Haskell, numeric literals like 1, 5, or 42 don't have a concrete type. They have type Num a => a, which means "any type a, as long as it has an instance of Num"

So when you write 1, what type is it? Could be Int (like I suggested above), could be Double, could be Complex. The compiler has no way to know what you meant.

Normally there is some other source from which to infer the type. Perhaps the numeric literal is passed to some function as a parameter, and that function expects an Int. Well, then the compiler could tell that you meant Int.

But in your case there is no such source. So the compiler doesn't know.

So to help the compiler out, you must specify the type, like this:

test = getTrue (Identity (1 :: Int))

Finally note that if you remove the NewConstraint a constraint from your instance NewConstraint (Identity a), then you don't have to specify the type of 1 :: Int.

Yes, the compiler still won't know the type of 1, but it won't care. It doesn't need to find an instance of NewConstraint a, so doesn't need to know what a is.

Related