4

Background

I'm trying to add a @Prisms annotation to Monocle that will work as follows. Given:

@Prisms sealed trait Foo
case object A extends Foo
case object B extends Foo

it will generate a companion object for Foo:

object Foo {
  val a = monocle.macros.GenPrism.apply[Foo, A]
  val b = monocle.macros.GenPrism.apply[Foo, B]
}

(or if the companion object already exists, it will add those methods to it).

The macro relies on directKnownSubclasses to find A and B, so there will be a note in the Monocle readme recommending that people use Typelevel Scala. I've configured Monocle to use TLS on my branch.

First attempt

I tried using TypeNames to represent the parent type Foo and child type (A or B):

def findSubclasses(typeName: TypeName): Set[TypeName] = {
  // Note: disable macros to prevent stack overflow caused by infinite typing loop!
  val tpe = c.typecheck(Ident(typeName), mode = c.TYPEmode, silent = true, withMacrosDisabled = true)
  tpe.symbol.asClass.knownDirectSubclasses.map(_.asType.name)
}

def prisms(parentTypename: TypeName, childNames: Set[TypeName]): Set[Tree] = childNames.map { childName =>
  val prismName = TermName(prefix + childName.decodedName.toString.toLowerCase)
  q"""val $prismName = monocle.macros.GenPrism.apply[$parentTypename, $childName]"""
}

This generates what looks like reasonable code:

{
  sealed trait Foo;
  object Foo {
    val a = monocle.macros.GenPrism.apply[Foo, A];
    val b = monocle.macros.GenPrism.apply[Foo, B]
  };
  ()
}

But it doesn't seem to work:

[error] /Users/chris/code/Monocle/test/shared/src/test/scala/other/PrismsAnnotationSpec.scala:5: not found: type A
[error] @monocle.macros.Prisms sealed trait Foo
[error]  ^
[error] one error found

Second attempt

I also tried using TypeSymbols instead of TypeNames:

def prisms(parentTypename: TypeName, childSymbols: Set[TypeSymbol]): Set[Tree] = {
  val parentSymbol: TypeSymbol = 
    c.typecheck(Ident(parentTypename), mode = c.TYPEmode, silent = true, withMacrosDisabled = true)
      .symbol
      .asType
  childSymbols.map { childSymbol =>
    val prismName = TermName(prefix + childSymbol.name.decodedName.toString.toLowerCase)
    q"""val $prismName = monocle.macros.GenPrism.apply[$parentSymbol, $childSymbol]"""
  }
}

But this gives me the following error, so I'm guessing the generated tree is invalid:

[error] /Users/chris/code/Monocle/test/shared/src/test/scala/other/PrismsAnnotationSpec.scala:5: macro annotation could not be expanded (the most common reason for that is that you need to enable the macro paradise plugin; another possibility is that you try to use macro annotation in the same compilation run that defines it)
[error] @monocle.macros.Prisms sealed trait Foo
[error]  ^
[error] one error found

Third attempt

I also tried turning the TypeSymbols into Types:

def prisms(parentTypename: TypeName, childSymbols: Set[TypeSymbol]): Set[Tree] = {
  val parentSymbol: TypeSymbol = 
    c.typecheck(Ident(parentTypename), mode = c.TYPEmode, silent = true, withMacrosDisabled = true)
      .symbol
      .asType
  childSymbols.map { childSymbol =>
    val prismName = TermName(prefix + childSymbol.name.decodedName.toString.toLowerCase)
    q"""val $prismName = monocle.macros.GenPrism.apply[${parentSymbol.toType}, ${childSymbol.toType}]"""
  }
}

But I get the same error, saying the annotation could not be expanded.

Now I'm out of ideas.

How to reproduce

The complete code is available on this branch: https://github.com/cb372/Monocle/tree/sealed-trait-macro-annotation.

The macro is used in test/shared/src/test/scala/other/PrismsAnnotationSpec.scala. To compile it, run sbt test:compile.

halfer
  • 19,824
  • 17
  • 99
  • 186
Chris B
  • 9,149
  • 4
  • 32
  • 38
  • Any chance you're running into the same issue we see in circe [here](https://github.com/travisbrown/circe/issues/251)? – Travis Brown Oct 07 '16 at 16:36
  • Are you trying to use macro annotation in the same compilation run that defines it? – Miles Sabin Oct 08 '16 at 13:39
  • @MilesSabin No, the annotation is defined in src/main of one subproject, and it's used src/test of a different subproject, so they're definitely different compilation runs. – Chris B Oct 09 '16 at 13:00
  • Workaround for "annotation could not be expanded" https://stackoverflow.com/a/20466423/5249621 – Dmytro Mitin Jun 24 '19 at 21:36

0 Answers0