Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Code from my talk on representing polymorphic function values using type classes at the Scala eXchange ... full blog post to follow on http://www.chuusai.com/blog.
object Tuples {
import HLists._
implicit def tuple1ToHList[A](t : Product1[A]) = new { def hlisted : A :: HNil = t._1 :: HNil }
implicit def tuple2ToHList[A, B](t : Product2[A, B]) = new { def hlisted : A :: B :: HNil = t._1 :: t._2 :: HNil }
implicit def tuple3ToHList[A, B, C](t : Product3[A, B, C]) = new { def hlisted : A :: B :: C :: HNil = t._1 :: t._2 :: t._3 :: HNil }
implicit def hListToTuple1[A](h : A :: HNil) = new { def tupled : Tuple1[A] = Tuple1(h.head) }
implicit def hListToTuple2[A, B](h : A :: B :: HNil) = new { def tupled : (A, B) = (h.head, h.tail.head) }
implicit def hListToTuple3[A, B, C](h : A :: B :: C :: HNil) = new { def tupled : (A, B, C) = (h.head, h.tail.head, h.tail.tail.head) }
}
object Functions {
import HLists._
implicit def fnToHListFn1[A, T](f : A => T) = (h : A :: HNil) => f(h.head)
implicit def fnToHListFn2[A, B, T](f : (A, B) => T) = (h : A :: B :: HNil) => (f curried)(h.head)(h.tail.head)
implicit def fnToHListFn3[A, B, C, T](f : (A, B, C) => T) = (h : A :: B :: C :: HNil) => (f curried)(h.head)(h.tail.head)(h.tail.tail.head)
implicit def hlistFnToFn1[A, T](hf : A :: HNil => T) = (a : A) => hf(a :: HNil)
implicit def hlistFnToFn2[A, B, T](hf : A :: B :: HNil => T) = (a : A, b : B) => hf(a :: b :: HNil)
implicit def hlistFnToFn3[A, B, C, T](hf : A :: B :: C :: HNil => T) = (a : A, b : B, c : C) => hf(a :: b :: c :: HNil)
}
object HLists {
sealed trait HList
final case class HCons[H, T <: HList](head : H, tail : T) extends HList {
def ::[T](v : T) = HCons(v, this)
override def toString = head+" :: "+tail.toString
}
sealed class HNil extends HList {
def ::[T](v : T) = HCons(v, this)
override def toString = "HNil"
}
case object HNil extends HNil
type ::[H, T <: HList] = HCons[H, T]
val :: = HCons
}
object MapFn {
import PolyFun._
import HLists._
trait Mapper[F <: Trans, In, Out] {
def map(t : In) : Out
}
implicit def hnilMapper[F <: Trans] = new Mapper[F, HNil, HNil] {
def map(l : HNil) = HNil
}
implicit def hlistMapper[F <: Trans, InH, OutH, InT <: HList, OutT <: HList](implicit fh : F#λ[InH, OutH], mt : Mapper[F, InT, OutT]) = new Mapper[F, InH :: InT, OutH :: OutT] {
def map(l : InH :: InT) = HCons(fh(l.head), mt.map(l.tail))
}
trait PartialMap[F <: Trans] {
def apply[In <: HList, Out <: HList](in : In)(implicit mapper : Mapper[F, In, Out]) : Out = mapper.map(in)
}
def map[F <: Trans] = new PartialMap[F] {}
}
object FoldFn {
import PolyFun._
import HLists._
trait FoldLeft[F <: Trans, In, R] {
def foldLeft(l : In, acc : R, op : (R, R) => R) : R
}
implicit def hlistFoldLeft[F <: Trans, InH, InT <: HList, R](implicit fh : F#λ[InH, R], ft : FoldLeft[F, InT, R]) = new FoldLeft[F, InH :: InT, R] {
def foldLeft(l : InH :: InT, acc : R, op : (R, R) => R) = ft.foldLeft(l.tail, op(fh(l.head), acc), op)
}
implicit def hnilFoldLeft[F <: Trans, R] = new FoldLeft[F, HNil, R] {
def foldLeft(l : HNil, acc : R, op : (R, R) => R) : R = acc
}
trait PartialFoldLeft[F <: Trans] {
def apply[In <: HList, R](in : In)(z : R)(op : (R, R) => R)(implicit folder : FoldLeft[F, In, R]) : R = folder.foldLeft(in, z, op)
}
def foldLeft[F <: Trans] = new PartialFoldLeft[F] {}
}
object LiftO {
import GetFn._
import IsDefinedFn._
import HLists._
import MapFn._
import FoldFn._
import Functions._
def liftO[In <: HList, Out <: HList, R](f : Out => R)(implicit mapper : Mapper[Get, In, Out], folder : FoldLeft[IsDefined, In, Boolean]) =
(i : In) => if(foldLeft[IsDefined](i)(true)(_ & _)) Option(f(map[Get](i))) else None
val sumO = liftO((_ : Int) + (_ : Int))
val prodO = liftO((_ : Int) * (_ : Int) * (_ : Int))
def main(args : Array[String]) {
val s1 = sumO(Some(1), Some(2))
println(s1)
val s2 = sumO(Some(1), None)
println(s2)
val s3 = sumO(None, Some(2))
println(s3)
val s4 = sumO(None, None)
println(s4)
val p1 = prodO(Some(2), Some(3), Some(4))
println(p1)
val p2 = prodO(Some(2), None, Some(4))
println(p2)
}
}
object TestHList {
import IncFn._
import GetFn._
import IsDefinedFn._
import HLists._
import MapFn._
import FoldFn._
def main(args : Array[String]) {
val i1 = inc(23)
println(i1)
val i2 = inc("foo")
println(i2)
type ISII = Int :: String :: Int :: Int :: HNil
val l1 : ISII = 1 :: "foo" :: 2 :: 3 :: HNil
println(l1)
val l2 : ISII = map[Inc](l1)
println(l2)
type OIOS = Option[Int] :: Option[String] :: HNil
type IS = Int :: String :: HNil
val l3 = Option(1) :: Option("foo") :: HNil
val l4 : IS = map[Get](l3)
println(l3)
println(l4)
val l5 : IS = map[Get](l3)
println(l5)
type OIODOBOSOI = Option[Int] :: Option[Double] :: Option[Boolean] :: Option[String] :: Option[Int] :: HNil
type IDBSI = Int :: Double :: Boolean :: String :: Int :: HNil
type BBBBB = Boolean :: Boolean :: Boolean :: Boolean :: Boolean :: HNil
val l6 : OIODOBOSOI = Option(1) :: Option(1.0) :: Option(false) :: Option("foo") :: Option(2) :: HNil
val l7 : OIODOBOSOI = Option(1) :: Option(1.0) :: (None : Option[Boolean]) :: Option("foo") :: Option(2) :: HNil
val l8 : IDBSI = map[Get](l6)
println(l8)
val l9 : BBBBB = map[IsDefined](l6)
println(l9)
val l10 : BBBBB = map[IsDefined](l7)
println(l10)
val b1 = foldLeft[IsDefined](l6)(true)(_ & _)
println(b1)
val b2 = foldLeft[IsDefined](l7)(true)(_ & _)
println(b2)
}
}
object PolyFun {
type Trans = {
type λ[T, R] <: (T => R)
}
trait TransCase[F, T, R] extends (T => R) {
val f : T => R
def apply(t : T) : R = f(t)
}
trait PTrans[F] {
type λ[T, R] = TransCase[F, T, R]
}
}
object Uses {
import PolyFun._
def useTrans1[F <: Trans](implicit fi : F#λ[Int, Int], fs : F#λ[String, String]) = (fi(23), fs("foo"))
trait PartialUseTransPoly1[F <: Trans] {
def apply[T, U](t : T, u : U)(implicit ft : F#λ[T, T], fu : F#λ[U, U]) = (ft(t), fu(u))
}
def useTransPoly1[F <: Trans] = new PartialUseTransPoly1[F] {}
def useTrans2[F <: Trans](implicit fi : F#λ[Int, Int], fs : F#λ[String, Int]) = (fi(23), fs("foo"))
trait PartialUseTransPoly2[F <: Trans] {
def apply[T, U](t : T, u : U)(implicit ft : F#λ[T, Int], fu : F#λ[U, Int]) = (ft(t), fu(u))
}
def useTransPoly2[F <: Trans] = new PartialUseTransPoly2[F] {}
}
object IncFn {
import PolyFun._
class Inc extends PTrans[Inc]
object Inc {
def apply[T](f0 : T => T) = new TransCase[Inc, T, T] { val f = f0 }
}
implicit def incInt = Inc[Int](_+1)
implicit def incString = Inc[String](_+"*")
def inc[T](t : T)(implicit f : TransCase[Inc, T, T]) = f(t)
inc(23)
inc("foo")
}
object TwiceFn {
import PolyFun._
class Twice extends PTrans[Twice]
object Twice {
def apply[T](f0 : T => T) = new TransCase[Twice, T, T] { val f = f0 }
}
implicit def twiceInt = Twice[Int](_*2)
implicit def twiceString = Twice[String](s => s+s)
def twice[T](t : T)(implicit f : TransCase[Twice, T, T]) = f(t)
twice(23)
twice("foo")
}
object SizeFn {
import PolyFun._
class Size extends PTrans[Size]
object Size {
def apply[T](f0 : T => Int) = new TransCase[Size, T, Int] { val f = f0 }
}
implicit def sizeInt = Size[Int](identity)
implicit def sizeString = Size[String](_.length)
def size[T](t : T)(implicit f : TransCase[Size, T, Int]) = f(t)
size(23)
size("foo")
}
object GetFn {
import PolyFun._
class Get extends PTrans[Get]
object Get {
def apply[T](f0 : Option[T] => T) = new TransCase[Get, Option[T], T] { val f = f0 }
}
implicit def getDflt[T] = Get[T](_.get)
def get[T](t : Option[T])(implicit f : TransCase[Get, Option[T], T]) = f(t)
get(Option(23))
get(Option("foo"))
}
object IsDefinedFn {
import PolyFun._
class IsDefined extends PTrans[IsDefined]
object IsDefined {
def apply[T](f0 : Option[T] => Boolean) = new TransCase[IsDefined, Option[T], Boolean] { val f = f0 }
}
implicit def idDefinedDflt[T] = IsDefined[T](_.isDefined)
def isDefined[T](t : Option[T])(implicit f : TransCase[IsDefined, Option[T], Boolean]) = f(t)
isDefined(Option(23))
isDefined(Option("foo"))
}
object TestPolyFun {
import Uses._
import IncFn._
import SizeFn._
def main(args : Array[String]) {
val p1 = useTrans1[Inc]
println(p1)
val p2 = useTransPoly1[Inc](23, "foo")
println(p2)
val p3 = useTransPoly1[Inc]("foo", 23)
println(p3)
val p4 = useTrans2[Size]
println(p4)
val p5 = useTransPoly2[Size](23, "foo")
println(p5)
val p6 = useTransPoly2[Size]("foo", 23)
println(p6)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.