Materialisasi implisit dalam modul yang sama

Dec 15 2020

Saya memiliki kelas berparameter seperti

class Test[T]{
    //...
}

object Test{
    implicit def materializeTest[T]: Test[T] = macro impl[T]

    def impl[T: c.WeakTypeTag](c: blackbox.Context) = //...
}

Jika menggunakan materialized implisit dari modul yang sama, kesalahan ini muncul:

macro implementation not found

Tetapi masalahnya adalah mengekstrak satu kelas menjadi modul terpisah terlihat sangat jelek dan tidak praktis. Mungkin ada beberapa "solusi terkenal" untuk menghindarinya? Mungkin tak berbentuk bisa membantu di sini?

UPD:

scalaVersion in ThisBuild := "2.13.2"

Inilah contoh minimal saya:

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

object Main {
  sealed trait Adt
  case object Adt1 extends Adt
  case object Adt2 extends Adt

  trait Test[Adtt <: Adt] {
    def restrict(restrictions: List[Int]): List[Int]
  }

  object Test {
    def apply[Adtt <: Adt](implicit ev: Test[Adtt]): Test[Adtt] = ev

    implicit def implicitMaterializer[
        Adtt <: Adt
    ]: Test[Adtt] = macro impl[Adtt]

    def impl[Adtt <: Adt: c.WeakTypeTag](
        c: blackbox.Context
    ): c.Expr[Test[Adtt]] = {

      import c.universe._
      c.Expr[Test[Adtt]](q"""???""")
    }
  }

  def main(args: Array[String]): Unit = {
    Test[Adt1.type].restrict(List(1, 2, 3))
  }
}

yang menghasilkan kesalahan berikut:

[error] Main.scala:32:9: macro implementation not found: implicitMaterializer
[error] (the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)

Jawaban

3 DmytroMitin Dec 15 2020 at 21:35

Anda tidak dapat mengekstrak ke modul terpisah TesttetapiTestMacro

inti

import scala.language.experimental.macros

class Test[T]

object Test {
  implicit def materializeTest[T]: Test[T] = macro TestMacro.impl[T]
}

implicitly[Test[Int]] // compiles

makro

import scala.reflect.macros.blackbox

object TestMacro {
  def impl[T: c.WeakTypeTag](c: blackbox.Context) = {
    import c.universe._
    q"new Test[${weakTypeOf[T]}]"
  }
}

Jelek atau tidak, tetapi implementasi makro harus dikompilasi sebelum diterapkan.

Ini ditingkatkan di Scala 3

http://dotty.epfl.ch/docs/reference/metaprogramming/macros.html#defining-a-macro-and-using-it-in-a-single-project

Shapeless hanya menyembunyikan beberapa kumpulan makro standar yang telah ditentukan sebelumnya, itu tidak dapat membantu dengan makro Anda sendiri.