I would like the following code works:
@main
def test1(): Unit = {
val a = Some("hello")
val b = a.!.length
val c = a.!.toUpperCase
println(b)
println(c)
}
I can implements the ! as:
import scala.language.dynamics
implicit final class Boxed[T](val a: T) extends AnyVal, Dynamic:
inline transparent def selectDynamic(inline name: String): Any = ${ BoxedMacro.selectDynamicImpl[T]('{ a }, '{ name } )}
opaque type OpaueBox[T] = T
extension [T](a: OpaueBox[T])
inline def selectDynamic(inline name: String): Any = ${ BoxedMacro.selectDynamicImpl[T]('{ a }, '{ name } )}
object BoxedMacro:
import scala.quoted.*
// a demo implementation for fast test
def selectDynamicImpl[T: Type](container: Expr[T], selector: Expr[String])(using Quotes): Expr[Any] =
import quotes.reflect.*
// container.map(_.selector)
val container2: Expr[Option[String]] = container.asExprOf[Option[String]]
selector.value match
case Some("length") =>
'{ $container2.map(x => x.length) }
case Some("toUpperCase") =>
'{ $container2.map(x => x.toUpperCase) }
case Some(name@_) =>
report.error(s"selector ${name} Not found")
'{ None }
case None =>
report.error(s"selector is not a constant")
'{ None }
extension [T](a: Option[T])
inline def ! : Boxed[Option[T]] = Boxed(a) // this works
// inline def ! : OpaueBox[Option[T]] = a // this does not work
by using the AnyVal implicit, it works, but the generate byte code is something complex, it called a empty Boxed method and checkcast multi times.
12: aload_1
13: invokevirtual #51 // Method Main$package$.Boxed:(Ljava/lang/Object;)Ljava/lang/Object;
16: checkcast #53 // class scala/Option
19: astore_3
20: aload_3
21: checkcast #53 // class scala/Option
// $3 = $1, so bytecode 12..21 is redundancy code
24: invokedynamic #72, 0 // InvokeDynamic #0:apply:()Lscala/Function1;
29: invokevirtual #76 // Method scala/Option.map:(Lscala/Function1;)Lscala/Option;
32: astore_2
I world like to using the opaque type which has no runtime cost, but it looks the extension not works with Dynamic method(selectDynamic).
So, My question is: Can Scala3 support extension methods works with Dynamic trait? If it can’t, Why it can’t?