Skip to content

Instantly share code, notes, and snippets.

@milessabin
Created June 15, 2011 14:29
Show Gist options
  • Save milessabin/1027231 to your computer and use it in GitHub Desktop.
Save milessabin/1027231 to your computer and use it in GitHub Desktop.
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