object To { trait Bijection[A, B] { def to: A => B def from: B => A } sealed trait Translate[A] { type T def apply(a: A): T } sealed trait To[A] extends Translate[A] sealed trait From[A] extends Translate[A] implicit def BijectionTo[A, B](implicit map: Bijection[A, B]) = new To[A] { type T = B def apply(a: A): T = map.to(a) } implicit def BijectionFrom[A, B](implicit map: Bijection[A, B]) = new From[B] { type T = A def apply(a: B): A = map.from(a) } implicit class ToJavaSyntax[A](a: A) { def toJava(implicit to: To[A]): to.T = to(a) } implicit class ToScalaSyntax[A](a: A) { def toScala(implicit from: From[A]): from.T = from(a) } implicit object IntToInteger extends Bijection[Int, Integer] { def from = _.toInt def to = _.toInt } trait Function[A, B] { def apply(a: A): B } implicit def FunctionBijection[A1, A2, B1, B2](implicit A: Bijection[A1, A2], B: Bijection[B1, B2]) = new Bijection[(A1 => B1), Function[A2, B2]] { def to: (A1 => B1) => Function[A2, B2] = f => new Function[A2, B2] { def apply(a: A2) = B.to(f(A.from(a))) } def from: Function[A2, B2] => (A1 => B1) = f => a => B.from(f(A.to(a))) } }