--

{- Defined in Prelude, so must be commented out here

class Functor f where
    map :: (a -> b) -> (f a -> f b)

-- Functor Laws
map id = id
map (f.g) = map f . map g
-}


-- Examples

data BinTree a = Leaf a | Branch (BinTree a) (BinTree a)

instance Functor BinTree where
   fmap f (Leaf x) = Leaf (f x)
   fmap f (Branch x y) = Branch (fmap f x) (fmap f y)

instance Functor ((,) a) where
   fmap f (x,y) = (x, f y)

{- Prelude defined instances

instance Functor [] where
    fmap f []     = []
    fmap f (x:xs) = f x : fmap f xs

instance Functor Maybe where
    fmap f Nothing  = Nothing
    fmap f (Just x) = Just (f x)

-}

-- Other uses of higher order type constructors

data Tree t a = Tip a | Node (t (Tree t a))

t1 = Node [Tip 3, Tip 0]

data Bin x = Two x x

t2 = Node (Two(Tip 5) (Tip 21))



{- Class Monad - predefined in prelude

class Monad m where
    (>>=)  :: m a -> (a -> m b) -> m b
    (>>)   :: m a -> m b -> m b
    return :: a -> m a
    fail   :: String -> m a

    p >> q  = p >>= \ _ -> q
    fail s  = error s
-}

{- Predefined instances

instance Monad [ ] where
    (x:xs) >>= f  =  f x ++ (xs >>= f)
    []     >>= f  =  []
    return x      =  [x]

instance Monad Maybe where
    Just x  >>= k  =  k x
    Nothing >>= k  =  Nothing
    return         =  Just

-}

find :: Eq a => a -> [(a,b)] -> Maybe b
find x [] = Nothing
find x ((y,a):ys) = if x == y then Just a else find x ys

test a c x =
  do { b <- find a x; return (c+b) }


{- Class MonadZero and MonadPlus

class Monad m => MonadZero m where
    zero   :: m a

class MonadZero m => MonadPlus m where
    (++)   :: m a -> m a -> m a

-}

{- Predefined Instances of MonadZero and MonadPluss

instance MonadZero [ ] where
    zero = []

instance MonadPlus [ ] where
    []     ++ ys = ys
    (x:xs) ++ ys = x : (xs ++ ys)

instance MonadZero Maybe where
    zero           = Nothing

instance MonadPlus Maybe where
    Nothing ++ ys  = ys
    xs      ++ ys  = xs

-}

-- Generic Monad operations
{-
sequence       :: Monad m => [m a] -> m [a]
sequence       =  foldr mcons (return [])
                    where mcons p q = do x  <- p
                                         xs <- q
                                         return (x:xs)

sequence_      :: Monad m => [m a] -> m ()
sequence_      =  foldr (>>) (return ())

mapM             :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f as        =  sequence (map f as)

mapM_            :: Monad m => (a -> m b) -> [a] -> m ()
mapM_ f as       =  sequence_ (map f as)

(=<<)            :: Monad m => (a -> m b) -> m a -> m b
f =<< x          =  x >>= f
-}

data Op = Plus | Minus | Times | Div
data Exp = Const Float | Var String | Oper Op Exp Exp

eval :: Exp -> [(String , Float)] -> Maybe Float
eval (Const n) env = Just n
eval (Var s) env = do { x <- find s env; Just x }
eval (Oper Div x y) env =
    do { a <- eval x env
       ; b <- eval y env
       ; if b==0.0 then Nothing else Just(a+b)
       }

eval2 :: Exp -> (String -> [Float]) -> [Float]
eval2 (Const n) env = [ n ]
eval2 (Var s) env = env s
eval2 (Oper Div x y) env =
    do { a <- eval2 x env
       ; b <- eval2 y env
       ; if b==0.0 then [] else return (a+b)
       }
--