Created
June 15, 2011 14:29
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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