Как получить дерево для параметра с более высоким родом в макросе scala - PullRequest
3 голосов
/ 02 июня 2019

Я пытаюсь написать макрос, чтобы упростить некоторый связанный с монадами код (я использую кошек 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}

// edit: поскольку у людей, видимо, возникают проблемы с воспроизведением проблемы, я сделал полный репозиторий: https://github.com/mberndt123/macro-experiment Сейчас я попытаюсь выяснить, в чем разница между Дмитрием и моим собственным проектом.

Ответы [ 2 ]

4 голосов
/ 02 июня 2019

Не помещайте 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] 

?

1 голос
/ 02 июня 2019

Хорошо, я выяснил, в чем проблема.

Макросы необходимо компилировать отдельно от сайтов их использования. Я подумал, что это означает, что Macros нужно компилировать отдельно от MacroImpl, поэтому я поместил их в отдельные подпроекты sbt, и я вызвал макрос в проекте, где определено Macros. Но на самом деле это означает, что вызовы макроса необходимо компилировать отдельно от его определения. Поэтому я поместил MacroImpl и Macros в один подпроект и вызвал макрос в другом, и он отлично работал.

Спасибо Дмитрию за то, что он нашел время продемонстрировать, как это сделать правильно!

// edit: похоже, что Дмитрий избил меня своим комментарием: -)

...