summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2017-11-09 04:56:36 -0800
committerLi Haoyi <haoyi.sg@gmail.com>2017-11-09 04:56:36 -0800
commitfd545a9ae9f41a4bfdc2bf13a4554943f206f92b (patch)
treebfb76d137529f95026d4b576cd9bef154dabe531 /core
parentcdecfeedc1900aff0c4acfd284b07bf36087c3c9 (diff)
downloadmill-fd545a9ae9f41a4bfdc2bf13a4554943f206f92b.tar.gz
mill-fd545a9ae9f41a4bfdc2bf13a4554943f206f92b.tar.bz2
mill-fd545a9ae9f41a4bfdc2bf13a4554943f206f92b.zip
Make `Applicative` macros able to inject a configurable `Ctx` object, used in `Target` to inject the `dest` folder for the `T{...}` block to use
Diffstat (limited to 'core')
-rw-r--r--core/src/main/scala/forge/define/Applicative.scala51
-rw-r--r--core/src/main/scala/forge/define/Target.scala24
-rw-r--r--core/src/test/scala/forge/ApplicativeTests.scala10
3 files changed, 60 insertions, 25 deletions
diff --git a/core/src/main/scala/forge/define/Applicative.scala b/core/src/main/scala/forge/define/Applicative.scala
index dfa0cf84..b2034d13 100644
--- a/core/src/main/scala/forge/define/Applicative.scala
+++ b/core/src/main/scala/forge/define/Applicative.scala
@@ -6,28 +6,39 @@ import scala.annotation.compileTimeOnly
import scala.collection.mutable
import scala.reflect.macros.blackbox.Context
+/**
+ * A generic Applicative-functor macro: translates calls to
+ *
+ * Applier.apply{ ... applyable1.apply() ... applyable2.apply() ... }
+ *
+ * into
+ *
+ * Applier.zipMap(applyable1, applyable2){ (a1, a2, ctx) => ... a1 ... a2 ... }
+ */
object Applicative {
trait Applyable[+T]{
@compileTimeOnly("Target#apply() can only be used with a T{...} block")
def apply(): T = ???
}
- trait Applyer[W[_], T[_]]{
+ trait Applyer[W[_], T[_], Ctx]{
+ @compileTimeOnly("Target.ctx() can only be used with a T{...} block")
+ def ctx(): Ctx = ???
def underlying[A](v: W[A]): T[_]
- def map[A, B](a: T[A], f: A => B): T[B]
- def zipMap[R]()(f: () => R) = map(zip(), (_: Unit) => f())
- def zipMap[A, R](a: T[A])(f: A => R) = map(a, f)
- def zipMap[A, B, R](a: T[A], b: T[B])(f: (A, B) => R) = map(zip(a, b), f.tupled)
+ def mapCtx[A, B](a: T[A])(f: (A, Ctx) => B): T[B]
+ def zipMap[R]()(cb: Ctx => R) = mapCtx(zip()){ (_, ctx) => cb(ctx)}
+ def zipMap[A, R](a: T[A])(f: (A, Ctx) => R) = mapCtx(a)(f)
+ def zipMap[A, B, R](a: T[A], b: T[B])(cb: (A, B, Ctx) => R) = mapCtx(zip(a, b)){case ((a, b), x) => cb(a, b, x)}
def zipMap[A, B, C, R](a: T[A], b: T[B], c: T[C])
- (f: (A, B, C) => R) = map(zip(a, b, c), f.tupled)
+ (cb: (A, B, C, Ctx) => R) = mapCtx(zip(a, b, c)){case ((a, b, c), x) => cb(a, b, c, x)}
def zipMap[A, B, C, D, R](a: T[A], b: T[B], c: T[C], d: T[D])
- (f: (A, B, C, D) => R) = map(zip(a, b, c, d), f.tupled)
+ (cb: (A, B, C, D, Ctx) => R) = mapCtx(zip(a, b, c, d)){case ((a, b, c, d), x) => cb(a, b, c, d, x)}
def zipMap[A, B, C, D, E, R](a: T[A], b: T[B], c: T[C], d: T[D], e: T[E])
- (f: (A, B, C, D, E) => R) = map(zip(a, b, c, d, e), f.tupled)
+ (cb: (A, B, C, D, E, Ctx) => R) = mapCtx(zip(a, b, c, d, e)){case ((a, b, c, d, e), x) => cb(a, b, c, d, e, x)}
def zipMap[A, B, C, D, E, F, R](a: T[A], b: T[B], c: T[C], d: T[D], e: T[E], f: T[F])
- (cb: (A, B, C, D, E, F) => R) = map(zip(a, b, c, d, e, f), cb.tupled)
+ (cb: (A, B, C, D, E, F, Ctx) => R) = mapCtx(zip(a, b, c, d, e, f)){case ((a, b, c, d, e, f), x) => cb(a, b, c, d, e, f, x)}
def zipMap[A, B, C, D, E, F, G, R](a: T[A], b: T[B], c: T[C], d: T[D], e: T[E], f: T[F], g: T[G])
- (cb: (A, B, C, D, E, F, G) => R) = map(zip(a, b, c, d, e, f, g), cb.tupled)
+ (cb: (A, B, C, D, E, F, G, Ctx) => R) = mapCtx(zip(a, b, c, d, e, f, g)){case ((a, b, c, d, e, f, g), x) => cb(a, b, c, d, e, f, g, x)}
def zip(): T[Unit]
def zip[A](a: T[A]): T[Tuple1[A]]
def zip[A, B](a: T[A], b: T[B]): T[(A, B)]
@@ -38,8 +49,8 @@ object Applicative {
def zip[A, B, C, D, E, F, G](a: T[A], b: T[B], c: T[C], d: T[D], e: T[E], f: T[F], g: T[G]): T[(A, B, C, D, E, F, G)]
}
- def impl[M[_], T: c.WeakTypeTag](c: Context)
- (t: c.Expr[T]): c.Expr[M[T]] = {
+ def impl[M[_], T: c.WeakTypeTag, Ctx: c.WeakTypeTag](c: Context)
+ (t: c.Expr[T]): c.Expr[M[T]] = {
import c.universe._
def rec(t: Tree): Iterator[c.Tree] = Iterator(t) ++ t.children.flatMap(rec(_))
@@ -50,6 +61,10 @@ object Applicative {
// https://gist.github.com/olafurpg/596d62f87bf3360a29488b725fbc7608
val defs = rec(t.tree).filter(_.isDef).map(_.symbol).toSet
+ val ctxName = TermName(c.freshName("ctx"))
+ val ctxSym = c.internal.newTermSymbol(c.internal.enclosingOwner, ctxName)
+ c.internal.setInfo(ctxSym, weakTypeOf[Ctx])
+
val transformed = c.internal.typingTransform(t.tree) {
case (t @ q"$fun.apply()", api) if t.symbol == targetApplySym =>
@@ -71,13 +86,23 @@ object Applicative {
c.internal.setFlag(tempSym, (1L << 44).asInstanceOf[c.universe.FlagSet])
bound.append((q"${c.prefix}.underlying($fun)", c.internal.valDef(tempSym)))
tempIdent
+ case (t @ q"$prefix.ctx()", api)
+ if prefix.tpe.baseClasses.exists(_.fullName == "forge.define.Applicative.Applyer") =>
+
+ val tempIdent = Ident(ctxSym)
+ c.internal.setType(tempIdent, t.tpe)
+ c.internal.setFlag(ctxSym, (1L << 44).asInstanceOf[c.universe.FlagSet])
+ tempIdent
+
case (t, api) => api.default(t)
}
val (exprs, bindings) = bound.unzip
- val callback = c.typecheck(q"(..$bindings) => $transformed ")
+ val ctxBinding = c.internal.valDef(ctxSym)
+
+ val callback = c.typecheck(q"(..$bindings, $ctxBinding) => $transformed ")
val res = q"${c.prefix}.zipMap(..$exprs){ $callback }"
diff --git a/core/src/main/scala/forge/define/Target.scala b/core/src/main/scala/forge/define/Target.scala
index 9cb2dd1a..667fe467 100644
--- a/core/src/main/scala/forge/define/Target.scala
+++ b/core/src/main/scala/forge/define/Target.scala
@@ -29,7 +29,7 @@ abstract class Target[+T] extends Target.Ops[T] with Applyable[T]{
}
-object Target extends Applicative.Applyer[Target, Target]{
+object Target extends Applicative.Applyer[Target, Target, Args]{
def underlying[A](v: Target[A]) = v
type Cacher = forge.define.Cacher[Target[_]]
@@ -39,18 +39,20 @@ object Target extends Applicative.Applyer[Target, Target]{
def evaluate(args: Args) = t0
}
def apply[T](t: Target[T]): Target[T] = macro forge.define.Cacher.impl0[Target, T]
- def command[T](t: T): Target[T] = macro Applicative.impl[Target, T]
- def apply[T](t: T): Target[T] = macro impl[Target, T]
- def impl[M[_], T: c.WeakTypeTag](c: Context)
- (t: c.Expr[T])
- (implicit tt: c.WeakTypeTag[M[_]]): c.Expr[M[T]] = {
- forge.define.Cacher.wrapCached(c)(
- Applicative.impl(c)(t)
+ def command[T](t: T): Target[T] = macro Applicative.impl[Target, T, Args]
+ def apply[T](t: T): Target[T] = macro impl[Target, T, Args]
+ def impl[M[_], T: c.WeakTypeTag, Ctx: c.WeakTypeTag]
+ (c: Context)
+ (t: c.Expr[T])
+ (implicit tt: c.WeakTypeTag[M[_]]): c.Expr[M[T]] = {
+ forge.define.Cacher.wrapCached[M, T](c)(
+ Applicative.impl[M, T, Ctx](c)(t)
)
}
abstract class Ops[+T]{ this: Target[T] =>
def map[V](f: T => V) = new Target.Mapped(this, f)
+ def mapDest[V](f: (T, Args) => V) = new Target.MappedDest(this, f)
def filter(f: T => Boolean) = this
def withFilter(f: T => Boolean) = this
@@ -72,6 +74,10 @@ object Target extends Applicative.Applyer[Target, Target]{
def evaluate(args: Args) = f(args(0))
val inputs = List(source)
}
+ class MappedDest[+T, +V](source: Target[T], f: (T, Args) => V) extends Target[V]{
+ def evaluate(args: Args) = f(args(0), args)
+ val inputs = List(source)
+ }
class Zipped[+T, +V](source1: Target[T], source2: Target[V]) extends Target[(T, V)]{
def evaluate(args: Args) = (args(0), args(1))
val inputs = List(source1, source2)
@@ -106,7 +112,7 @@ object Target extends Applicative.Applyer[Target, Target]{
}
}
- def map[A, B](t: Target[A], f: A => B) = t.map(f)
+ def mapCtx[A, B](t: Target[A])(f: (A, Args) => B) = t.mapDest(f)
def zip() = new Target.Target0(())
def zip[A](a: Target[A]) = a.map(Tuple1(_))
def zip[A, B](a: Target[A], b: Target[B]) = a.zip(b)
diff --git a/core/src/test/scala/forge/ApplicativeTests.scala b/core/src/test/scala/forge/ApplicativeTests.scala
index 1da21a5c..682414d3 100644
--- a/core/src/test/scala/forge/ApplicativeTests.scala
+++ b/core/src/test/scala/forge/ApplicativeTests.scala
@@ -7,13 +7,14 @@ import language.experimental.macros
object ApplicativeTests extends TestSuite {
implicit def optionToOpt[T](o: Option[T]): Opt[T] = new Opt(o)
class Opt[T](val o: Option[T]) extends Applicative.Applyable[T]
- object Opt extends define.Applicative.Applyer[Opt, Option]{
+ object Opt extends define.Applicative.Applyer[Opt, Option, String]{
+ val injectedCtx = "helloooo"
def underlying[A](v: Opt[A]) = v.o
- def apply[T](t: T): Option[T] = macro Applicative.impl[Option, T]
+ def apply[T](t: T): Option[T] = macro Applicative.impl[Option, T, String]
type O[+T] = Option[T]
- def map[A, B](a: O[A], f: A => B) = a.map(f)
+ def mapCtx[A, B](a: O[A])(f: (A, String) => B): Option[B] = a.map(f(_, injectedCtx))
def zip() = Some(())
def zip[A](a: O[A]) = a.map(Tuple1(_))
def zip[A, B](a: O[A], b: O[B]) = {
@@ -53,6 +54,9 @@ object ApplicativeTests extends TestSuite {
'singleNone - assert(Opt("lol " + None()) == None)
'twoNones - assert(Opt("lol " + None() + None()) == None)
}
+ 'context - {
+ assert(Opt(Opt.ctx() + Some("World")()) == Some("hellooooWorld"))
+ }
'capturing - {
val lol = "lol "
def hell(o: String) = "hell" + o