[英]How to obtain a tree for a higher-kinded type parameter in a scala macro
我正在尝试编写一个宏来简化一些与monad相关的代码(我正在为Monads使用cat 1.6.0)。 现在我只想写出lift[F](a)
其中F
是一元型构造函数,并将其扩展为a.pure[F]
。 看起来很简单,但我不能让它工作。
现在我有这个代码来帮助进行类型推断:
object Macros {
class LiftPartiallyApplied[F[_]] {
def apply[A](a: A): F[A] = macro MacroImpl.liftImpl[F, A]
}
def lift[F[_]] = new LiftPartiallyApplied[F]
}
而对于宏的实际实现:
object MacroImpl {
def liftImpl[F[_], A](c: blackbox.Context)(a: c.Tree)(implicit tt: c.WeakTypeTag[F[_]]): c.Tree = {
import c.universe._
q"$a.pure[${tt.tpe.typeConstructor}]"
}
}
现在我可以像这个lift[List](42)
一样调用宏lift[List](42)
并且它会扩展到42.pure[List]
,很棒。 但是当我用更复杂的类型调用它时,例如lift[({type F[A] = Either[String, A]})#F](42)
,它将扩展为42.pure[Either]
,显然是坏了,因为Either
是二进制类型的构造函数而不是一元的构造函数。 问题是我只是不知道放在那里而不是${tt.tpe.typeConstructor}
...
//编辑:由于人们显然无法重现问题,我已经建立了一个完整的存储库: https : //github.com/mberndt123/macro-experiment我现在将尝试找出Dmytro和我自己项目之间的区别是。
不要将Main
和Macros
放在同一个编译单元中。
但是当我用更复杂的类型调用它时,例如
lift[({type F[A] = Either[String, A]})#F](42)
,它将扩展为42.pure[Either]
无法重现。
对我来说, lift[List](42)
产生(使用scalacOptions += "-Ymacro-debug-lite"
)
Warning:scalac: 42.pure[List]
TypeApply(Select(Literal(Constant(42)), TermName("pure")), List(TypeTree()))
在编译时和运行时的List(42)
。
lift[({ type F[A] = Either[String, A] })#F](42)
产生
Warning:scalac: 42.pure[[A]scala.util.Either[String,A]]
TypeApply(Select(Literal(Constant(42)), TermName("pure")), List(TypeTree()))
在编译时和在运行时Right(42)
。
这是我的项目https://gist.github.com/DmytroMitin/334c230a4f2f1fd3fe9e7e5a3bb10df5
你为什么需要宏? 你为什么不写
import cats.Applicative
import cats.syntax.applicative._
class LiftPartiallyApplied[F[_]: Applicative] {
def apply[A](a: A): F[A] = a.pure[F]
}
def lift[F[_]: Applicative] = new LiftPartiallyApplied[F]
?
好吧,我发现了问题所在。
宏需要与其使用站点分开编译。 我认为这意味着Macros
需要与MacroImpl
分开编译,所以我将它们放在单独的sbt子项目中,并在项目中调用Macros
来定义Macros
。 但它实际上意味着宏的调用需要与其定义分开编译。 所以我将MacroImpl
和Macros
放在一个子项目中,并在另一个子项目中调用宏,它运行得很好。
感谢Dmytro花时间演示如何正确行事!
//编辑:看起来像Dmytro用他的评论打败了我:-)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.