Skip to content

Instantly share code, notes, and snippets.

@lihaoyi
Last active November 5, 2017 17:02
Show Gist options
  • Save lihaoyi/1198b4e10b4aaba20d068553a9ea1024 to your computer and use it in GitHub Desktop.
Save lihaoyi/1198b4e10b4aaba20d068553a9ea1024 to your computer and use it in GitHub Desktop.
import scala.reflect.macros.blackbox.Context
import language.experimental.macros
object Applicative {
def apply[T](t: T): Option[T] = macro Applicative.impl[T]
def impl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Option[T]] = {
import c.universe._
def rec(t: Tree): Iterator[c.Tree] = Iterator(t) ++ t.children.flatMap(rec(_))
val bound = collection.mutable.Buffer.empty[(c.Tree, ValDef)]
val defsWithinMacro = rec(t.tree).filter(_.isDef).map(_.symbol).toSet
val transformed = c.internal.typingTransform(t.tree) {
case (t @ q"$fun.get", api) if fun.tpe <:< c.typeOf[Option[_]] =>
val defsWithinGet = rec(fun).filter(_.isDef).map(_.symbol).toSet
val used = rec(t)
val banned = used.filter(x =>
defsWithinMacro(x.symbol) && !defsWithinGet(x.symbol)
)
if (banned.hasNext){
val banned0 = banned.next()
c.abort(banned0.pos, "cannot apply() value defined within the T{...} block")
}
val tempName = c.freshName(TermName("tmp"))
val tempSym = c.internal.newTermSymbol(api.currentOwner, tempName)
c.internal.setInfo(tempSym, t.tpe)
val tempIdent = Ident(tempSym)
c.internal.setType(tempIdent, t.tpe)
bound.append((fun, c.internal.valDef(tempSym)))
tempIdent
case (t, api) => api.default(t)
}
val (exprs, bindings) = bound.unzip
c.Expr[Option[T]](q"${exprs(0)}.map{ (${c.untypecheck(bindings(0))}) => $transformed }")
}
}
@ Applicative{ (() => Some(1).get)() }
java.util.NoSuchElementException: value tmp$macro$1
at scala.collection.mutable.AnyRefMap$ExceptionDefault.apply(AnyRefMap.scala:425)
at scala.collection.mutable.AnyRefMap$ExceptionDefault.apply(AnyRefMap.scala:424)
at scala.collection.mutable.AnyRefMap.apply(AnyRefMap.scala:180)
at scala.tools.nsc.backend.jvm.BCodeSkelBuilder$PlainSkelBuilder$locals$.load(BCodeSkelBuilder.scala:389)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genLoad(BCodeBodyBuilder.scala:354)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.$anonfun$genLoadArguments$1(BCodeBodyBuilder.scala:935)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genLoadArguments(BCodeBodyBuilder.scala:935)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genApply(BCodeBodyBuilder.scala:628)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genLoad(BCodeBodyBuilder.scala:296)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genBlock(BCodeBodyBuilder.scala:813)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genLoad(BCodeBodyBuilder.scala:366)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.$anonfun$genLoadArguments$1(BCodeBodyBuilder.scala:935)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genLoadArguments(BCodeBodyBuilder.scala:935)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genApply(BCodeBodyBuilder.scala:665)
at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genLoad(BCodeBodyBuilder.scala:296)
at scala.tools.nsc.backend.jvm.BCodeSkelBuilder$PlainSkelBuilder.emitNormalMethodBody$1(BCodeSkelBuilder.scala:601)
at scala.tools.nsc.backend.jvm.BCodeSkelBuilder$PlainSkelBuilder.genDefDef(BCodeSkelBuilder.scala:633)
at scala.tools.nsc.backend.jvm.BCodeSkelBuilder$PlainSkelBuilder.gen(BCodeSkelBuilder.scala:507)
at scala.tools.nsc.backend.jvm.BCodeSkelBuilder$PlainSkelBuilder.$anonfun$gen$7(BCodeSkelBuilder.scala:509)
at scala.tools.nsc.backend.jvm.BCodeSkelBuilder$PlainSkelBuilder.gen(BCodeSkelBuilder.scala:509)
at scala.tools.nsc.backend.jvm.BCodeSkelBuilder$PlainSkelBuilder.genPlainClass(BCodeSkelBuilder.scala:110)
at scala.tools.nsc.backend.jvm.CodeGen.genClass(CodeGen.scala:56)
at scala.tools.nsc.backend.jvm.CodeGen.genClassDef$1(CodeGen.scala:26)
at scala.tools.nsc.backend.jvm.CodeGen.genClassDefs$1(CodeGen.scala:44)
at scala.tools.nsc.backend.jvm.CodeGen.$anonfun$genUnit$2(CodeGen.scala:43)
at scala.tools.nsc.backend.jvm.CodeGen.genClassDefs$1(CodeGen.scala:43)
at scala.tools.nsc.backend.jvm.CodeGen.$anonfun$genUnit$2(CodeGen.scala:43)
at scala.tools.nsc.backend.jvm.CodeGen.genClassDefs$1(CodeGen.scala:43)
at scala.tools.nsc.backend.jvm.CodeGen.genUnit(CodeGen.scala:47)
at scala.tools.nsc.backend.jvm.GenBCode$BCodePhase.$anonfun$apply$1(GenBCode.scala:43)
at scala.tools.nsc.backend.jvm.GenBCode$BCodePhase.apply(GenBCode.scala:43)
at scala.tools.nsc.Global$GlobalPhase.$anonfun$applyPhase$1(Global.scala:436)
at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:429)
at scala.tools.nsc.Global$GlobalPhase.$anonfun$run$1(Global.scala:400)
at scala.tools.nsc.Global$GlobalPhase.$anonfun$run$1$adapted(Global.scala:400)
at scala.collection.Iterator.foreach(Iterator.scala:929)
at scala.collection.Iterator.foreach$(Iterator.scala:929)
at scala.collection.AbstractIterator.foreach(Iterator.scala:1417)
at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:400)
at scala.tools.nsc.backend.jvm.GenBCode$BCodePhase.super$run(GenBCode.scala:53)
at scala.tools.nsc.backend.jvm.GenBCode$BCodePhase.$anonfun$run$1(GenBCode.scala:53)
at scala.tools.nsc.backend.jvm.GenBCode$BCodePhase.run(GenBCode.scala:51)
at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1452)
at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1436)
at scala.tools.nsc.Global$Run.compileSources(Global.scala:1429)
at scala.tools.nsc.Global$Run.compileFiles(Global.scala:1530)
at ammonite.interp.Compiler$$anon$2.compile(Compiler.scala:320)
at ammonite.interp.CompilerLifecycleManager.compileClass(CompilerLifecycleManager.scala:127)
at ammonite.interp.Interpreter.$anonfun$evaluateLine$1(Interpreter.scala:246)
at ammonite.util.Catching.flatMap(Res.scala:114)
at ammonite.interp.Interpreter.evaluateLine(Interpreter.scala:242)
at ammonite.interp.Interpreter.$anonfun$processLine$5(Interpreter.scala:229)
at ammonite.util.Res$Success.flatMap(Res.scala:61)
at ammonite.interp.Interpreter.$anonfun$processLine$3(Interpreter.scala:214)
at ammonite.util.Res$Success.flatMap(Res.scala:61)
at ammonite.interp.Interpreter.$anonfun$processLine$1(Interpreter.scala:208)
at ammonite.util.Catching.flatMap(Res.scala:114)
at ammonite.interp.Interpreter.processLine(Interpreter.scala:207)
at ammonite.repl.Repl.$anonfun$action$7(Repl.scala:161)
at ammonite.repl.Scoped.$anonfun$flatMap$1(Signaller.scala:43)
at ammonite.repl.Signaller.apply(Signaller.scala:28)
at ammonite.repl.Scoped.flatMap(Signaller.scala:43)
at ammonite.repl.Scoped.flatMap$(Signaller.scala:43)
at ammonite.repl.Signaller.flatMap(Signaller.scala:11)
at ammonite.repl.Repl.$anonfun$action$5(Repl.scala:153)
at ammonite.util.Res$Success.flatMap(Res.scala:61)
at ammonite.repl.Repl.$anonfun$action$1(Repl.scala:140)
at ammonite.util.Catching.flatMap(Res.scala:114)
at ammonite.repl.Repl.action(Repl.scala:132)
at ammonite.repl.Repl.loop$1(Repl.scala:172)
at ammonite.repl.Repl.run(Repl.scala:188)
at ammonite.Main.$anonfun$run$2(Main.scala:194)
at scala.Option.getOrElse(Option.scala:121)
at ammonite.Main.run(Main.scala:181)
at ammonite.MainRunner.$anonfun$runRepl$1(Main.scala:363)
at ammonite.MainRunner.watchLoop(Main.scala:344)
at ammonite.MainRunner.runRepl(Main.scala:363)
at ammonite.Main$.main0(Main.scala:280)
at ammonite.Main$.main(Main.scala:244)
at ammonite.Main.main(Main.scala)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment