范围内的隐式参数不应该作为宏生成代码的隐式参数吗?
Shouldn't an implicit parameter in scope be available as an implicit argument for macro-generated code?
我正在尝试使生成类型类实例的现有宏适用于参数化类型,其中类型参数已经具有类型类的实例。我很惊讶它在这种情况下无法解析现有的(类型参数的)类型类,但这似乎是正在发生的事情。
我试图将其缩减为一个展示该行为的小示例。这是宏定义:
package test
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
trait Read[A] {
def read(in: String): A
}
object Read {
def CaseClassReadImpl[A: c.WeakTypeTag](c: Context): c.Expr[Read[A]] = {
import c.universe._
val aType = weakTypeOf[A]
val params = aType.decls.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
val paramList = params.map(param => q"Read.read[${param.typeSignature}](in)")
val src = q"""
new Read[$aType] {
def read(in: String) = ${aType.typeSymbol.companion}.apply(..$paramList)
}
"""
c.Expr[Read[A]](src)
}
def readFor[A]: Read[A] = macro CaseClassReadImpl[A]
def read[A](in: String)(implicit A: Read[A]): A = A.read(in)
}
这是练习它的代码:
package test
object MacroTest {
case class Foo[A](bar: A)
implicit def fooRead[A](implicit A: Read[A]): Read[Foo[A]] =
Read.readFor[Foo[A]]
}
我期待这会成功,认为生成的对 Read.read
的调用的隐式参数将解析为 fooRead
函数的隐式参数。相反,它在定义 fooRead
时失败:
Error:(7, 17) could not find implicit value for parameter A: test.Read[A]
Read.readFor[Foo[A]]
为什么不使用隐式参数A
到fooRead
?它在词法范围内。
我意识到我应该将此代码库转换为使用 shapeless 或类似的库,但现在我只是想尽可能少地让它工作。
更新:
我发现问题出在两个不同的 A
上。上面的错误使它看起来像所需的隐式与我在范围内的类型相同,但在稍微摆弄它并显式传递隐式之后,我设法得到这个(更有帮助的)错误消息:
Error: type mismatch;
found : A(in class Foo)
required: A(in method fooRead)
implicit def fooRead[A](implicit read: Read[A]): Read[Foo[A]] = Read.readFor[Foo[A]]
我仍然无法找到一种方法让 Scala 理解我希望 A
s 是相同的:传递给 fooRead
函数的那个。
尝试替换
param => q"Read.read[${param.typeSignature}](in)"
和
param => q"Read.read[${param.typeSignatureIn(aType)}](in)"
https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/api/Symbols.scala#L335-L345
/** @see [[infoIn]] */
def typeSignatureIn(site: Type): Type
/** The type signature of this symbol seen as a member of given type `site`.
*
* @group Basics
*/
def infoIn(site: Type): Type
/** @see [[info]] */
def typeSignature: Type
/** The type signature of this symbol.
*
* This method always returns signatures in the most generic way possible, even if the underlying symbol is obtained from an
* instantiation of a generic type. For example, signature
* of the method `def map[B](f: (A) ⇒ B): List[B]`, which refers to the type parameter `A` of the declaring class `List[A]`,
* will always feature `A`, regardless of whether `map` is loaded from the `List[_]` or from `List[Int]`. To get a signature
* with type parameters appropriately instantiated, one should use `infoIn`.
*
* @group Basics
*/
def info: Type
我正在尝试使生成类型类实例的现有宏适用于参数化类型,其中类型参数已经具有类型类的实例。我很惊讶它在这种情况下无法解析现有的(类型参数的)类型类,但这似乎是正在发生的事情。
我试图将其缩减为一个展示该行为的小示例。这是宏定义:
package test
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
trait Read[A] {
def read(in: String): A
}
object Read {
def CaseClassReadImpl[A: c.WeakTypeTag](c: Context): c.Expr[Read[A]] = {
import c.universe._
val aType = weakTypeOf[A]
val params = aType.decls.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
val paramList = params.map(param => q"Read.read[${param.typeSignature}](in)")
val src = q"""
new Read[$aType] {
def read(in: String) = ${aType.typeSymbol.companion}.apply(..$paramList)
}
"""
c.Expr[Read[A]](src)
}
def readFor[A]: Read[A] = macro CaseClassReadImpl[A]
def read[A](in: String)(implicit A: Read[A]): A = A.read(in)
}
这是练习它的代码:
package test
object MacroTest {
case class Foo[A](bar: A)
implicit def fooRead[A](implicit A: Read[A]): Read[Foo[A]] =
Read.readFor[Foo[A]]
}
我期待这会成功,认为生成的对 Read.read
的调用的隐式参数将解析为 fooRead
函数的隐式参数。相反,它在定义 fooRead
时失败:
Error:(7, 17) could not find implicit value for parameter A: test.Read[A]
Read.readFor[Foo[A]]
为什么不使用隐式参数A
到fooRead
?它在词法范围内。
我意识到我应该将此代码库转换为使用 shapeless 或类似的库,但现在我只是想尽可能少地让它工作。
更新:
我发现问题出在两个不同的 A
上。上面的错误使它看起来像所需的隐式与我在范围内的类型相同,但在稍微摆弄它并显式传递隐式之后,我设法得到这个(更有帮助的)错误消息:
Error: type mismatch;
found : A(in class Foo)
required: A(in method fooRead)
implicit def fooRead[A](implicit read: Read[A]): Read[Foo[A]] = Read.readFor[Foo[A]]
我仍然无法找到一种方法让 Scala 理解我希望 A
s 是相同的:传递给 fooRead
函数的那个。
尝试替换
param => q"Read.read[${param.typeSignature}](in)"
和
param => q"Read.read[${param.typeSignatureIn(aType)}](in)"
https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/api/Symbols.scala#L335-L345
/** @see [[infoIn]] */
def typeSignatureIn(site: Type): Type
/** The type signature of this symbol seen as a member of given type `site`.
*
* @group Basics
*/
def infoIn(site: Type): Type
/** @see [[info]] */
def typeSignature: Type
/** The type signature of this symbol.
*
* This method always returns signatures in the most generic way possible, even if the underlying symbol is obtained from an
* instantiation of a generic type. For example, signature
* of the method `def map[B](f: (A) ⇒ B): List[B]`, which refers to the type parameter `A` of the declaring class `List[A]`,
* will always feature `A`, regardless of whether `map` is loaded from the `List[_]` or from `List[Int]`. To get a signature
* with type parameters appropriately instantiated, one should use `infoIn`.
*
* @group Basics
*/
def info: Type