All Downloads are FREE. Search and download functionalities are using the official Maven repository.

.uniform.core_sjs1_2.13.5.0.0-RC6.source-code.InterpreterMacros.scala Maven / Gradle / Ivy

The newest version!
package ltbs.uniform

import com.github.ghik.silencer.silent
import scala.language.higherKinds
import scala.reflect.macros.blackbox
import scala.annotation.tailrec

class InterpreterMacros(val c: blackbox.Context) {
  import c.universe._

  /**
    * Turn a Needs[_] type into a (List[InteractTypes], List[(ConvFrom, ConvTo)], List[AskListType]) 
    */ 
  @silent("never used") // quasiquoting seems to produce lots of false warnings  
  def getNeeds[H <: Needs[_, _], INTERACTTC[_,_], ASKLISTTC[_]](
    implicit ttn: c.WeakTypeTag[H], 
    ttInteractTc: WeakTypeTag[INTERACTTC[_,_]],
    ttAskListTc: WeakTypeTag[ASKLISTTC[_]]
  ): (List[(Type, Type)], List[(Type, Type)], List[Type]) = {

    val INTERACT: TypeSymbol = symbolOf[Needs.Interact[_,_]]
    val CONV = symbolOf[Needs.Convert[Any,_]]
    val ASKLIST = symbolOf[Needs.AskList[_]]        

    ttn.tpe match {
      case _: ExistentialType => (Nil, Nil, Nil)
      case TypeRef(_, INTERACT, List(inta, intb)) => ((inta,intb) :: Nil, Nil, Nil)
      case TypeRef(_, CONV, List(convf, conva)) => (Nil, (convf, conva) :: Nil, Nil)
      case TypeRef(_, ASKLIST, List(askl)) => (Nil, Nil, askl :: Nil)                
      case RefinedType(parents,_) =>
        parents.foldLeft((List.empty[(Type,Type)], List.empty[(Type, Type)], List.empty[Type])){
          case ((iacc, convacc, alacc), x) =>
            x match {
              case TypeRef(_, INTERACT, List(inta, intb)) => ((inta,intb) :: iacc, convacc, alacc)
              case TypeRef(_, CONV, List(convf, conva)) => (iacc, (convf, conva):: convacc, alacc)
              case TypeRef(_, ASKLIST, List(askl)) => (iacc, convacc, askl :: alacc)
              case _ => (iacc, convacc, alacc)
            }
        }
      case other =>
        c.abort(c.enclosingPosition, s"I don't know how to extract Needs from $other (${showRaw(other)})")
    }
  }

  def implicitMaps(
    fType: Type, 
    types: List[Type]
  ): Tree = {

    @tailrec
    def inner(types: List[Type], acc: Tree): Tree = types match {
      case v::x =>
        val resultingType = injectType(fType, v)
        inner(x, q"(implicitly[izumi.reflect.Tag[$v]].tag, implicitly[$resultingType]) :: $acc")
      case Nil => acc
    }

    val s = inner(types, q"Nil")
    q"Map($s :_*)"
  }

  /** Given a unary type constructor and a proper type will apply that
    * type to the constructor and return the result.
    * 
    * e.g. injectType(Either[?, Int], String) == Either[String, Int]
    */
  def injectType(
    higherKind: Type,
    inject: Type
  ): Type = {
    val (name: Symbol, newTypeParams: List[Type]) = higherKind match {
      case PolyType(param :: Nil, TypeRef(_,_,allParams)) => (
        higherKind.typeSymbol,
        allParams.map{ case TypeRef(_,`param`,_) => inject; case x => x }
      )
      case TypeRef(_,name,_) =>
        (name, List(inject))
      case bad =>
        c.abort(c.enclosingPosition, s"$bad is not of kind * -> *")
    }
    tq"${name}[..$newTypeParams]".toType
  }

  def injectType2(
    higherKind: Type,
    injectOne: Type,
    injectTwo: Type    
  ): Type = {
    val (name: Symbol, newTypeParams: List[Type]) = higherKind match {
      case TypeRef(_,name,_) =>
        (name, List(injectOne, injectTwo))
      case PolyType(paramA :: paramB :: Nil, TypeRef(_,_,allParams)) => (
        higherKind.typeSymbol,
        allParams.map{
          case TypeRef(_,`paramA`,_) => injectOne
          case TypeRef(_,`paramB`,_) => injectTwo
          case x => x
        }
      )
      case bad =>
        c.abort(c.enclosingPosition, s"$bad is not of kind * -> * -> *")
    }
    tq"${name}[..$newTypeParams]".toType
  }

  implicit class RichTree(t: Tree) {
    def toType: Type =
      c.typecheck(q"??? : $t").tpe    
  }

  // exists only for testing injectType
  def coldImplicit[A, TC[_]](
    implicit tta: WeakTypeTag[A],    
    ttn: WeakTypeTag[TC[_]]
  ): Expr[TC[A]] = {
    c.Expr(
      c.inferImplicitValue(injectType(ttn.tpe, tta.tpe), false)
    )
  }

  // E[A] => F[A] // where F is interpreted type (perhaps WebMonad)
  // Future[A] => WebMonad[A]
  /** for {
    *    a <- convert(callApiOne()) // Future[Int]
    *    b <- convert(callApiTwo()) // IO[String]
    * }
    */
  def getConvMap(fType : Type, eTypes: List[(Type, Type)]): Tree = {
    val mapElems = eTypes.map{
      case (e: Type, a: Type) =>
        q"(implicitly[izumi.reflect.TagK[${e}]].tag, implicitly[izumi.reflect.Tag[${a}]].tag) -> ((implicitly[ltbs.uniform.Converter[$e, $fType, $a]]): Any)"
      case (bad, _) =>
        c.abort(c.enclosingPosition, s"$bad is not of kind * -> * (${showRaw(bad)})")
    }
    q"Map( ..$mapElems )"
  }

  def getInteractMap(fType : Type, eTypes: List[(Type, Type)]): Tree = {
    val mapElems = eTypes.map{
      case (t: Type, a: Type) =>
        val key = q"(implicitly[izumi.reflect.Tag[${t}]].tag, implicitly[izumi.reflect.Tag[${a}]].tag)"
        val faType = injectType2(fType, t, a)
        val value = q"((implicitly[$faType]))"
        q"$key -> $value"
      case (bad, _) =>
        c.abort(c.enclosingPosition, s"$bad is not of kind * -> * (${showRaw(bad)})")
    }
    q"Map( ..$mapElems )"
  }

  def interpreter_impl[H <: Needs[_, _], A, INTERACTTC[_,_], ASKLISTTC[_], F[_], T](
    program: Expr[Uniform[H,T,A]]
  )(
    implicit ttn: WeakTypeTag[H], 
    ttInteractTc: WeakTypeTag[INTERACTTC[_,_]],
    ttAskListTc: WeakTypeTag[ASKLISTTC[_]],    
    fTypeTag: WeakTypeTag[F[_]]
  ): c.Expr[F[A]] = {
    val (interactTypes, convTypes, listTypes) = getNeeds[H, INTERACTTC, ASKLISTTC]
    val interactMap = getInteractMap(ttInteractTc.tpe, interactTypes)
    val listMap = implicitMaps(ttAskListTc.tpe, listTypes)    
    val convMap = getConvMap(fTypeTag.tpe, convTypes)
    val r = q"${c.prefix}.interpretImpl($program, $interactMap, $convMap, $listMap)"
    c.Expr[F[A]](r)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy