Search code examples
haskelltypeclasstype-constraints

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?


Solution

  • 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)