package scalaz

sealed trait Function2W[T1, T2, R] {
  val k: (T1, T2) => R

  def flip : (T2, T1) => R = (v2: T2, v1: T1) => k(v1, v2)

  def on[X](f: (R, R) => X, t1: (T1, T1), t2: (T2, T2)): X = f(k(t1._1, t2._1), k(t1._2, t2._2))

  import concurrent.Strategy
  import concurrent.Promise
  import Scalaz._

  def promise(implicit s: Strategy): (T1, T2) => Promise[R] = (x: T1, y: T2) => x.pure[Promise].<**>(y.pure[Promise])(k)

  def contramap[TT](f: TT => T1)(implicit ev: T1 =:= T2): (TT, TT) => R = (t1, t2) => k(f(t1), ev(f(t2)))

  def lift[F[_]](implicit f: Applicative[F]): (F[T1], F[T2]) => F[R] = (a: F[T1], b: F[T2]) => (a <**> b)(this)

  def byName: (=> T1, => T2) => R = (t1, t2) => k(t1, t2)
}

trait Function2s {
  implicit def Function2To[T1, T2, R](f: (T1, T2) => R): Function2W[T1, T2, R] = new Function2W[T1, T2, R] {
    val k = f
  }

  implicit def Function2From[T1, T2, R](f: Function2W[T1, T2, R]): (T1, T2) => R = f.k
}