Scala 宏生成的依赖类型似乎“不起作用" [英] Dependent type seems to “not work” when generated by Scala macro
问题描述
为handwavey 标题道歉.我不完全确定如何简洁地表达这个问题,因为我以前从未遇到过这样的事情.
Apologies for the handwavey title. I’m not entirely sure how to phrase the question succinctly, since I’ve never encountered something like this before.
我有以下特征,其中 U
类型用于保存 无形可扩展记录 类型:
I have the following trait, where the type U
is meant to hold a Shapeless extensible record type:
trait Flattened[T] {
type U <: shapeless.HList
def fields: U
}
我正在使用黑盒宏(出于本问题范围之外的原因)来创建 trait 的新实例:
I’m using a blackbox macro (for reasons outside the scope of this question) to create new instances of the trait:
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
的类型不再是一个可扩展的记录:
The problem is, when I use this macro to create a new instance of Flattened
, the type of fields
is no longer an extensible record:
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
奇怪的是,如果我手动执行宏所做的工作,它会起作用:
Oddly, if I do by hand what the macro does, it works:
// 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
为什么会出现这种差异,我如何才能使宏版本与手动版本的行为相同?
推荐答案
您的宏可能没有问题.这是类型签名:
Nothing's wrong with your macro, probably. It's the type signatures:
def apply[T](struct: T): Flattened[T] = macro impl[T]
def impl[T : c.WeakTypeTag](c: Context)(struct: c.Tree): c.Tree
您正在使用黑盒宏,并且根据 文档,黑盒宏对其签名是真实的.也就是说,即使 impl
产生一个 Flattened[T] { type U = ... }
,它是一个黑盒宏的事实意味着 scalac
总是把它包裹在 (_: Flattened[T])
中,忘记了细化中 U
的定义.使其成为白盒宏:
You are using a blackbox macro, and, according to the documentation, blackbox macros are true to their signatures. That is, even though impl
produces a Flattened[T] { type U = ... }
, the fact that it is a blackbox macro means that scalac
always wraps it in (_: Flattened[T])
, forgetting the definition of U
in the refinement. Make it a whitebox macro:
// 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
这篇关于Scala 宏生成的依赖类型似乎“不起作用"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!