Dan Burton
Dan Burton

Reputation: 53715

Typeclass constraint of different kind

I have been fiddling with general type classes for lists in Haskell.

class HasEmpty a where
  empty :: a
  isEmpty :: a -> Bool

class HasEmpty (l a) => List l where
  cons :: a -> l a -> l a
  uncons :: l a -> (a, l a)

To give you an idea of how it might work, here are instances for []:

instance HasEmpty [a] where
  empty = []
  isEmpty [] = True
  isEmpty _  = False

instance List [] where
  cons = (:)
  uncons (x:xs) = (x,xs)

However, this raises an error:

Not in scope: type variable 'a'

This is caused by the constraint HasEmpty (l a). I am not desperately interested in this particular example, but I am interested in the concept in general. HasEmpty is a class for types of kind *, while List is a class for types of kind * -> *. Is it possible for me to make a typeclass constraint of a different kind than the typeclass it is constraining?

Upvotes: 4

Views: 375

Answers (2)

Ed'ka
Ed'ka

Reputation: 7158

In any case you can always express underlying logic either using multiparameter typeclasses (as in fact it is done in ListLike):

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}

class HasEmpty a where
  empty :: a
  isEmpty :: a -> Bool

class HasEmpty (l a) => List l a where
  cons :: a -> l a -> l a
  uncons :: l a -> (a, l a)


instance HasEmpty [a] where
  empty = []
  isEmpty [] = True
  isEmpty _  = False

instance List [] a where
  cons = (:)
  uncons (x:xs) = (x,xs)

Or more elegantly via type families:

{-# LANGUAGE TypeFamilies #-}

class HasEmpty a where
  empty :: a
  isEmpty :: a -> Bool


class HasEmpty a => List a where
  type Elem a :: *
  cons :: Elem a -> a -> a
  uncons :: a -> (Elem a, a)


instance HasEmpty [a] where
  empty = []
  isEmpty [] = True
  isEmpty _  = False


instance List [a] where
  type Elem [a] = a
  cons = (:)
  uncons (x:xs) = (x,xs)

Upvotes: 3

Alexey Romanov
Alexey Romanov

Reputation: 170899

Of course you can. E.g. this will work fine for the same two classes:

class HasEmpty (l ()) => List l where
  cons :: a -> l a -> l a
  uncons :: l a -> (a, l a)

or (where List1 :: (* -> *) -> * -> *)

class HasEmpty1 (l a) => List1 l a where
  cons :: a -> l a -> l a
  uncons :: l a -> (a, l a)

What you can't do is add new variables in constraints.

Upvotes: 2

Related Questions