如何在 Scala 宏中获取更高级类型参数的树

Mat*_*ndt 4 scala higher-kinded-types scala-macros scala-reflect

我正在尝试编写一个宏来简化一些与 monad 相关的代码(我使用cats 1.6.0 作为Monads)。现在我只想能够编写lift[F](a)whereF是一个一元类型的构造函数,并将其扩展为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]
}
Run Code Online (Sandbox Code Playgroud)

对于宏的实际实现:

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}]"
  }
}

Run Code Online (Sandbox Code Playgroud)

现在我可以像这样调用宏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 和我自己的项目之间的区别是。

Dmy*_*tin 5

不要把MainMacros放到同一个编译单元。


但是当我用更复杂的类型调用它时,比如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()))
Run Code Online (Sandbox Code Playgroud)

在编译时和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()))
Run Code Online (Sandbox Code Playgroud)

在编译时和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] 
Run Code Online (Sandbox Code Playgroud)

?