{-# LANGUAGE PatternSignatures, EmptyDataDecls, KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverlappingInstances #-}
-- The overlapping instances are needed exclusively for the implementation
-- of TypeEq.

-- Smash your boilerplate IVA, without class and without Typeable
-- and with extensible traversal strategies

-- This file defines the SYB4A core and several typical traversal
-- strategies: 
--   - reconstruction, used for generalized gmap;
--   - reduction, aka generalized gfold, used for computing the size or 
--     the depth of a term, for collecting in a list all values of a specified
--     type occurring in a term or for showing the term; and 
--   - two-terms-in-a-lockstep reduction (used for generic equality). 
-- The user may add more classes of traversal at any time 
-- (see Syb4ABuild.hs for an example).
-- We give some examples, e.g., replacing all Ints in a term 
-- with Floats. The type of the resulting term is the function of the type
-- of the input term and the gmapping function; that result type is 
-- _computed_ and need not be specified.
-- See other files in this directory for more examples.

module Syb4A (
	     -- core
	     LDat(..),
	     STApply(..),
	     HNil(..),
	     HCons(..),
	     Apply(..),

	     -- general generic applicator
	     GAPP(..),
	     -- sample traversal strategies
	     TL_recon(..),
	     TL_reconM(..),
	     TL_red(..),
	     TL_red_ctr(..),
	     TL_red_shallow(..),

	     TL_red_lockstep(..),
             Couple(..),
             eq_clauses,
             geq

	    ) where

-- ============= 
-- The library core: Applying exceptional rules
--
--	stapply :: spec -> a -> d -> w
-- Given a heterogeneous list |spec| of functions |ai->wi| (where 
-- |ai| and |wi| vary from one function to another in the list) 
-- and a datum of type |a|, check to see if there is a function in that
-- list whose argument type |ai| is equal to |a|. If so, apply that 
-- function to the datum, and return its result of the type |wi|. 
-- If no such function is found, return the supplied default value, 
-- of the type |d|. Thus the result type |w| of stapply is a function
-- of the types |spec|, |a|, and |d|.
-- The core procedure above is essentially overloading resolution,
-- but stated without the use of typeclasses.
-- Actually, spec is a list of functions |ai->d->wi|; each of these functions
-- also receives the default value and so it knows the overall result if
-- none of the functions in spec apply. A spec function may 
-- examine this default value and even return it, perhaps after some
-- modifications. Thus a choice of a defualt value can be made not only
-- statically but also dynamicaaly, based on the actual values 
-- of 'ai' and 'd' rather than merely on their types.
-- NB! Althougfh it is often the case that 'd' and 'wi' have the same
-- type, it is not always the case. The example of the latter is
-- a mapping that changes the type of the result, e.g., replacing all
-- booleans with characters. Then in an application
--   stapply ((\(b::Bool) d -> 'a') :+: HNil) boolean
-- ai=Bool, d=Bool but wi and hence w is Char.

class STApply spec a d w | spec a d -> w where
    stapply :: spec -> a -> d -> w

instance STApply HNil a d d where
    stapply _ a deflt  = deflt

instance (TypeEq a a' bf, STApply' bf (HCons (a'->d'->w') r) a d w)
    => STApply (HCons (a'->d'->w') r) a d w where
    stapply = stapply' (undefined::bf)

class STApply' bf spec a d w | bf spec a d -> w where
    stapply' :: bf -> spec -> a -> d -> w

instance TypeCast d d' => STApply' HTrue (HCons (a->d'->w) r) a d w where
    stapply' _ (p :+: _) x d = p x (typeCast d)

instance STApply r a d w => STApply' HFalse (HCons p' r) a d w where
    stapply' _ (_ :+: r) x deflt = stapply r x deflt

{-
instance (TypeEq2 a a' bf, SApply'' bf (SCons2 a' l r) a)
    => SApply (SCons2 a' l r) a where
    sapply = sapply'' (undefined::bf)

class SApply'' bf spec a where
    sapply'' :: bf -> spec w -> a -> w -> w

instance ApplyW l a => SApply'' HTrue (SCons2 a l r) a where
    sapply'' _ (SCons2 _ p _) x _ = applyW p x

instance SApply r a => SApply'' HFalse (SCons2 a' l r) a where
    sapply'' _ (SCons2 _ _ r) x deflt = sapply r x deflt
-}


-- ============= 
-- This part of the code is the SYB library
-- It is independent of any generic functions. The instances therein
-- depend on the data types only (and, can hopefully, can be automatically
-- derived for new data types)

-- First, we define the function |gapp| that applies a generic function
-- to a term. A generic function is (quite literally) made of two parts.
-- First, there is a term traversal strategy, identified by a _label_
-- |tlab|. One strategy may be to `reduce' a term using a supplied
-- reducing function (cf. fold over a tree). Another strategy may be to
-- rebuild a term. The second component of a generic function is |spec|,
-- the list of `exceptions'. Each component of |spec| is a function that
-- tells how to transform a term of a specific type. Exceptions override
-- the generic traversal.

-- The function |gapp| is defined generically. The code below says:
-- first, check to see if any of the exceptions apply. If not, do the
-- generic traversal.
class (STApply spec a df w, LDat tlab spec a df)
    => GAPP tlab spec a df w | tlab spec a -> df w where
    gapp :: tlab -> spec -> a -> w

instance (STApply spec a df w, LDat tlab spec a df)
    => GAPP tlab spec a df w where
    gapp tlab spec x = stapply spec x (gin tlab spec x)

-- The function |gin| does a generic traversal. It (may) invoke |gapp|
-- on the children of the term, if any (and if the traversal strategy
-- calls for traversing the children).
-- This class is similar to the class Data of SYB.
-- However, different traversal strategies of Data are methods of this class.
-- Here, different strategies are identified by the label |tlab|
-- and so the set of strategies is _extensible_.
class LDat tlab spec x w | tlab spec x -> w where
    gin :: tlab -> spec -> x -> w


-- We define labels for some popular traversal strategies

		-- Reconstruct the term: used in generic mapping
data TL_recon = TL_recon

		-- Reduce the term: used in folding over a term
newtype TL_red w = TL_red ([w]->w)

		-- The same as above but the reducer accepts the name
		-- of the term's constructor, a string
		-- This is used for generic show
newtype TL_red_ctr w = TL_red_ctr (String -> [w]->w)


-- ============= 
-- In this part of the code, we define the instances of LDat for some
-- popular data types. The user may define more instances for their
-- own data types.

-- Primitive, atomic data types

instance LDat TL_recon spec Int Int where
    gin _ spec x = x

instance LDat (TL_red w) spec Int w where
    gin (TL_red f) spec x = f []

instance LDat (TL_red_ctr w) spec Int w where
    gin (TL_red_ctr f) spec x = f "Int" []


instance LDat TL_recon spec Char Char where
    gin _ spec x = x
instance LDat (TL_red w) spec Char w where
    gin (TL_red f) spec x = f []
instance LDat (TL_red_ctr w) spec Char w where
    gin (TL_red_ctr f) spec x = f "Char" []


instance LDat TL_recon spec Bool Bool where
    gin _ spec x = x
instance LDat (TL_red w) spec Bool w where
    gin (TL_red f) spec x = f []
instance LDat (TL_red_ctr w) spec Bool w where
    gin (TL_red_ctr f) spec x = f "Bool" []


-- product
instance (GAPP TL_recon spec a dfa wa, GAPP TL_recon spec b dfb wb)
    => LDat TL_recon spec (a,b) (wa,wb) where
    gin tlab spec (x,y) = (gapp tlab spec x, gapp tlab spec y)

instance (GAPP (TL_red w) spec a w w, GAPP (TL_red w) spec b w w)
    => LDat (TL_red w) spec (a,b) w where
    gin tlab@(TL_red f) spec (x,y) = f [gapp tlab spec x, gapp tlab spec y]


instance (GAPP (TL_red_ctr w) spec a w w, GAPP (TL_red_ctr w) spec b w w)
    => LDat (TL_red_ctr w) spec (a,b) w where
    gin tlab@(TL_red_ctr f) spec (x,y) = 
	f "(,)" [gapp tlab spec x, gapp tlab spec y]


-- (semi-)sums
instance (GAPP TL_recon spec a dfa w)
    => LDat TL_recon spec (Maybe a) (Maybe w) where
    gin _    spec Nothing  = Nothing
    gin tlab spec (Just x) = Just (gapp tlab spec x)


instance (GAPP (TL_red w) spec a w w)
    => LDat (TL_red w) spec (Maybe a) w where
    gin (TL_red f)      spec Nothing  = f []
    gin tlab@(TL_red f) spec (Just x) = f [gapp tlab spec x]

instance (GAPP (TL_red_ctr w) spec a w w)
    => LDat (TL_red_ctr w) spec (Maybe a) w where
    gin (TL_red_ctr f)      spec Nothing  = f "Nothing" []
    gin tlab@(TL_red_ctr f) spec (Just x) = f "Just" [gapp tlab spec x]

-- Lists
{-
-- The following is the simplified/optimized definition
instance (STApply spec a df w,
	  LDat TL_recon spec a df)
    => LDat TL_recon spec [a] [w] where
    gin tlab spec xs = map (gapp tlab spec) xs

instance (STApply spec a w w,
	  LDat (TL_red w) spec a w)
    => LDat (TL_red w) spec [a] w where
    gin tlab@(TL_red f) spec xs = f . map (gapp tlab spec) $ xs
-}

-- The following definition is in the spirit of SYB. Cf. Maybe
instance (GAPP TL_recon spec [a] [w] [w], GAPP TL_recon spec a dfa w)
    => LDat TL_recon spec [a] [w] where
    gin tlab spec []     = []
    gin tlab spec (x:xs) = (gapp tlab spec x):(gapp tlab spec xs)

instance (GAPP (TL_red w) spec [a] w w, GAPP (TL_red w) spec a w w)
    => LDat (TL_red w) spec [a] w where
    gin tlab@(TL_red f) spec []     = f []
    gin tlab@(TL_red f) spec (x:xs) = f [gapp tlab spec x, gapp tlab spec xs]


instance (GAPP (TL_red_ctr w) spec [a] w w, GAPP (TL_red_ctr w) spec a w w)
    => LDat (TL_red_ctr w) spec [a] w where
    gin tlab@(TL_red_ctr f) spec []     = f "[]" []
    gin tlab@(TL_red_ctr f) spec (x:xs) = f ":" [gapp tlab spec x, 
						 gapp tlab spec xs]

-- Full sums (co-products)
instance (GAPP TL_recon spec a dfa wa, GAPP TL_recon spec b dfb wb)
    => LDat TL_recon spec (Either a b) (Either wa wb) where
    gin tlab spec (Left  x) = Left  (gapp tlab spec x)
    gin tlab spec (Right y) = Right (gapp tlab spec y)

instance (GAPP (TL_red w) spec a w w, GAPP (TL_red w) spec b w w)
    => LDat (TL_red w) spec (Either a b) w where
    gin tlab@(TL_red f) spec (Left x)  = f [gapp tlab spec x]
    gin tlab@(TL_red f) spec (Right y) = f [gapp tlab spec y]

instance (GAPP (TL_red_ctr w) spec a w w, GAPP (TL_red_ctr w) spec b w w)
    => LDat (TL_red_ctr w) spec (Either a b) w where
    gin tlab@(TL_red_ctr f) spec (Left x)  = f "Left"  [gapp tlab spec x]
    gin tlab@(TL_red_ctr f) spec (Right y) = f "Right" [gapp tlab spec y]


-- Functions

instance (GAPP TL_recon spec b dfb wb)
    => LDat TL_recon spec (a->b) (a->wb) where
    gin tlab spec f = \a -> gapp tlab spec (f a)



-- ============= 
-- This part of the code is gsize client code. See also tests in this dir

-- First, the fully generic gsize functions: it counts the data constructors
-- in a complex term

gsize a = gapp (TL_red (\l -> 1 + sum l)) HNil  a


test1 = gsize (1::Int) -- 1

test2 = gsize [1::Int,2,3] -- 7 = 3 integers + 3 (:) plus one []

test3 = gsize "abc" -- 7, as above, with Char instead of Int

test4 = gsize ["abc"] -- 9: one extra (:) and one extra []

-- We now override the generic size processing for some specific
-- data type: string. We assign each String the fixed size 999.

gsize' a = gapp (TL_red (succ . sum)) 
	         ((\ (_::String) _ -> (999::Int)) :+: HNil)  a

test1' = gsize' (1::Int)

test2' = gsize' [1::Int,2,3] -- 7

test3' = gsize' "abc" -- 999

test4' = gsize' ["abc"] -- 1001


-- 999, 1, 1007
tests' = (gsize' ['a','b'],gsize' 'a', gsize' ([("a",True)],[1::Int]))

-- ============= 
-- Let us define a new generic function, to test if a given data structure
-- contains the letter 'a' (or an integer with the 'a' code) somewhere

hasa a = gapp (TL_red or) 
	 ((\ x _ -> x == 'a') :+: (\x _ -> x == (fromEnum 'a')) :+: HNil) a
testh = (hasa ('x',False), 
	 hasa ('x',97::Int), hasa 'a', hasa [[["cde"],["abc"]]])

-- (False,True,True,True)


-- ============= 
-- Using the generic term replacement

term1 = ([1::Int,2], (True,('2',[(3::Int,4::Int)])))


-- we can define generic gmap in one line:
-- It takes a function f::a->b and a term and returns a term with
-- all values of type a replaced with the corresponding values of
-- type b. The type of the result is computed. When the input term
-- is a list, gmap is the ordinary map.
gmap f = gapp TL_recon ((\x _ -> f x) :+: HNil) 

-- Traverse a term and increment all integers. 
-- This is similar to Ralf's example of raising the salary.

testi1 = gmap inci term1 where inci (x::Int) = succ x
-- ([2,3],(True,('2',[(4,5)])))

termc = (["ab"],(Just 'c', Just 'd'))
-- Replace all Chars with their Int equivalents
testc1 = gmap ord termc where ord (c::Char) = fromEnum c
-- Only replace those Chars that occur in Maybe Char
testc2 = gmap maybeord termc 
    where maybeord (Just (c::Char)) = Right (fromEnum c)
	  maybeord Nothing = Left 0
-- Fuse both testc1 and testc2 traversals
testc3 = gapp TL_recon (ord :+: maybeord :+: HNil) termc
    where maybeord (Just (c::Char)) _ = Right (fromEnum c)
	  maybeord Nothing _ = Left 0
	  ord (c::Char) _ = fromEnum c

-- replace all tuples (x,y) with Int elements with an array [x,y], and
-- negate all booleans
p2l a = gapp TL_recon ((\ (x::Int,y) _ -> [x,y]) :+: (const . not) :+: HNil) a

test_p2l = p2l term1
-- ([1,2],(False,('2',[[3,4]])))

-- replace an Int with a Double everywhere
i2d a = gmap (fromIntegral::Int->Double) a
test_i2d = i2d term1
-- ([1.0,2.0],(True,('2',[(3.0,4.0)])))

-- Replacing functions and under the functions
term2 = ([not], (True,('2',[(&&),(||)])))

-- traverse the term and replace Bool->Bool->Bool with
-- Int->Int->Int

testt2 = gmap (\ (f::Bool->Bool->Bool) -> ((+)::Int->Int->Int)) term2

-- *Syb4> :t testt2
-- testt2 :: ([Bool -> Bool], (Bool, (Char, [Int -> Int -> Int])))

-- traverse the term and replace Bool with a Char. Do the traversal
-- under the lambda!
testt3 = gmap (\x -> if x then 'a' else 'b') term2

-- *Syb4> :t testt3
-- testt3 :: ([Bool -> Char], (Char, (Char, [Bool -> Bool -> Char])))

testtt31 = let ([f],_) = testt3 in f True
-- 'b'


{-
The following code emulates a type class

class C a where fn :: a -> Int
instance C Bool where fn x = if x then 10 else 20
instance C Char where fn x = fromEnum x

-}

data ResFailure -- an abstract data type with no non-bottom values
                -- and no defined operations. An attempt to actually
                -- use it will trigger a type error

testtyc_fn x = 
    stapply ((\ (x::Bool) _ -> if x then (10::Int) else 20) :+:
             (\ (x::Char) _ -> fromEnum x) :+:
	     HNil) 
            x
	    (undefined::ResFailure)

testtyc_fn1 = testtyc_fn True
testtyc_fn2 = testtyc_fn 'x'
-- testtyc_fn3 = show $ testtyc_fn () -- type error, can't show ResFailure


-- Generic Equality
-- We define a traversal strategy that traverses and reduces
-- two terms in lock-step
-- That is, we traverse the term (Couple x y) where x and y
-- have the same type, and reduce the result using the supplied
-- function 'f' 

data Couple a = Couple a a

--  TL_red_lockstep d f
-- Here, f is a reduction function. It reduces the result of traversing
-- the corresponding kids of the two terms (see the instance of LDat for
-- pairs for illustration). The value 'd' is returned when the two
-- terms in question are of the sum type and have different branches
-- of that sum (like (Couple Nothing (Just x))).
data TL_red_lockstep w = TL_red_lockstep w ([w] -> w)


instance LDat (TL_red_lockstep w) spec (Couple Int) w where
    gin (TL_red_lockstep _ f) spec (Couple x y) = f []

instance LDat (TL_red_lockstep w) spec (Couple Char) w where
    gin (TL_red_lockstep _ f) spec (Couple x y) = f []

instance LDat (TL_red_lockstep w) spec (Couple Bool) w where
    gin (TL_red_lockstep _ f) spec (Couple x y) = f []

instance (GAPP (TL_red_lockstep w) spec (Couple a) w w, 
	  GAPP (TL_red_lockstep w) spec (Couple b) w w)
    => LDat (TL_red_lockstep w) spec (Couple (a,b)) w where
    gin tlab@(TL_red_lockstep _ f) spec (Couple (x1,y1) (x2,y2)) = 
	f [gapp tlab spec (Couple x1 x2), 
	   gapp tlab spec (Couple y1 y2)]


-- (semi-)sums
instance (GAPP (TL_red_lockstep w) spec (Couple a) w w)
    => LDat (TL_red_lockstep w) spec (Couple (Maybe a)) w where
    gin (TL_red_lockstep _ f)      spec (Couple Nothing Nothing)  = f []
    gin tlab@(TL_red_lockstep _ f) spec (Couple (Just x) (Just y))
	= f [gapp tlab spec (Couple x y)]
    gin tlab@(TL_red_lockstep d _) spec _ = d


instance (GAPP (TL_red_lockstep w) spec (Couple a) w w,
	  GAPP (TL_red_lockstep w) spec (Couple [a]) w w)
    => LDat (TL_red_lockstep w) spec (Couple [a]) w where
    gin (TL_red_lockstep _ f)      spec (Couple [] [])  = f []
    gin tlab@(TL_red_lockstep _ f) spec (Couple (x:xs) (y:ys))
	= f [gapp tlab spec (Couple x y),
	     gapp tlab spec (Couple xs ys)]
    gin tlab@(TL_red_lockstep d _) spec _ = d


eq_clauses = (\ (Couple (x::Int) y)  _ -> x == y) :+:
	     (\ (Couple (x::Char) y) _ -> x == y) :+:
	     (\ (Couple (x::Bool) y) _ -> x == y) :+:
	     HNil

geq x y = gapp (TL_red_lockstep False and) eq_clauses (Couple x y)

tte10 = geq (1::Int) (1::Int)
tte11 = geq ((1::Int),(2::Int)) (1,2)
tte12 = geq ((1::Int),(2::Int)) (2,1)
tte13 = geq ((1::Int),((3::Int),(2::Int))) (1,(2,2))
tte14 = geq ((1::Int),((3::Int),(2::Int))) (1,(3,2))

tte2  = geq True False
tte2' = geq True True
tte3  = geq (1::Int,True) (2,False)
tte3' = geq (True,True) (True,True)
tte4  = geq [(1::Int,True),(2::Int,False)] [(1,True)]
tte4' = geq [(1::Int,True),(2::Int,False)] [(1,True),(2,False)]


-- Monadic reconstruction
-- Reconstruction of a term (like with TL_recon, aka GMap)
-- but with the threading of state.

		-- Reconstruct the term: used in generic mapping
data TL_reconM (m :: * -> * ) = TL_reconM


instance Monad m => LDat (TL_reconM m) spec Int (m Int) where
    gin _ spec x = return x
instance Monad m => LDat (TL_reconM m) spec Bool (m Bool) where
    gin _ spec x = return x
instance Monad m => LDat (TL_reconM m) spec Char (m Char) where
    gin _ spec x = return x

-- product
instance (Monad m, 
	  GAPP (TL_reconM m) spec a dfa (m wa), 
	  GAPP (TL_reconM m) spec b dfb (m wb))
    => LDat (TL_reconM m) spec (a,b) (m (wa,wb)) where
    gin tlab spec (x,y) = liftM2 (,) 
	                    (gapp tlab spec x)
	                    (gapp tlab spec y)

-- (semi-)sums
instance (Functor m, Monad m, GAPP (TL_reconM m) spec a dfa (m w))
    => LDat (TL_reconM m) spec (Maybe a) (m (Maybe w)) where
    gin _ spec Nothing  = return Nothing
    gin tlab spec (Just x) = fmap Just (gapp tlab spec x)

instance (Monad m, GAPP (TL_reconM m) spec [a] (m [w]) (m [w]), 
	  GAPP (TL_reconM m) spec a dfa (m w))
    => LDat (TL_reconM m) spec [a] (m [w]) where
    gin tlab spec []     = return []
    gin tlab spec (x:xs) = liftM2 (:) (gapp tlab spec x) (gapp tlab spec xs)


-- in STApply, we need a case for a guarded polymorphism
-- We can't use explicit quantification. We need HLists' Apply
{-
newtype Forall l g = Forall l

instance (Apply g a bf, STApply'' bf (HCons (Forall l g) r) a d w) 
    => STApply (HCons (Forall l g) r) a d w where
    stapply = stapply'' (undefined::bf)
class STApply'' bf spec a d w | bf spec a d -> w where
    stapply'' :: bf -> spec -> a -> d -> w
instance Apply l a w => STApply'' HTrue (HCons (Forall l g) r) a d w
    stapply'' _ ((Forall l) :+: _) a _ = apply l a
instance STApply r a d w => STApply'' HFalse (HCons (Forall l g) r) a d w
    stapply'' _ (_ :+: r) a _ = stapply r l a

gsize'' a = gmapq (SCons (\ (_::String) -> (999::Int)) 
		   (SCons2 (Just ()) AW_maybe_size 
		   SNil))
	    (succ . sum) a
data AW_maybe_size w = AW_maybe_size
instance ApplyW AW_maybe_size (Maybe a) where
    applyW _ Nothing = 1000::Int

-}


-- Shallow traversal (like gMapQ, I guess)
-- Instead of recursing on children, use the user-specified,
-- possibly *generic* function to traverse the children
-- and collect the results in a list

		-- Reconstruct the term: used in generic mapping
newtype TL_red_shallow f w = TL_red_shallow f


instance LDat (TL_red_shallow f w) spec Int [w] where
    gin _ spec x = []
instance LDat (TL_red_shallow f w) spec Char [w] where
    gin _ spec x = []
instance LDat (TL_red_shallow f w) spec Bool [w] where
    gin _ spec x = []

-- product
instance (Apply f a w, Apply f b w)
    => LDat (TL_red_shallow f w) spec (a,b) [w] where
    gin (TL_red_shallow f) spec (x,y) = [apply f x,apply f y]

-- (semi-)sums
instance (Apply f a w)
    => LDat (TL_red_shallow f w) spec (Maybe a) [w] where
    gin _                  spec Nothing  = []
    gin (TL_red_shallow f) spec (Just x) = [apply f x]


-- The following definition is in the spirit of SYB. Cf. Maybe
instance (Apply f a w, Apply f [a] w)
    => LDat (TL_red_shallow f w) spec [a] [w] where
    gin _                  spec []     = []
    gin (TL_red_shallow f) spec (x:xs) = [apply f x, apply f xs]


-- Full sums (co-products)
instance (Apply f a w, Apply f b w)
    => LDat (TL_red_shallow f w) spec (Either a b) [w] where
    gin (TL_red_shallow f) spec (Left x)  = [apply f x]
    gin (TL_red_shallow f) spec (Right x) = [apply f x]


-- extracted from HList

data HNil = HNil
data HCons a b = a :+: b
infixr 5 :+:
data HTrue
data HFalse

class TypeCast   a b   | a -> b, b->a   where typeCast   :: a -> b
class TypeCast'  t a b | t a -> b, t b -> a where typeCast'  :: t->a->b
class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b
instance TypeCast'  () a b => TypeCast a b where typeCast x = typeCast' () x
instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast''
instance TypeCast'' () a a where typeCast'' _ x  = x

class  TypeEq x y b | x y -> b
instance TypeEq x x HTrue
instance TypeCast HFalse b => TypeEq x y b

class  TypeEq2 x y b | x y -> b
instance TypeEq2 (x a1) (x a2) HTrue
instance TypeCast HFalse b => TypeEq2 (x a1) (y a2) b

class Apply f x y | f x -> y where
    apply :: f -> x -> y
    apply = undefined

liftM2 op m1 m2 = do {x <- m1; y <- m2; return $ op x y}
