Implizites Makro. Impliziter Standardwert. Wie?

Sep 29 2020

Ich weiß nicht einmal, wie ich die Frage stellen soll.

Ich habe ein Makro, das eine Instanz IsEnum[T]für einen Typ erstellt T.

Ich teste es und möchte sicherstellen, dass das Implizite nicht für Typen gefunden wird, die nicht versiegelt sind oder die im Allgemeinen nicht den Anforderungen einer Aufzählung entsprechen.

Also habe ich diese Methode zum Testen erstellt

    def enumOf[T](implicit isEnum:IsEnum[T] = null) = isEnum

Und dann sorge ich dafür enumOf[NotAnEnum] == null

Stattdessen schlägt es beim Kompilieren fehl.

Eine Sache ist das Makrofehler. Eine andere Sache ist, dass das Makro für einen bestimmten Fall einfach nicht gilt. Wie kann man diese Unterscheidung beim Erstellen von Makros treffen?

Bearbeiten: Ich habe verwendet c.abortund c.errorbeide geben mir die gleichen Ergebnisse.

Antworten

2 DmytroMitin Sep 29 2020 at 23:43

Klingt so, als hätten Sie Ihre Makro-Materialisierungs-Typklasse- IsEnumWhitebox nicht erstellt. Normalerweise sollten implizite Makros Whitebox sein.

import scala.language.experimental.macros
import scala.reflect.macros.whitebox

trait IsEnum[T]
object IsEnum {
  implicit def mkIsEnum[T]: IsEnum[T] = macro mkIsEnumImpl[T]
  def mkIsEnumImpl[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._
    val typ = weakTypeOf[T]
    val classSymbol = typ.typeSymbol.asClass
    if (!classSymbol.isTrait || !classSymbol.isSealed) c.abort(c.enclosingPosition, s"$typ must be sealed trait") val symbols = classSymbol.knownDirectSubclasses symbols.collectFirst { case symbol if !symbol.isModuleClass || !symbol.asClass.isCaseClass => c.abort(c.enclosingPosition, s"${symbol.asClass.toType} must be case object")
    }
    q"new IsEnum[$typ] {}"
  }
}

def enumOf[T](implicit isEnum: IsEnum[T] = null) = isEnum

Verwendung:

sealed trait A
object A {
  case object B extends A
  case object C extends A
  case class D() extends A
}
enumOf[A] //null

sealed trait A
object A {
  case object B extends A
  case object C extends A
  class D extends A
}
enumOf[A] //null

sealed trait A
object A {
  case object B extends A
  object C extends A
}
enumOf[A] //null

trait A
object A {
  case object B extends A
  case object C extends A
}
enumOf[A] //null

sealed trait A
object A {
  case object B extends A
  case object C extends A
}
enumOf[A] //App$$anon$1@47f37ef1

Die Laufzeit von Makros ist die Kompilierungszeit des Hauptcodes. Wenn ein Blackbox-Makro (sogar ein implizites Blackbox-Makro) eine Ausnahme auslöst, handelt es sich beim Kompilieren des Hauptcodes um einen Kompilierungsfehler. Wenn ein implizites Whitebox-Makro eine Ausnahme auslöst, wird das implizite Makro während der Kompilierung des Hauptcodes stillschweigend von den Kandidaten entfernt.

https://docs.scala-lang.org/overviews/macros/blackbox-whitebox.html