WebHome
- Education Page?
- Description?
- Literature?
- Schedule?
- Assignments?

Center
Master Program

Generics Examples

Uhc
ALERT! This is work in progress ALERT! On this page I will give some examples of generic functions written in different styles. I will show the differences between the styles and show what the shortcomings of the different styles are.

Then I will try to describe a form of generic programming which combines the strong points of the different styles and is built on the class system of Haskell.

Generic equals

Derivable Type Classes

Below we see a type class Eq with an generic default method for equality. We also see an instance declaration on lists after which we can use the equality function on lists.
class Eq t where
  (==), (/=) :: t -> t -> Bool
  (==){| Unit |} Unit Unit = True
  (==){| a :+: b |} (Inl x) (Inl x') = x == x'
  (==){| a :+: b |} (Inr y) (Inr y') = y == y'
  (==){| a :+: b |} _        _       = False
  (==){| a :*: b |} (x :*: y) (x' :*: y') = x == x' && y == y'

instance Eq t => Eq [t]
instance Eq a => Eq (Set a) where
  s == s' = s `subset` s' && s' `subset` s
  
> eq [1,2,3] [1,2,3]
True

eq :: (Eq t) => t -> t -> Bool   
Some properties:
  • The function (==) can only be used on types which are instances of Eq.
  • ALERT! Generic functions can only be defined on types of kind *.
  • DONE We can override an generic definition for specific types and give our own implementation for equals (see the equality on Set).
  • ALERT! We cannot locally redefine our generic function. For instance we cannot locally redefine our equals function on characters to do an case insensitive comparision.

Classic Generic Haskell

Below we see a generic defination for equals and how we can use it in Generic Haskell. We also see how we can use the generic definition.
type Eq{[ * ]} t t'      = t -> t' -> Bool
type Eq{[ k -> l ]} t t' = forall u u'. Eq{[ k ]} u u' -> Eq{[ l ]} (t u) (t' u')

(==){| t::k |} :: Eq{[ k ]} t t
(==){| Unit |} Unit Unit = True
(==){| :+: |} eqA eqB (Inl a) (Inl a') = eqA a a'
(==){| :+: |} eqA eqB (Inr b) (Inr b') = eqB b b'
(==){| :+: |} eqA eqB  _       _       = False
(==){| :*: |} eqA eqB (a :*: b) (a' :*: b') = eqA a a' && eqB b b'   

(==){| Set a |} eqA eqB s s' = s `subset` s' && s' `subset` s
 
> eq{| [Int] |} [1,2,3] [1,2,3]  
True
> let f x y = toUpper x == toUpper y in eq{| [] |} f "uhc" "UHC"
True 

eq{| [Int] |} :: [Int] -> [Int] -> Bool
eq{| [] |}    :: (a -> b -> Bool) -> [a] -> [b] -> Bool 
Some properties we see here:
  • DONE Generic functions can be applied to all types.
  • DONE Generic functions can be defined on types of all kinds.
  • ALERT! Explicit type annotation everywere. not a very good description
  • ALERT! Explicit passing of recursive functions.
  • DONE Easy replacement of recursive functions. not a very good description
  • ALERT! For types of different kinds we have different signatures.
  • DONE We can overide generic functions for specific types.

Dependency-style Haskell

eq{| t |} :: (generalize{| δa |} a a' |-> (eq{| δa |} a -> a' -> Bool) => a -> a' -> Bool) t t 
eq{| Unit |} Unit Unit = True
eq{| δa :+: δb |} (Inl a) (Inl a') = eq{| δa |} a a'
eq{| δa :+: δb |} (Inr b) (Inr b') = eq{| δb |} b b'
eq{| δa :+: δb |} _       _        = False
eq{| δa :*: δb |} (a :*: b) (a' :*: b') = eq{| δa |} a a' && eq{| δb |} b b'

eq{| Set δa |}  s s' = s `subset` s' && s' `subset` s

> eq{| [Int] |} [1,2,3] [1,2,3]
True
> let eq{| δa |} x y = toUpper x == toUpper y in eq{| [δa] |} "uhc" "UHC"
True

eq{| [Int] |} :: [Int] -> [Int] -> Bool
eq{| [δa] |} :: forall b b'. (eq{|δa|} :: b -> b' -> Bool) => [b] -> [b'] -> Bool      
Some properties we see here:
  • DONE Generic functions can be applied to all types.
  • DONE Generic function can be defined on types of all kinds.
  • ALERT! Explicit type annotation everywere.
  • DONE No explicit passing of recursive functions.
  • DONE Easy replacement of recursive functions.
  • DONE For every use of the generic function we have the same number of arguments (but context might differ).
  • DONE We can overide generic functions for specific types.

Generic UHC

Goal: Combinate dependency-style generics and derivable type classes.
class Eq t where
  (==), (/=) :: t -> t -> Bool
  (==){| Unit |} Unit Unit = True
  (==){| a :+: b |} (Inl x) (Inl x') = x == x'
  (==){| a :+: b |} (Inr y) (Inr y') = y == y'
  (==){| a :+: b |} _        _       = False
  (==){| a :*: b |} (x :*: y) (x' :*: y') = x == x' && y == y'

instance Eq (Set a) where
  s == s' = s `subset` s' && s' `subset` s

>[1,2,3] == [1,2,3]
True
> let instance Eq δa where 
      x == x = toUpper x == toUpper y 
  in (=={|[δa]|} "uhc" "UHC"
True

Generic map

Derivable Type Classes

We can't implement a generic map using derivable type classes. more needed...

Classic Generic Haskell

type Map {[ * ]} t1 t2 = t1 -> t2
type Map {[ k -> l ]} t1 t2 = forall u1 u2. Map {[ k ]} u1 u2 -> Map {[ l ]} (t1 u1) (t2 u2)

gmap {| t :: k |} :: Map {[ k ]} t t
gmap {| Unit |} = id
gmap {| :+: |} gmapA gmapB (Inl a)     = Inl (gmapA a)
gmap {| :+: |} gmapA gmapB (Inr b)     = Inr (gmapB b)
gmap {| :*: |} gmapA gmapB (a :*: b)   = (gmapA a) :*: (gmapB b)

fmap{| t :: * -> * |} f = gmap {| t |} f 

> gmap{| String |} "hello world"
"hello world"
> gmap{| [] |} toUpper "hello world"
"HELLO WORLD"
> fmap toUpper "hello world"
"HELLO WORLD"

gmap{| String |} :: String -> String
gmap{| [] |}::(a -> b) -> [a] -> [b]

Dependency-style Generic Haskell

gmap{| t |} :: (generalize{| δa |} b b' |-> (gmap{| δa |} :: (b -> b')) =>  b -> b') t t 
gmap{| Unit |} = Unit
gmap{| δa :+: δb |} (Inl a) = (Inl gmap{| δa |} a)
gmap{| δa :+: δb |} (Inr a) = (Inr gmap{| δb |} b)
gmap{| δa :*: δb |} (a :*: b) = gmap{| δa |} a :*: gmap{| δb |} b

fmap{| t:: * -> * |} f x = let gmap{| δa |} = f in gmap {| t δa |} x 

> gmap{| String |} "hello world"   
"hello world"
> let gmap{| δa |} = toUpper in gmap{| [δa] |} "hello world"
"HELLO WORLD"
> fmap toUpper "Hello world"
"HELLO WORLD"

gmap{| String |} :: String -> String
gmap{| [δa] |} :: (map{| δa |} :: b -> c) => [b] -> [c]    

Generic UHC

class GMap t where 
   gmap :: t -> t 
   gmap{| Unit |} Unit = Unit
   gmap{| a :+: b |} (Inl a) = Inl (gmap a)
   gmap{| a :+: b |} (Inr b) = Inr (gmap b)
   gmap(| a :*: b |} (a :*: b) = gmap a :*: gmap b

> let instance GMap δa where
     gmap = toUpper
  in gmap{| [δa] |} "hello world"
"HELLO WORLD"

-- ArjanOosting - 06 Oct 2003