/* sbt -- Simple Build Tool
 * Copyright 2010 Mark Harrah
 */
package sbt

trait TypeFunctions
{
	type Id[X] = X
	sealed trait Const[A] { type Apply[B] = A }
	sealed trait ConstK[A] { type l[L[x]] = A }
	sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] }
	sealed trait [A[_], B[_]] { type l[T] = A[B[T]] }
	sealed trait P1of2[M[_,_], A] { type Apply[B] = M[A,B]; type Flip[B] = M[B, A] }

	final val left = new (Id ~> P1of2[Left, Nothing]#Flip) { def apply[T](t: T) = Left(t) }
	final val right = new (Id ~> P1of2[Right, Nothing]#Apply) { def apply[T](t: T) = Right(t) }
	final val some = new (Id ~> Some) { def apply[T](t: T) = Some(t) }
	final def idFun[T] = (t: T) => t
	final def const[A,B](b: B): A=> B = _ => b
	final def idK[M[_]]: M ~> M = new (M ~> M) { def apply[T](m: M[T]): M[T] = m }
	
	def nestCon[M[_], N[_], G[_]](f: M ~> N): (M ∙ G)#l ~> (N ∙ G)#l =
		f.asInstanceOf[(M ∙ G)#l ~> (N ∙ G)#l] // implemented with a cast to avoid extra object+method call.  castless version:
	/* new ( (M ∙ G)#l ~> (N ∙ G)#l ) {
		def apply[T](mg: M[G[T]]): N[G[T]] = f(mg)
	}*/

	implicit def toFn1[A,B](f: A => B): Fn1[A,B] = new Fn1[A,B] {
		def [C](g: C => A) = f compose g
	}
	
	type Endo[T] = T=>T
	type ~>|[A[_],B[_]] = A ~> Compose[Option, B]#Apply
}
object TypeFunctions extends TypeFunctions

trait ~>[-A[_], +B[_]]
{ outer =>
	def apply[T](a: A[T]): B[T]
	// directly on ~> because of type inference limitations
	final def [C[_]](g: C ~> A): C ~> B = new (C ~> B) { def apply[T](c: C[T]) = outer.apply(g(c)) }
	final def [C,D](g: C => D)(implicit ev: D <:< A[D]): C => B[D] = i => apply(ev(g(i)) )
	final def fn[T] = (t: A[T]) => apply[T](t)
}
object ~>
{
	import TypeFunctions._
	val Id: Id ~> Id = new (Id ~> Id) { def apply[T](a: T): T = a }
	implicit def tcIdEquals: (Id ~> Id) = Id
}
trait Fn1[A, B] {
	def [C](g: C => A): C => B
}