简体   繁体   English

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

[英]How to obtain a tree for a higher-kinded type parameter in a scala macro

I'm trying to write a macro to simplify some monad-related code (I'm using cats 1.6.0 for the Monads). 我正在尝试编写一个宏来简化一些与monad相关的代码(我正在为Monads使用cat 1.6.0)。 For now I just want to be able to write lift[F](a) where F is a unary type constructor, and have that expand to a.pure[F] . 现在我只想写出lift[F](a)其中F是一元型构造函数,并将其扩展为a.pure[F] Seems simple enough, but I can't get it working. 看起来很简单,但我不能让它工作。

For now I have this code to help with type inference: 现在我有这个代码来帮助进行类型推断:

object Macros {
  class LiftPartiallyApplied[F[_]] {
    def apply[A](a: A): F[A] = macro MacroImpl.liftImpl[F, A]
  }

  def lift[F[_]] = new LiftPartiallyApplied[F]
}

And for the actual implementation of the macro: 而对于宏的实际实现:

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

Now I can call the macro like this lift[List](42) and it'll expand to 42.pure[List] , great. 现在我可以像这个lift[List](42)一样调用宏lift[List](42)并且它会扩展到42.pure[List] ,很棒。 But when I call it with a more complicated type, like lift[({type F[A] = Either[String, A]})#F](42) , It'll expand to 42.pure[Either] , which is obviously broken, because Either is a binary type constructor and not a unary one. 但是当我用更复杂的类型调用它时,例如lift[({type F[A] = Either[String, A]})#F](42) ,它将扩展为42.pure[Either] ,显然是坏了,因为Either是二进制类型的构造函数而不是一元的构造函数。 The problem is I just don't know what to put there instead of ${tt.tpe.typeConstructor} 问题是我只是不知道放在那里而不是${tt.tpe.typeConstructor} ...

// edit: since people apparently have trouble reproducing the problem, I've made a complete repository: https://github.com/mberndt123/macro-experiment I will now try to figure out what the difference between Dmytro's and my own project is. //编辑:由于人们显然无法重现问题,我已经建立了一个完整的存储库: https//github.com/mberndt123/macro-experiment我现在将尝试找出Dmytro和我自己项目之间的区别是。

Don't put Main and Macros to the same compilation unit. 不要将MainMacros放在同一个编译单元中。


But when I call it with a more complicated type, like lift[({type F[A] = Either[String, A]})#F](42) , It'll expand to 42.pure[Either] 但是当我用更复杂的类型调用它时,例如lift[({type F[A] = Either[String, A]})#F](42) ,它将扩展为42.pure[Either]

Can't reproduce. 无法重现。

For me lift[List](42) produces (with scalacOptions += "-Ymacro-debug-lite" ) 对我来说, lift[List](42)产生(使用scalacOptions += "-Ymacro-debug-lite"

Warning:scalac: 42.pure[List]
TypeApply(Select(Literal(Constant(42)), TermName("pure")), List(TypeTree()))

at compile time and List(42) at runtime. 在编译时和运行时的List(42)

lift[({ type F[A] = Either[String, A] })#F](42) produces 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()))

at compile time and Right(42) at runtime. 在编译时和在运行时Right(42)

This is my project https://gist.github.com/DmytroMitin/334c230a4f2f1fd3fe9e7e5a3bb10df5 这是我的项目https://gist.github.com/DmytroMitin/334c230a4f2f1fd3fe9e7e5a3bb10df5


Why do you need macros? 你为什么需要宏? Why can't you write 你为什么不写

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] 

?

Allright, I found out what the problem was. 好吧,我发现了问题所在。

Macros need to be compiled separately from their use sites. 宏需要与其使用站点分开编译。 I thought that this meant that Macros needs to be compiled separately from MacroImpl , so I put those in separate sbt subprojects, and I called the macro in the project where Macros is defined. 我认为这意味着Macros需要与MacroImpl分开编译,所以我将它们放在单独的sbt子项目中,并在项目中调用Macros来定义Macros But what it in fact means is that calls of the macro need to be compiled separately from its definition. 但它实际上意味着宏的调用需要与其定义分开编译。 So I put MacroImpl and Macros in one subproject and called the macro in another and it worked perfectly. 所以我将MacroImplMacros放在一个子项目中,并在另一个子项目中调用宏,它运行得很好。

Thanks to Dmytro for taking the time to demonstrate how to do it right! 感谢Dmytro花时间演示如何正确行事!

// edit: looks like Dmytro beat me to it with his comment :-) //编辑:看起来像Dmytro用他的评论打败了我:-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM