I'm trying to write a macro to simplify some monad-related code (I'm using cats 1.6.0 for the Monads). 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]
. 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. 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. The problem is I just don't know what to put there instead of ${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.
Don't put Main
and Macros
to the same compilation unit.
But when I call it with a more complicated type, like
lift[({type F[A] = Either[String, A]})#F](42)
, It'll expand to42.pure[Either]
Can't reproduce.
For me lift[List](42)
produces (with 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.
lift[({ type F[A] = Either[String, A] })#F](42)
produces
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.
This is my project 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]
?