Macro implícita. Valor implícito padrão. Quão?

Sep 29 2020

Eu nem sei como fazer a pergunta.

Eu tenho uma macro que cria uma instância de IsEnum[T]para um tipo T.

Estou fazendo testes para isso e quero ter certeza de que o implícito não seja encontrado para tipos que não são lacrados ou que, em geral, não atendem aos requisitos de um enum.

Então eu criei este método para testar

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

E então eu garanto que enumOf[NotAnEnum] == null

Mas em vez disso, ele falha em tempo de compilação.

Uma coisa é o erro de macro. Outra coisa é a macro simplesmente não se aplicar a um determinado caso. Como fazer essa distinção ao criar macros?

Edit: Eu usei c.aborte c.error, ambos me dando os mesmos resultados.

Respostas

2 DmytroMitin Sep 29 2020 at 23:43

Parece que você não fez sua IsEnumcaixa branca de classe de tipo de materialização de macro . Normalmente, as macros implícitas devem ser uma caixa branca.

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

Uso:

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

O tempo de execução de macros é o tempo de compilação do código principal. Se uma macro de caixa preta (mesmo a macro de caixa preta implícita) lançar uma exceção, será um erro de compilação durante a compilação do código principal. Se uma macro implícita de caixa branca lançar uma exceção, durante a compilação do código principal, o implícito será removido silenciosamente dos candidatos.

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