scala – 嵌套隐式宏似乎不适用于类型参数?
我正在编写一个
Scala隐式宏,它自动为case类生成一个类型类(使用quasiquote,Scala 2.10.3与宏天堂编译器插件和Scala 2.11.0-M7).
隐式宏递归地查找参数的类型类. 只要case类没有使用类型参数,或者类型参数在生成的代码中没有使用,它工作正常. 但是,一旦需要< TypeClass> [< Type>的TypeCarameter]的隐含值,则调用站点的编译失败,而“找不到参数e的隐式值”. 以下是重现问题的代码: trait TestTypeClass[A] { def str(x: A): String } object Test { implicit def BooleanTest = new TestTypeClass[Boolean] { def str(x: Boolean) = x.toString } def CaseClassTestImpl[A: c.WeakTypeTag](c: Context): c.Expr[TestTypeClass[A]] = { import c.universe._ val aType = weakTypeOf[A] val TestTypeClassType = weakTypeOf[TestTypeClass[_]] val typeName = aType.typeSymbol.name.decoded val params = aType.declarations.collectFirst { case m: MethodSymbol if m.isPrimaryConstructor => m }.get.paramss.head val paramTypes = aType.declarations.collectFirst { case m: MethodSymbol if m.isPrimaryConstructor => m }.get.paramss.head.map(_.typeSignature) val paramList = for (i <- 0 until params.size) yield { val param = params(i) val paramType = paramTypes(i) val paramName = param.name.decoded q"str($param)" } println(paramList) val src = q""" new TestTypeClass[$aType] { def str(x: $aType) = Seq(..$paramList).mkString(",") } """ c.Expr[TestTypeClass[A]](src) } implicit def CaseClassTest[A]: TestTypeClass[A] = macro CaseClassTestImpl[A] def str[A: TestTypeClass](x: A) = implicitly[TestTypeClass[A]].str(x) } // somewhere in other module implicitly[TestTypeClass[TestClass]] // OK. implicitly[TestTypeClass[TestClass2[Boolean]]] // Error // could not find implicit value for parameter e: TestTypeClass[TestClass2[Boolean]] implicitly[TestTypeClass[TestClass2[TestClass]]] // Error // could not find implicit value for parameter e: TestTypeClass[TestClass2[TestClass]] 是否这样设计,我做错了,还是编译错误? 解决方法
有一些表面层次的问题,应该阻止你的版本的工作,但一旦解决了你应该能够做到你想要的(无论是一个好主意还是不是另一个问题 – 我会在这个答案的结尾尝试解决).
这三个最大的问题是这样的: q"str($param)" 首先,在生成的代码的上下文中,str将引用你正在定义和实例化的匿名类的方法,而不是在Test上的str方法.接下来,这将生成看起来像str(member)的代码,但成员并不意味着生成代码的上下文中的任何内容 – 你想要像str(x.member)这样的东西.最后(和相关),每个参数将是一个构造函数参数,而不是访问器. 以下是一个完整的工作示例(在2.10.3上进行了测试): import scala.language.experimental.macros import scala.reflect.macros.Context trait TestTypeClass[A] { def str(x: A): String } object Test { implicit def BooleanTest = new TestTypeClass[Boolean] { def str(x: Boolean) = x.toString } def CaseClassTestImpl[A: c.WeakTypeTag]( c: Context ): c.Expr[TestTypeClass[A]] = { import c.universe._ val aType = weakTypeOf[A] val params = aType.declarations.collect { case m: MethodSymbol if m.isCaseAccessor => m }.toList val paramList = params.map(param => q"Test.str(x.$param)") val src = q""" new TestTypeClass[$aType] { def str(x: $aType) = Seq(..$paramList).mkString(",") } """ c.Expr[TestTypeClass[A]](src) } implicit def CaseClassTest[A]: TestTypeClass[A] = macro CaseClassTestImpl[A] def str[A: TestTypeClass](x: A) = implicitly[TestTypeClass[A]].str(x) } 然后一些演示设置: import Test._ case class Foo(x: Boolean,y: Boolean) case class Bar[A](a: A) 最后: scala> str(Bar(Foo(true,false))) res0: String = true,false 这表明编译器已经通过递归地应用宏成功地找到了Bar [Foo]的实例. 所以这种方法是有效的,但它也破坏了类型类提供的一些大的优点.基于运行时反射的解决方案来解决这类问题.当我们有一些只是把它们从空中拉出来的宏就会变得简单易懂.确定它能够找到的逻辑是埋在宏实现代码中 – 这将在编译时运行,所以它在某种意义上仍然是类型安全的,但是它的透明度较低. 这个实现也大大超过了生成实例(尝试str(1)),这可以很容易地被纠正,但这是一个很好的例证,这种东西可以是多么危险. 对于什么是值得的,以下是使用Shapeless 2.0的TypeClass类型类别的替代解决方案,由Miles上面提到(您也可以看到我的博客文章here进行类似的比较). implicit def BooleanTest = new TestTypeClass[Boolean] { def str(x: Boolean) = x.toString } def str[A: TestTypeClass](x: A) = implicitly[TestTypeClass[A]].str(x) import shapeless._ implicit object `TTC is a type class` extends ProductTypeClass[TestTypeClass] { def product[H,T <: HList](htc: TestTypeClass[H],ttc: TestTypeClass[T]) = new TestTypeClass[H :: T] { def str(x: H :: T) = { val hs = htc.str(x.head) val ts = ttc.str(x.tail) if (ts.isEmpty) hs else hs + "," + ts } } def emptyProduct = new TestTypeClass[HNil] { def str(x: HNil) = "" } def project[F,G](inst: => TestTypeClass[G],to: F => G,from: G => F) = new TestTypeClass[F] { def str(x: F) = inst.str(to(x)) } } object TestTypeClassHelper extends TypeClassCompanion[TestTypeClass] import TestTypeClassHelper.auto._ 这不是真的更简洁,但它更通用,更不可能做你不期望的事情.还有魔术发生,但是更容易控制和推理. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |