Last active
August 29, 2015 14:19
-
-
Save ianoc/38ce47fbada557ac1709 to your computer and use it in GitHub Desktop.
Tpe Info + Select
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 exp { | |
sealed trait ExprTpe[T] | |
sealed trait ExprPrimitiveTpe[T] extends ExprTpe[T] | |
sealed trait ExprContainerTpe[T] extends ExprTpe[T] { | |
def mapping: List[(String, ExprTpe[_])] | |
} | |
case class ContainerTpe2[A, B](_1: ExprTpe[A], nme1: String, _2: ExprTpe[B], nme2: String) extends ExprContainerTpe[(A, B)] { | |
override def toString = s"""Type container of ${_1} and ${_2}""" | |
override val mapping = List(nme1 -> _1, nme2 -> _2) | |
} | |
case class TransformingTpe[A, B](_1: ExprTpe[A], _2: ExprTpe[B]) extends ExprTpe[Fn[A, B]] { | |
override def toString = s"""Type container of ${_1} and ${_2}""" | |
} | |
case object PInt extends ExprPrimitiveTpe[Int] | |
case object PDouble extends ExprPrimitiveTpe[Double] | |
case object PFloat extends ExprPrimitiveTpe[Float] | |
case object PBoolean extends ExprPrimitiveTpe[Boolean] | |
object ExprTpe { | |
implicit def pIntProvider: ExprTpe[Int] = PInt | |
implicit def pDoubleProvider: ExprTpe[Double] = PDouble | |
implicit def pFloatProvider: ExprTpe[Float] = PFloat | |
implicit def pBooleanProvider: ExprTpe[Boolean] = PBoolean | |
implicit def container2[T: ExprTpe, U: ExprTpe]: ExprContainerTpe[(T, U)] = | |
ContainerTpe2(implicitly[ExprTpe[T]], "_1", implicitly[ExprTpe[U]], "_2") | |
implicit def exprTpeProvider[A:ExprTpe, B: ExprTpe]: ExprTpe[Fn[A, B]] = TransformingTpe(implicitly, implicitly) | |
} | |
case class Scope(m: Map[Var[_], Exp[_]]) { | |
def put[T](pair: (Var[T], Exp[T])): Scope = new Scope(m + pair) | |
def apply[T](v: Var[T]): Either[String, Exp[T]] = m.get(v) match { | |
case Some(exp) => Right(exp.asInstanceOf[Exp[T]]) | |
case None => Left(s"$v not found. Scope: $m") | |
} | |
} | |
object Scope { | |
def empty: Scope = Scope(Map.empty) | |
} | |
sealed trait Exp[A] { | |
def tpeInfo: ExprTpe[A] | |
/** | |
* Either evaluate the Expression, or return an error string | |
* (about missing Vars is the only thing that can go wrong) | |
*/ | |
private[exp] def run(scope: Scope): Either[String, A] | |
} | |
case class Const[A: ExprTpe](get: A) extends Exp[A] { | |
override val tpeInfo = implicitly[ExprTpe[A]] | |
private[exp] def run(scope: Scope) = Right(get) | |
} | |
case class Pair[A: ExprTpe, B: ExprTpe](first: Exp[A], second: Exp[B]) extends Exp[(A, B)] { | |
override val tpeInfo = implicitly[ExprTpe[(A, B)]] | |
private[exp] def run(scope: Scope) = for { | |
a <- first.run(scope).right | |
b <- second.run(scope).right | |
} yield (a, b) | |
} | |
case class Apply[B: ExprTpe, A: ExprTpe](in: Exp[B], fn: Exp[Fn[B, A]]) extends Exp[A] { | |
override val tpeInfo = implicitly[ExprTpe[A]] | |
private[exp] def run(scope: Scope) = for { | |
b <- in.run(scope).right | |
f <- fn.run(scope).right | |
} yield f.run(b) | |
} | |
case class Var[A: ExprTpe](name: String) extends Exp[A] { | |
override val tpeInfo = implicitly[ExprTpe[A]] | |
private[exp] def run(scope: Scope) = for { | |
a <- scope(this).right | |
result <- a.run(scope).right | |
} yield result | |
} | |
case class Lambda[A: ExprTpe, B: ExprTpe](x: Var[A], result: Exp[B]) extends Exp[Fn[A, B]] { | |
override val tpeInfo = implicitly[ExprTpe[Fn[A, B]]] | |
private[exp] def run(scope: Scope) = Right(Fn.LambdaFn(this, scope)) | |
} | |
// Private Fn class, so we can't write general scala functions | |
sealed trait Fn[A, B] { | |
private[exp] def run(a: A): B | |
} | |
object Exp { | |
// would not be here in real life, as it is an escape hatch | |
// instead, we would convert the Exp to code/source that can | |
// be run elsewhere | |
def run[A](e: Exp[A]): Either[String, A] = e.run(Scope.empty) | |
implicit class FnExp[A: ExprTpe, B: ExprTpe](val e: Exp[Fn[A, B]]) { | |
def apply(a: Exp[A]): Exp[B] = Apply[A, B](a, e) | |
def andThen[C: ExprTpe](that: Exp[Fn[B, C]]): Exp[Fn[A, C]] = { | |
val va = Var[A]("a") | |
val eb = Apply(va, e) | |
val ec = Apply(eb, that) | |
Lambda(va, ec) | |
} | |
def zip[C: ExprTpe](that: Exp[Fn[A, C]]): Exp[Fn[A, (B, C)]] = { | |
val va = Var[A]("a") | |
val b = Apply(va, e) | |
val c = Apply(va, that) | |
Lambda(va, Pair(b, c)) | |
} | |
} | |
} | |
object Fn { | |
import ExprTpe._ | |
def fst[A: ExprTpe, B: ExprTpe](p: Exp[(A, B)]): Exp[A] = Apply(p, lift(Fst[A, B]())) | |
def snd[A: ExprTpe, B: ExprTpe](p: Exp[(A, B)]): Exp[B] = Apply(p, lift(Snd[A, B]())) | |
def addInt(a: Exp[Int], b: Exp[Int]): Exp[Int] = | |
Apply(Pair(a, b), lift(AddInt)) | |
def ifThenElse[A: ExprTpe](b: Exp[Boolean], iftrue: Exp[A], iffalse: Exp[A]): Exp[A] = | |
Apply(Pair(b, Pair(iftrue, iffalse)), lift(IfThenElse[A]())) | |
def select[A, B](a: Exp[A], fieldName: String): Exp[B] = { | |
a.tpeInfo match { | |
case container: ExprContainerTpe[_] => | |
val indx = container | |
.mapping | |
.indexWhere(e => e._1 == fieldName) | |
require(indx >= 0) | |
implicit val tpeInfoA: ExprTpe[A] = a.tpeInfo.asInstanceOf[ExprTpe[A]] | |
implicit val tpeInfoB: ExprTpe[B] = container.mapping(indx)._2.asInstanceOf[ExprTpe[B]] | |
Apply(a, lift(Select[A, B](indx))) | |
case _ => sys.error("Should not occur, A is a container Tpe") | |
} | |
} | |
// non fundamental ops | |
def lift[A: ExprTpe, B: ExprTpe](f: Fn[A, B]): Exp[Fn[A, B]] = Const(f) | |
def swap[A: ExprTpe, B: ExprTpe](p: Exp[(A, B)]): Exp[(B, A)] = | |
Pair(snd(p), fst(p)) | |
def curry[A: ExprTpe, B: ExprTpe, C: ExprTpe](fn: Exp[Fn[(A, B), C]]): Exp[Fn[A, Fn[B, C]]] = { | |
val a = Var[A]("A") | |
val b = Var[B]("B") | |
val pair = Pair(a, b) | |
val c = Apply(pair, fn) | |
val fnbc = Lambda(b, c) | |
Lambda(a, fnbc) | |
} | |
def uncurry[A: ExprTpe, B: ExprTpe, C: ExprTpe](fn: Exp[Fn[A, Fn[B, C]]]): Exp[Fn[(A, B), C]] = { | |
val pair = Var[(A, B)]("pair") | |
val a = fst(pair) | |
val fnbc = Apply(a, fn) | |
val b = snd(pair) | |
val c = Apply(b, fnbc) | |
Lambda(pair, c) | |
} | |
// implementations | |
private[exp] case class LambdaFn[A: ExprTpe, B: ExprTpe](l: Lambda[A, B], scope: Scope) extends Fn[A, B] { | |
def run(a: A) = { | |
val newScope = scope.put(l.x -> Const(a)) | |
val b = l.result.run(newScope) | |
b.right.get | |
} | |
} | |
private case class Fst[A: ExprTpe, B: ExprTpe]() extends Fn[(A, B), A] { | |
def run(b: (A, B)) = b._1 | |
} | |
private case class Snd[A: ExprTpe, B: ExprTpe]() extends Fn[(A, B), B] { | |
def run(b: (A, B)) = b._2 | |
} | |
private case object AddInt extends Fn[(Int, Int), Int] { | |
def run(p: (Int, Int)) = p._1 + p._2 | |
} | |
private case class IfThenElse[A]() extends Fn[(Boolean, (A, A)), A] { | |
def run(in: (Boolean, (A, A))) = if(in._1) in._2._1 else in._2._2 | |
} | |
private case class Select[A, B](idx: Int) extends Fn[A, B] { | |
def run(in: A) = { | |
val iter = in.asInstanceOf[Product].productIterator | |
if (idx > 0) iter.drop(idx) | |
iter.next.asInstanceOf[B] | |
} | |
} | |
} | |
} | |
/* | |
scala> import exp._ | |
import exp._ | |
scala> val c = Const(123, (245.0, 121)) | |
c: exp.Const[(Int, (Double, Int))] = Const((123,(245.0,121))) | |
scala> val sel = Fn.select(Fn.select(c, "_2"), "_1") | |
sel: exp.Exp[Nothing] = Apply(Apply(Const((123,(245.0,121))),Const(Select(1))),Const(Select(0))) | |
scala> sel.tpeInfo | |
res37: exp.ExprTpe[Nothing] = PDouble | |
scala> | |
scala> Exp.run(Fn.select(Fn.select(c, "_2"), "_1")) | |
res38: Either[String,Nothing] = Right(245.0) | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment