package sbt

object Classes {
  trait Applicative[M[_]] {
    def apply[S, T](f: M[S => T], v: M[S]): M[T]
    def pure[S](s: => S): M[S]
    def map[S, T](f: S => T, v: M[S]): M[T]
  }
  trait Monad[M[_]] extends Applicative[M] {
    def flatten[T](m: M[M[T]]): M[T]
  }
  implicit val optionMonad: Monad[Option] = new Monad[Option] {
    def apply[S, T](f: Option[S => T], v: Option[S]) = (f, v) match { case (Some(fv), Some(vv)) => Some(fv(vv)); case _ => None }
    def pure[S](s: => S) = Some(s)
    def map[S, T](f: S => T, v: Option[S]) = v map f
    def flatten[T](m: Option[Option[T]]): Option[T] = m.flatten
  }
  implicit val listMonad: Monad[List] = new Monad[List] {
    def apply[S, T](f: List[S => T], v: List[S]) = for (fv <- f; vv <- v) yield fv(vv)
    def pure[S](s: => S) = s :: Nil
    def map[S, T](f: S => T, v: List[S]) = v map f
    def flatten[T](m: List[List[T]]): List[T] = m.flatten
  }
}