由 Scala 宏生成时,依赖类型似乎“不起作用”
Dependent type seems to “not work” when generated by Scala macro
抱歉标题太粗俗了。我不太确定如何简洁地表达这个问题,因为我以前从未遇到过这样的事情。
背景资料:
我有以下特征,其中类型 U
是为了容纳 Shapeless extensible record 类型:
trait Flattened[T] {
type U <: shapeless.HList
def fields: U
}
我正在使用黑盒宏(出于本问题范围之外的原因)来创建特征的新实例:
object flatten {
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
def apply[T](struct: T): Flattened[T] =
macro impl[T]
def impl[T : c.WeakTypeTag](c: Context)(struct: c.Tree): c.Tree = {
import c.universe._
// AST representing a Shapeless extensible record (actual
// implementation uses values in `struct` to construct this
// AST, but I've simplified it for this example).
val fields: Tree = q"""("a" ->> 1) :: ("b" ->> "two") :: ("c" ->> true) :: HNil"""
// Result type of the above AST
val tpe: TypeTree = TypeTree(c.typecheck(q"$fields").tpe)
q"""
new Flattened[${weakTypeOf[T]}] {
import shapeless._
import syntax.singleton._
import record._
type U = $tpe
val fields = $fields
}
"""
}
}
问题:
问题是,当我使用此宏创建 Flattened
的新实例时,fields
的类型不再是可扩展记录:
import shapeless._
import syntax.singleton._
import record._
val s = "some value... it doesn't matter for this example, since it isn't used. I'm just putting something here so the example compiles and runs in a REPL."
val t = flatten(s)
val fields = t.fields
// fields: t.U = 1 :: "two" :: true :: HNil
fields("a") // compile error!
// The compile error is:
// cmd5.sc:1: No field String("a") in record ammonite.$sess.cmd4.t.U
// val res5 = fields("a")
// ^
// Compilation Failed
旁注:
奇怪的是,如果我手动执行宏的操作,它会起作用:
// I can't actually instantiate a new `Flattened[_]` manually, since
// defining the type `U` would be convoluted (not even sure it can be
// done), so an object with the same field is the next best thing.
object Foo {
import shapeless._
import syntax.singleton._
import record._
val fields = ("a" ->> 1) :: ("b" ->> "two") :: ("c" ->> true) :: HNil
}
val a = Foo.fields("a")
// a: Int = 1
val b = Foo.fields("b")
// b: String = "two"
val c = Foo.fields("c")
// c: Boolean = true
为什么会出现这种差异,如何让宏版本的行为与手动版本相同?
你的宏应该没有问题。这是类型签名:
def apply[T](struct: T): Flattened[T] = macro impl[T]
def impl[T : c.WeakTypeTag](c: Context)(struct: c.Tree): c.Tree
您正在使用黑盒宏,并且根据 the documentation,黑盒宏符合其签名。也就是说,即使 impl
产生一个 Flattened[T] { type U = ... }
,它是一个黑盒宏这一事实意味着 scalac
总是将它包装在 (_: Flattened[T])
中,而忘记了 [=16] 的定义=] 在细化中。将其设为白盒宏:
// import scala.reflect.macros.blackbox.context // NO!
import scala.reflect.macros.whitebox.context
def impl[T: c.WeakTypeTag](c: Context)(struct: c.Tree): c.Tree
抱歉标题太粗俗了。我不太确定如何简洁地表达这个问题,因为我以前从未遇到过这样的事情。
背景资料:
我有以下特征,其中类型 U
是为了容纳 Shapeless extensible record 类型:
trait Flattened[T] {
type U <: shapeless.HList
def fields: U
}
我正在使用黑盒宏(出于本问题范围之外的原因)来创建特征的新实例:
object flatten {
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
def apply[T](struct: T): Flattened[T] =
macro impl[T]
def impl[T : c.WeakTypeTag](c: Context)(struct: c.Tree): c.Tree = {
import c.universe._
// AST representing a Shapeless extensible record (actual
// implementation uses values in `struct` to construct this
// AST, but I've simplified it for this example).
val fields: Tree = q"""("a" ->> 1) :: ("b" ->> "two") :: ("c" ->> true) :: HNil"""
// Result type of the above AST
val tpe: TypeTree = TypeTree(c.typecheck(q"$fields").tpe)
q"""
new Flattened[${weakTypeOf[T]}] {
import shapeless._
import syntax.singleton._
import record._
type U = $tpe
val fields = $fields
}
"""
}
}
问题:
问题是,当我使用此宏创建 Flattened
的新实例时,fields
的类型不再是可扩展记录:
import shapeless._
import syntax.singleton._
import record._
val s = "some value... it doesn't matter for this example, since it isn't used. I'm just putting something here so the example compiles and runs in a REPL."
val t = flatten(s)
val fields = t.fields
// fields: t.U = 1 :: "two" :: true :: HNil
fields("a") // compile error!
// The compile error is:
// cmd5.sc:1: No field String("a") in record ammonite.$sess.cmd4.t.U
// val res5 = fields("a")
// ^
// Compilation Failed
旁注:
奇怪的是,如果我手动执行宏的操作,它会起作用:
// I can't actually instantiate a new `Flattened[_]` manually, since
// defining the type `U` would be convoluted (not even sure it can be
// done), so an object with the same field is the next best thing.
object Foo {
import shapeless._
import syntax.singleton._
import record._
val fields = ("a" ->> 1) :: ("b" ->> "two") :: ("c" ->> true) :: HNil
}
val a = Foo.fields("a")
// a: Int = 1
val b = Foo.fields("b")
// b: String = "two"
val c = Foo.fields("c")
// c: Boolean = true
为什么会出现这种差异,如何让宏版本的行为与手动版本相同?
你的宏应该没有问题。这是类型签名:
def apply[T](struct: T): Flattened[T] = macro impl[T]
def impl[T : c.WeakTypeTag](c: Context)(struct: c.Tree): c.Tree
您正在使用黑盒宏,并且根据 the documentation,黑盒宏符合其签名。也就是说,即使 impl
产生一个 Flattened[T] { type U = ... }
,它是一个黑盒宏这一事实意味着 scalac
总是将它包装在 (_: Flattened[T])
中,而忘记了 [=16] 的定义=] 在细化中。将其设为白盒宏:
// import scala.reflect.macros.blackbox.context // NO!
import scala.reflect.macros.whitebox.context
def impl[T: c.WeakTypeTag](c: Context)(struct: c.Tree): c.Tree