Created
January 3, 2019 23:35
-
-
Save ahoy-jon/8aed3243a21d143b37b381fb10b856f5 to your computer and use it in GitHub Desktop.
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
package io.univalence | |
import io.univalence.Fk.Encoder.{BindedTypedExprEncoder, SimpleEncoder} | |
import io.univalence.Fk.Expr.Ops | |
import io.univalence.Fk.TypedExpr.TypedOr | |
import org.json4s.JsonAST.{JArray, JString, JValue} | |
import scala.language.{dynamics, implicitConversions} | |
object Fk { | |
sealed trait Expr { | |
} | |
trait LowPriority { | |
implicit def antiArrowAssocExpr[A: SimpleEncoder](t: (A, Expr)): (Expr, Expr) = (lit(t._1), t._2) | |
implicit def antiArrowAssocExpr2[A: SimpleEncoder, B: SimpleEncoder](t: (A, B)): (Expr, Expr) = | |
(lit(t._1), lit(t._2)) | |
implicit def toLit[T: SimpleEncoder](t: T): Expr = lit(t) | |
implicit class ExprOps(expr: Expr) { | |
def first: Expr = Ops.First(expr) | |
def as[T: Encoder]: TypedExpr[T] = TypedExpr.TypeCasted(expr, implicitly[Encoder[T]]) | |
def or(expr2: Expr):Expr = Ops.Or(expr,expr2) | |
def #>[T:SimpleEncoder](f: JValue => T):TypedExpr[T] = TypedExpr.JsonMap(expr,f,implicitly[SimpleEncoder[T]]) | |
def when(ife: (Expr, Expr), ifes: (Expr, Expr)*): Expr = Ops.CaseWhen(ife, ifes: _*) | |
} | |
} | |
object Expr extends LowPriority { | |
sealed trait Ops extends Expr | |
object Ops { | |
case class Or(expr: Expr,expr2: Expr) extends Ops | |
case class First(expr: Expr) extends Ops | |
case class CaseWhen(ife: (Expr, Expr), ifes: (Expr, Expr)*) extends Ops | |
} | |
} | |
case class FieldPath(pathParts: Seq[String]) extends Dynamic with Expr { | |
def selectDynamic(field: String): FieldPath = FieldPath(pathParts :+ field) | |
} | |
sealed trait Encoder[T] { | |
type ScalaType | |
def scalaEnc: Encoder[ScalaType] | |
} | |
object Encoder { | |
sealed trait SimpleEncoder[T] extends Encoder[T] { | |
final type ScalaType = T | |
def scalaEnc: Encoder[T] = this | |
} | |
implicit case object str extends SimpleEncoder[String] | |
implicit case object int extends SimpleEncoder[Int] | |
implicit case object bool extends SimpleEncoder[Boolean] | |
implicit def opt[T: Encoder]: Encoder[Option[T]] = ??? | |
case class BindedTypedExprEncoder[T](scalaEnc: Encoder[T]) extends Encoder[TypedExpr[T]] { | |
override type ScalaType = T | |
} | |
implicit def bindedTypedExprEncoder[T](implicit scalaEnc: Encoder[T]): BindedTypedExprEncoder[T] = | |
BindedTypedExprEncoder(scalaEnc) | |
} | |
sealed abstract class TypedExpr[T](enc: Encoder[T]) extends Expr { | |
def or(typedExpr2: TypedExpr[T])(implicit encoder: Encoder[T]):TypedExpr[T] = TypedOr(this,typedExpr2,encoder) | |
import TypedExpr._ | |
def |>[B](f: T => B)(implicit enc: Encoder[B]): TypedExpr[enc.ScalaType] = | |
enc match { | |
case se: SimpleEncoder[B] => DirectMap[T, B](this, f, se).asInstanceOf[TypedExpr[enc.ScalaType]] | |
case bt: BindedTypedExprEncoder[enc.ScalaType] => | |
BindMap[T, enc.ScalaType](this, f.asInstanceOf[T => TypedExpr[enc.ScalaType]], bt) | |
} | |
} | |
object TypedExpr { | |
case class TypedOr[T](value: TypedExpr[T], value1: TypedExpr[T],enc:Encoder[T]) extends TypedExpr[T](enc) | |
abstract class Tx[A, B](enc: Encoder[B]) extends TypedExpr[B](enc) | |
case class TypeCasted[Scala](source: Expr, enc: Encoder[Scala]) extends TypedExpr[Scala](enc) | |
case class JsonMap[O](source:Expr, f : JValue => O, encoder: SimpleEncoder[O]) extends TypedExpr[O](encoder) | |
case class BindMap[S, O](source: TypedExpr[S], f: S => TypedExpr[O], bte: BindedTypedExprEncoder[O]) | |
extends TypedExpr[O](bte.scalaEnc) | |
case class DirectMap[S, O](source: TypedExpr[S], f: S => O, enc: SimpleEncoder[O]) extends TypedExpr[O](enc) | |
case class MappedExpr[S, O, Scala](source: TypedExpr[S], f: S => O, enc: Encoder[Scala]) | |
extends TypedExpr[Scala](enc) | |
case class Lit[T](value: T, simpleEncoder: SimpleEncoder[T]) extends TypedExpr[T](simpleEncoder) | |
} | |
def lit[T: SimpleEncoder](t: T): TypedExpr[T] = TypedExpr.Lit(t, implicitly[SimpleEncoder[T]]) | |
object in extends Dynamic { | |
def selectDynamic(field: String): FieldPath = FieldPath(Vector(field)) | |
} | |
} | |
object Test { | |
def main(args: Array[String]): Unit = { | |
import Fk._ | |
val toto = in.toto | |
val expr1 = toto.first.as[Int] |> (_ % 3 == 0) | |
val expr2 = in.tata.first.as[Boolean] |> { | |
case true => toto.as[Int] | |
case false => lit(1) | |
} | |
val expr3Typed: TypedExpr[Int] = toto.as[Int] or expr2 | |
val expr3Untype: Expr = toto or expr2 | |
val expr4 = in.tata.when(1 -> "1", 2 -> "3", 3 -> in.toto) | |
println(expr1) | |
println(expr2) | |
println(expr3Typed) | |
println(expr3Untype) | |
println(expr4) | |
val expr5 = in.nestedField #> { | |
case JArray(arr) => arr.count({ | |
case JString(s) => s.contains("XYZ") | |
case _ => false | |
}) | |
} | |
println(expr5) | |
/* | |
DirectMap(TypeCasted(First(FieldPath(Vector(toto))),int),<function1>,bool) | |
BindMap(TypeCasted(First(FieldPath(Vector(tata))),bool),<function1>,BindedTypedExprEncoder(int)) | |
TypedOr(TypeCasted(FieldPath(Vector(toto)),int),BindMap(TypeCasted(First(FieldPath(Vector(tata))),bool),<function1>,BindedTypedExprEncoder(int)),int) | |
Or(FieldPath(Vector(toto)),BindMap(TypeCasted(First(FieldPath(Vector(tata))),bool),<function1>,BindedTypedExprEncoder(int))) | |
CaseWhen((Lit(1,int),Lit(1,str)),WrappedArray((Lit(2,int),Lit(3,str)), (Lit(3,int),FieldPath(Vector(toto))))) | |
JsonMap(FieldPath(Vector(nestedField)),<function1>,int) | |
*/ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment