module L0229 where

data Maybe1 a = Just1 a | Nothing1 deriving Show

instance Functor Maybe1 where
 fmap f Nothing1 = Nothing1
 fmap f (Just1 a) = Just1 (f a)

instance Applicative Maybe1 where
 pure = Just1
 
 (<*>) Nothing1 _ = Nothing1
 (<*>) _ Nothing1 = Nothing1
 (<*>) (Just1 a) (Just1 b) = Just1 (a b)

instance Monad Maybe1 where
 (>>=) Nothing1  f = Nothing1
 (>>=) (Just1 x) f = f x
 
data Expr = Val Int | Div Expr Expr

eval0 :: Expr -> Int
eval0 (Val a) = a
eval0 (Div x y) = div (eval0 x) (eval0 y)

eval1 :: Expr -> Maybe1 Int
eval1 (Val a) = Just1 a
eval1 (Div x y) = case eval1 x of
                    Nothing1 -> Nothing1
                    Just1 n -> case eval1 y of
                        Nothing1 -> Nothing1
                        Just1 m -> safeDiv n m

safeDiv _ 0 = Nothing1
safeDiv n m = Just1 (div n m)

--eval2 :: Expr -> Maybe1: Int
--eval2 (Val n) = pure n
--eval2 (Div x y) = pure safeDiv <*> eval2 x <*> eval2 y

eval3 :: Expr -> Maybe1 Int
eval3 (Val n) = Just1 n
eval3 (Div x y) = eval3 x >>= \n ->
                  eval3 y >>= \m ->
                  safeDiv n m

eval4 :: Expr -> Maybe1 Int
eval4 (Val n) = Just1 n
eval4 (Div x y) = do
  n <- eval4 x
  m <- eval4 y
  safeDiv n m
  
pairs :: [a] -> [b] -> [(a,b)]
pairs xs ys = do
  x <- xs
  y <- ys
  return (x,y)

pairs' :: [a] -> [b] -> [(a,b)]
pairs' xs ys = [(x,y)|x <- xs,y <- ys]

pairs'' :: [a] -> [b] -> [(a,b)]
pairs'' xs ys = pure (\x->(\y->(x,y))) <*> xs <*> ys

--pairs''' xs ys = xs >>= \x ->
--                 ys >>= \y ->
--                 (x,y)
