module TypeClass

class Monad (m : U → U) where
  bind : ∀ a b. m a → (a → m b) → m b
  pure : ∀ a. a → m a

infixl 1 _>>=_ _>>_
_>>=_ : {0 m} {{Monad m}} {0 a b} -> (m a) -> (a -> m b) -> m b
ma >>= amb = bind ma amb

_>>_ : ∀ m a b. {{Monad m}} -> m a -> m b -> m b
ma >> mb = mb

data Either : U -> U -> U where
  Left : ∀ A B. A -> Either A B
  Right : ∀ A B. B -> Either A B

instance {a} -> Monad (Either a) where
  bind (Left a) amb = Left a
  bind (Right b) amb = amb b

  pure a = Right a

data Maybe : U -> U where
  Just    : ∀ A. A -> Maybe A
  Nothing : ∀ A. Maybe A

instance Monad Maybe where
  bind Nothing amb = Nothing
  bind (Just a) amb = amb a

  pure a = Just a

infixr 7 _::_
data List : U -> U where
  Nil : ∀ A. List A
  _::_ : ∀ A. A -> List A -> List A

infixl 7 _++_
_++_ : ∀ A. List A -> List A -> List A
Nil ++ ys = ys
(x :: xs) ++ ys = x :: (xs ++ ys)

instance Monad List where
  bind Nil f = Nil
  bind (x :: xs) f = f x ++ bind xs f

  pure x = x :: Nil

infixr 1 _,_
data Pair : U -> U -> U where
  _,_ : ∀ A B. A -> B -> Pair A B



test : Maybe Int
test = pure 10

foo : Int -> Maybe Int
foo x = Just 42 >> Just x >>= (\ x => pure 10)

bar : Int -> Maybe Int
bar x = do
  let y = x
  z <- Just x
  pure z

baz : ∀ A B. List A -> List B -> List (Pair A B)
baz xs ys = do
  x <- xs
  y <- ys
  pure (x , y)
