Created
March 11, 2025 21:37
-
-
Save Ichoran/c31b57cd8be0ff4aada46c8eb2f84a55 to your computer and use it in GitHub Desktop.
Macro solution to context function default argument evaluation
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
/> using scala 3.6.4 | |
package examplecontextmacro | |
object DoNotUse { | |
def __do_not_use__ : Nothing = throw new Exception("Do not use!") | |
} | |
object MacroImpl { | |
import scala.quoted.* | |
inline val debug = false | |
inline def print(s: String): Unit = | |
inline if debug then println(s) | |
else () | |
inline def containsDoNotUse(inline arg: Any): Boolean = | |
${ containsDoNotUseImpl('arg) } | |
private class CheckDoNotUse(using Quotes) { | |
import quotes.reflect.* | |
private def traverseStatement(statement: Statement): Boolean = statement match | |
case term: Term => { print("term statement"); traverseTree(term) } | |
case definition: Definition => { print("def statement"); traverseDefinition(definition) } | |
private def traverseDefinition(definition: Definition): Boolean = definition match | |
case DefDef(_, _, _, Some(rhs)) => { print("def def"); traverseTree(rhs) } | |
case ValDef(_, _, Some(rhs)) => { print("val def"); traverseTree(rhs) } | |
case _ => { print("??? def"); false } | |
private def traverseTree(term: Term): Boolean = term match | |
case Ident(name) if name == "__do_not_use__" => true | |
case Apply(Ident(name), _) if name == "modify" => false | |
case Select(_, name) if name == "__do_not_use__" => true | |
case Select(_, name) if name == "modify" => false | |
case Inlined(_, _, inner) => { print("inline"); traverseTree(inner) } | |
case Block(stats, expr) => { print("block"); stats.exists(traverseStatement) || traverseTree(expr) } | |
case Typed(inner, _) => { print("typed"); traverseTree(inner) } | |
case Closure(inner, _) => { print("closure"); traverseTree(inner) } | |
case Apply(fun, args) => { print("apply"); traverseTree(fun) || args.exists(traverseTree) } | |
case Select(term, name) => { print("select"); traverseTree(term) } | |
case TypeApply(fun, _) => { print("typeapply"); traverseTree(fun) } | |
case _ => { print("??? = " + term.show(using Printer.TreeStructure)); false } | |
def containsDoNotUseImpl(argExpr: Expr[Any]): Expr[Boolean] = | |
import quotes.reflect.* | |
print(argExpr.asTerm.show(using Printer.TreeStructure)) | |
Expr(traverseTree(argExpr.asTerm)) | |
} | |
def containsDoNotUseImpl(argExpr: Expr[Any])(using Quotes): Expr[Boolean] = | |
CheckDoNotUse().containsDoNotUseImpl(argExpr) | |
} |
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
//> using scala 3.6.4 | |
//> mainClass examplecontextmacro.Main | |
package examplecontextmacro | |
case class Foo(id: Int) { | |
import DoNotUse.* | |
var f: (x: Int) ?=> Int = x + 3 | |
var g: (y: String) ?=> String = y.reverse | |
var h: (x: Double) ?=> Double = x * 10.0 | |
inline def chkf(inline new_f: () => Int): Boolean = !MacroImpl.containsDoNotUse(new_f) | |
inline def chkg(inline new_g: () => String): Boolean = !MacroImpl.containsDoNotUse(new_g) | |
inline def chkh(inline new_h: () => Double): Boolean = !MacroImpl.containsDoNotUse(new_h) | |
inline def modify( | |
ignore: Null = null, | |
inline f: (x: Int) ?=> Int = __do_not_use__, | |
inline g: (y: String) ?=> String = __do_not_use__, | |
inline h: (x: Double) ?=> Double = __do_not_use__ | |
): Unit = | |
inline if chkf(() => f(using {println("Hi"); 0})) then | |
this.f = f | |
inline if chkg(() => g(using "")) then | |
this.g = g | |
inline if chkh(() => h(using 0.0)) then | |
this.h = h | |
} | |
object Main { | |
def main(args: Array[String]): Unit = | |
val foo = Foo(3) | |
val fue = Foo(8) | |
println(foo.f(using 2)) | |
foo.modify() | |
println(foo.f(using 2)) | |
foo.modify(f = x + 5) | |
println(foo.f(using 2)) | |
println(foo.g(using "eel")) | |
foo.modify( | |
g = y + y, | |
h = x + { fue.modify(f = x + 5); fue.f(using 3) } | |
) | |
println(foo.g(using "eel")) | |
println(foo.f(using 2)) | |
println(foo.h(using 2.5)) | |
println(fue.g(using "salmon")) | |
println(fue.h(using 0.5)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment