如何在scala宏中获取类型较高的参数的树

时间:2019-06-02 10:33:12

标签: scala scala-macros higher-kinded-types scala-reflect

我试图编写一个宏来简化一些与Monad相关的代码(我在Monad中使用cats 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)来调用宏,它将扩展为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的项目和我自己的项目之间的区别。

2 个答案:

答案 0 :(得分:4)

请勿将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()))

在编译时,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] 

答案 1 :(得分:1)

好的,我发现了问题所在。

宏需要与使用站点分开编译。我认为这意味着Macros需要与MacroImpl分开编译,因此我将它们放在单独的sbt子项目中,然后在定义了Macros的项目中调用了该宏。但是实际上,这意味着宏的 calls 需要与其定义分开编译。因此,我将MacroImplMacros放在一个子项目中,并在另一个子项目中调用了宏,它工作得很好。

感谢Dmytro抽出宝贵的时间来演示如何正确地做到这一点!

//编辑:好像Dmytro用他的评论击败了我:-)