5

I am trying to create a Scala macro that will generate an object - something like

object SomeEnum {
  sealed abstract class Enum(name: String)
  case object Option1 extends Enum("option1")
  case object Option2 extends Enum("option2")
  private val elements: Seq[Enum] = Seq(Option1, Option2)
  def apply(code: String): Enum = {
    ...
  }
} 

I thought I might be able to create a macro createEnum, so I could just put createEnum("SomeEnum", "Option1", "Option2") into my code and have it generate. Seems like it's calling out for a macro.

But I must not be understanding macros. I am using Scala 2.11.6, and just to try to get something working, I created the following:

object createEnumObj {
  def createEnumImpl(c: scala.reflect.macros.whitebox.Context)(ename: c.Expr[String]): c.universe.ModuleDef = {
    import c.universe._

    val Literal(Constant(s_ename: String)) = ename.tree
    val oname = TermName(s_ename)

    val barLine = q"val bar: Int = 5"
    q"object $oname { $barLine }"
  }

  def createEnum(ename: String): Unit = macro createEnumImpl
}

This is in a separate project - everything seems to be compiling for it OK.

If I stick a call to createEnumObj.createEnum into some source and try to compile that, I get a billion lines (give or take a few) of exception output, which seems to repeat something like this:

[error] (main/compile:compile) java.lang.AssertionError: assertion failed:
[error]   object foo extends scala.AnyRef {
[error]   def <init>() = {
[error]     super.<init>();
[error]     ()
[error]   };
[error]   val bar: Int = 5
[error] }
[error]      while compiling: /Users/bob/ICL/ironcore-id/src/main/scala/package.scala
[error]         during phase: typer
[error]      library version: version 2.11.6
[error]     compiler version: version 2.11.6
[error]   reconstructed args: -Xfuture ...
error]
[error]   last tree to typer: term foo
[error]        tree position: line 8 of /Users/bob/ICL/ironcore-id/src/main/scala/package.scala
[error]               symbol: <none>
[error]    symbol definition: <none> (a NoSymbol)
[error]       symbol package: <none>
[error]        symbol owners:
[error]            call site: <none> in <none>
[error]
[error] == Source file context for tree position ==
[error]
[error]      5   type DateTime = Int
[error]      6
[error]      7   createEnumObj.createEnum("foo")
[error]      8
[error]      9 }
[error] Total time: 2 s, completed Jun 18, 2015 2:46:05 PM

What I am trying to do doesn't seem too dissimilar to this question, but I'm obviously missing something. Any ideas about how to accomplish this would be gratefully accepted.

Thanks, Bob

Community
  • 1
  • 1
Bob Wall
  • 151
  • 3
  • I don't have any experience with enums, but Shapeless has an example which I think does something similar. See https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/enum.scala – Angelo Genovese Jun 18 '15 at 21:26
  • 3
    You can't use `def`-macros to introduce top-level definitions, unfortunately, but you can accomplish something similar with structural types, and if you really want to create objects you can use macro annotations—we have some examples of the two approaches [here](https://github.com/travisbrown/type-provider-examples). – Travis Brown Jun 18 '15 at 21:31
  • That looked promising, but it doesn't want to cooperate in Scala 2.11.6. I tried implementing something like your type-provider, and just putting an abort in it - the compiler compiles it, but even if I use the annotation in another file, it doesn't abort. If i mangle the name, the compiler does complain. I think I'm going to go with code generation in SBT - for what I am trying to do, that is better suited anyway. I might dive back into macros in the future - I would like to get them working. Thanks for the info. – Bob Wall Jun 22 '15 at 17:23

0 Answers0