Macro paradise: now a compiler plugin for 2.10.x, features quasiquotes and macro annotations

1516 views
Skip to first unread message

Eugene Burmako

unread,
Aug 5, 2013, 9:53:16 AM8/5/13
to scala-l...@googlegroups.com
Hi folks,

I've just released a new version of macro paradise, which represents a significant revamp and introduces new features. You can read the full text of the announcement at http://scalamacros.org/news/2013/08/05/macro-paradise-2.0.0-snapshot.html.

Short summary:
* Now a compiler plugin that works with Scala 2.10.2
* Backports quasiquotes from 2.11.0-M4
* Also provides freshly implemented macro annotations

Your feedback is very welcome!

Cheers,
Eugene

Alois Cochard

unread,
Aug 5, 2013, 10:09:42 AM8/5/13
to scala-l...@googlegroups.com
Congratulations for the hard work Eugene!

Haoyi Li

unread,
Aug 5, 2013, 10:18:52 AM8/5/13
to scala-l...@googlegroups.com
Looks awesome =) Can't wait to play with macro annotations.

Question: I remember reading this link (http://www.infoq.com/news/2012/07/scala-macros) a while ago, about using macros to inject implicits into a scope without having to keep typing `implicit blah =>` everywhere. I had always thought untyped macros would be the obvious solution for this; is that still going to happen?

-Haoyi


--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Rodrigo Cano

unread,
Aug 5, 2013, 4:02:36 PM8/5/13
to scala-l...@googlegroups.com
Wow, it just keeps getting better.

Rodrigo Cano

unread,
Aug 5, 2013, 4:50:29 PM8/5/13
to scala-l...@googlegroups.com
One thing I can't find in the docs: published artifact and an example to instruct sbt to use that plugin.

Thanks.

Eugene Burmako

unread,
Aug 5, 2013, 4:52:35 PM8/5/13
to scala-l...@googlegroups.com

Rodrigo Cano

unread,
Aug 5, 2013, 4:53:51 PM8/5/13
to scala-l...@googlegroups.com
Oh crap, it was in an obvious location. My bad. Thanks!

Eugene Burmako

unread,
Aug 5, 2013, 5:09:21 PM8/5/13
to scala-l...@googlegroups.com
Currently there exists certain skepticism wrt untyped macros on expression level.

Untyped macros are perceived as being too powerful, being something that we would like to better control but don't yet know how. A popular theme now is providing a way for programs to typecheck without triggering macro expansions, so that programs can be better understood by humans and computers alike (that's what led to the blackbox vs whitebox separation covered in my ScalaDays talk: http://scalamacros.org/news/2013/05/18/half-a-year-in-macro-paradise.html), and untyped macros don't quite fit into this scheme.

It is for that reason I'm wary of adding untyped macro to the new paradise. Exploring new avenues is of course important, but having production Scala as the ultimate target is at the foundation of the Scala macros project. Towards that goal I can motivate quasiquotes, macro annotations and the upcoming fundep materializers to the SIP committee, but I'm not yet sure about untyped macros.

All's not lost however. I'm very interested in the notion of untyped macros, because that's how they do a lot of things in LISP (and, in fact, quasiquotes have successfully brought the notion of untypedness into our previously reify-based world). It's just that I didn't yet have time to dig deep to provide personal involvement.

I think if we could come up with something that provides information about expansions of untyped macros and maybe a killer use case (like materialization for implicit macros), things can very well change. Look at macro annotations. They are also not blackbox, but they cover an important scenario of global code generation, so people are much more favorable towards them.

Haoyi Li

unread,
Aug 6, 2013, 12:36:48 AM8/6/13
to scala-l...@googlegroups.com
My use case for untyped macros would be essentially turning this:

run{ implicit i =>
    ... contextualOperation() ...
}

and this:

run{ i =>
    import i._
    ... contextualOperation() ...
}

into

run{ ... contextualOperation() ... } 

Essentially, I would like to abstract away the act of injecting a context into a scope (whether via import ._ or via an implicit). This would be nice for scala.rx (which is my original interest), but it's an identical problem that Akka actors' sender-ref, SLICK's sessions, scala-STM's transactions, and Scala.React's continuation-based DSL all face. 

You have a choice of using an implicit/explicit parameter to inject context, or using a threadlocal/mutable variable: the former gets extremely verbose/tedious after a few dozen uses, and the latter faces subtle scoping problems related to mutable state and lexical closure which really does cause problems/confusion (see all the questions regarding Akka's sender and Future{}). 

I guess the exact capability that all these things would need is the ability to inject either a single implicit or a single import ._ into the scope, so it's not so much completely untyped as it is typed-with-additional-local-bindings, presumably taken from the members of some type. I think an import ._ would be preferable since that means your contextualOperation won't need to exist around outside the run{...} block unnecessarily clogging up those scopes.

Simon Ochsenreither

unread,
Aug 6, 2013, 9:01:11 AM8/6/13
to scala-l...@googlegroups.com
Hi Eugene,

the work you have done is just incredibly impressive!

Here are a few points from the Enum perspective:

I'm not sure whether macro annotations bring us any closer to having good enum support in Scala, I see at least two big issues which both are deal-breakers from my point of view.
  • It is not possible to emit static members
  • The syntactic options given by macro annotations are very limited and I don't think it is possible to get close to a syntax which I would consider acceptable for mainstream use.
Please take this with a grain of salt, I'm aware that enum support is a very different use-case to the usual macro use-cases and I don't expect any changes to make this use-case easier, especially not in favor of more important and popular use-cases of macros.

I'm not sure how untyped macros would look like in the macro annotation context. I always thought they were closely coupled to the type macro approach. Could you show some example code?

Thank a lot,

Simon

Eugene Burmako

unread,
Aug 6, 2013, 9:07:07 AM8/6/13
to scala-l...@googlegroups.com
Hi Simon,

With macro annotations it is possible to create members in a companion object. Probably you could try to annotate them with STATIC, and probably that might work - didn't have time to check myself though.

Speaking of syntactic options, what exactly are you interested in?

Finally I didn't get the question about untyped macros. Could you elaborate?

Simon Ochsenreither

unread,
Aug 6, 2013, 9:33:27 AM8/6/13
to scala-l...@googlegroups.com
Hi Eugene,



With macro annotations it is possible to create members in a companion object. Probably you could try to annotate them with STATIC, and probably that might work - didn't have time to check myself though.

Yes, I have seen that. I'm not sure though that this will work. The JVM looks for some static members in the enum class. Having members, even static members, in a different class (the companion object) won't cut it from a JVM perspective. (I tried setting the STATIC flag in the past and couldn't see any difference, too.)
The only thing which might save us are static forwarders. I know that scalac emits those, but I'm not sure whether they are emitted for those occurrences I'm interested in.
 
Speaking of syntactic options, what exactly are you interested in?

Using type macros and untyped macros, it's possible to this syntax:

class Days extends Enum(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)

Finally I didn't get the question about untyped macros. Could you elaborate?

As far as I understand with macro annotations the best thing possible would be something along the lines of

@Enum(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
class Days


... assuming untyped macros work this way (in the sense that the untyped parts which looked like arguments to the super constructor with type macros + untyped macros are now inside the annotation).
This syntax just won't scale (user-defined constructors, user-defined methods, user-defined overridden methods in individual enum items) and will look quite foreign.

Bye,

Simon

Eugene Burmako

unread,
Aug 6, 2013, 9:37:29 AM8/6/13
to scala-l...@googlegroups.com
Trees that get into a macro annotation expansion don't get typechecked, so you can write whatever you want inside the definition of the class and, as long as it's syntactically valid, you'll get a chance to read that whatever, analyzer it and emit whatever you want instead. Would that bring enough flexibility to support your use case?


On Tuesday, 6 August 2013, Simon Ochsenreither <simon.och...@gmail.com> wrote:

Simon Ochsenreither

unread,
Aug 6, 2013, 10:28:07 AM8/6/13
to scala-l...@googlegroups.com


Trees that get into a macro annotation expansion don't get typechecked, so you can write whatever you want inside the definition of the class and, as long as it's syntactically valid, you'll get a chance to read that whatever, analyzer it and emit whatever you want instead. Would that bring enough flexibility to support your use case?

Does this mean that while something like ...

@Enum

class Days extends Enum(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)


... won't be possible anymore, something like ...

@Enum
class Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }


might work?

Eugene Burmako

unread,
Aug 6, 2013, 10:46:09 AM8/6/13
to scala-l...@googlegroups.com
It would work if it were valid Scala syntax :)


On Tuesday, 6 August 2013, Simon Ochsenreither <simon.och...@gmail.com> wrote:
>
>

Simon Ochsenreither

unread,
Aug 6, 2013, 11:41:16 AM8/6/13
to scala-l...@googlegroups.com

It would work if it were valid Scala syntax :)

Ok ... maybe I'm not getting then what untyped macros are about.
From my POV, being able to pass arbitrary trees to the macro is the important part of untyped macros ... without it, what's left?

Thorpe, Michael

unread,
Aug 6, 2013, 11:44:12 AM8/6/13
to <scala-language@googlegroups.com>
The arbitrary trees still need to be constructed somehow. The scalac parser cannot construct trees for things that there aren't production rules for.

Untyped =/= new syntax 

On 6 Aug 2013, at 16:41, Simon Ochsenreither <simon.och...@gmail.com>
 wrote:


It would work if it were valid Scala syntax :)

Ok ... maybe I'm not getting then what untyped macros are about.
From my POV, being able to pass arbitrary trees to the macro is the important part of untyped macros ... without it, what's left?

Rex Kerr

unread,
Aug 6, 2013, 11:45:00 AM8/6/13
to scala-l...@googlegroups.com
What you wrote wasn't even a tree, though.  This is:

@Enum class Days {
  Sunday; Monday; Tuesday; Wednesday; Thursday; Friday; Saturday
}

This too:

@Enum class Days {
  (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
}

  --Rex




--

Tim Underwood

unread,
Aug 6, 2013, 11:46:11 AM8/6/13
to scala-l...@googlegroups.com
Hi Eugene,

I tried adding the compiler plugin to my existing project so I could play with the new macro features but I get a compiler AssertionError (stack trace below).  I haven't tried using any of the new macro features yet and the code compiles fine using Scala 2.10.2 without the macro paradise plugin.

I'm trying to minimize the code that causes the error but haven't been successful so far.  Does the stack trace give you any hints as to what the problem might be?

Thanks,

-Tim



Stack Trace:

at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1210)
at scala.reflect.internal.Symbols$ModuleClassSymbol.implicitMembers(Symbols.scala:3095)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getClassParts$1(Implicits.scala:1073)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$getParts$1(Implicits.scala:1113)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$getParts$1(Implicits.scala:1120)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$getParts$1(Implicits.scala:1120)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.companionImplicitMap(Implicits.scala:1147)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$implicitsOfExpectedType(Implicits.scala:1167)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.bestImplicit(Implicits.scala:1427)
at scala.tools.nsc.typechecker.Implicits$class.inferImplicit(Implicits.scala:82)
at org.scalalang.macroparadise.Plugin$$anon$1.inferImplicit(Plugin.scala:25)
at scala.tools.nsc.typechecker.Implicits$class.inferImplicit(Implicits.scala:40)
at org.scalalang.macroparadise.Plugin$$anon$1.inferImplicit(Plugin.scala:25)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$wrapImplicit$1(Typers.scala:202)
at scala.tools.nsc.typechecker.Typers$Typer.inferView(Typers.scala:209)
at scala.tools.nsc.typechecker.Typers$Typer.inferView(Typers.scala:180)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2$$anonfun$isCoercible$1.apply$mcZ$sp(Typers.scala:105)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2$$anonfun$isCoercible$1.apply(Typers.scala:103)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2$$anonfun$isCoercible$1.apply(Typers.scala:103)
at scala.reflect.internal.Types$UndoLog.undo(Types.scala:186)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2.isCoercible(Typers.scala:102)
at scala.tools.nsc.typechecker.Infer$Inferencer.scala$tools$nsc$typechecker$Infer$Inferencer$$isCompatible(Infer.scala:425)
at scala.tools.nsc.typechecker.Infer$Inferencer$$anonfun$isCompatibleArgs$1.apply(Infer.scala:428)
at scala.tools.nsc.typechecker.Infer$Inferencer$$anonfun$isCompatibleArgs$1.apply(Infer.scala:428)
at scala.collection.LinearSeqLike$class.corresponds(LinearSeqLike.scala:76)
at scala.collection.immutable.List.corresponds(List.scala:84)
at scala.tools.nsc.typechecker.Infer$Inferencer.isCompatibleArgs(Infer.scala:428)
at scala.tools.nsc.typechecker.Infer$Inferencer.typesCompatible$1(Infer.scala:814)
at scala.tools.nsc.typechecker.Infer$Inferencer.scala$tools$nsc$typechecker$Infer$Inferencer$$isApplicable(Infer.scala:833)
at scala.tools.nsc.typechecker.Infer$Inferencer.tryTupleApply$1(Infer.scala:809)
at scala.tools.nsc.typechecker.Infer$Inferencer.scala$tools$nsc$typechecker$Infer$Inferencer$$isApplicable(Infer.scala:856)
at scala.tools.nsc.typechecker.Infer$Inferencer.isApplicableSafe(Infer.scala:877)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$63.apply(Typers.scala:3146)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$63.apply(Typers.scala:3135)
at scala.collection.TraversableLike$$anonfun$filter$1.apply(TraversableLike.scala:264)
at scala.collection.immutable.List.foreach(List.scala:318)
at scala.collection.TraversableLike$class.filter(TraversableLike.scala:263)
at scala.collection.AbstractTraversable.filter(Traversable.scala:105)
at scala.reflect.internal.Symbols$Symbol.filter(Symbols.scala:1658)
at scala.tools.nsc.typechecker.Typers$Typer.preSelectOverloaded$1(Typers.scala:3135)
at scala.tools.nsc.typechecker.Typers$Typer.doTypedApply(Typers.scala:3163)
at scala.tools.nsc.typechecker.Typers$Typer.normalTypedApply$1(Typers.scala:4589)
at scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:4621)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5526)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typed1(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:44)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5683)
at scala.tools.nsc.typechecker.Typers$Typer.computeType(Typers.scala:5770)
at scala.tools.nsc.typechecker.Namers$Namer.assignTypeToTree(Namers.scala:834)
at scala.tools.nsc.typechecker.Namers$Namer.valDefSig(Namers.scala:1315)
at scala.tools.nsc.typechecker.Namers$Namer.getSig$1(Namers.scala:1456)
at scala.tools.nsc.typechecker.Namers$Namer.typeSig(Namers.scala:1465)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1$$anonfun$apply$1.apply$mcV$sp(Namers.scala:731)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1$$anonfun$apply$1.apply(Namers.scala:730)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1$$anonfun$apply$1.apply(Namers.scala:730)
at scala.tools.nsc.typechecker.Namers$Namer.scala$tools$nsc$typechecker$Namers$Namer$$logAndValidate(Namers.scala:1498)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1.apply(Namers.scala:730)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1.apply(Namers.scala:729)
at org.scalalang.macroparadise.typechecker.Namers$$anon$3.completeImpl(Namers.scala:704)
at scala.tools.nsc.typechecker.Namers$LockingTypeCompleter$class.complete(Namers.scala:1621)
at org.scalalang.macroparadise.typechecker.Namers$$anon$3.complete(Namers.scala:702)
at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1229)
at scala.reflect.internal.Symbols$Symbol.initialize(Symbols.scala:1365)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:39)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2926)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.collection.immutable.List.loop$1(List.scala:170)
at scala.collection.immutable.List.mapConserve(List.scala:186)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1919)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typedTemplate(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typedTemplate(Typers.scala:51)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typedTemplate(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typedClassDef(Typers.scala:1759)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typedClassDef(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typedClassDef(Typers.scala:62)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typedClassDef(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5545)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typed1(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:44)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2926)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.collection.immutable.List.loop$1(List.scala:170)
at scala.collection.immutable.List.mapConserve(List.scala:186)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:5263)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5549)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typed1(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typedPackageDef$1(Typers.scala:35)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:43)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5666)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.apply(Analyzer.scala:99)
at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:464)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3$$anonfun$run$1.apply(Analyzer.scala:91)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3$$anonfun$run$1.apply(Analyzer.scala:91)
at scala.collection.Iterator$class.foreach(Iterator.scala:727)
at scala.collection.AbstractIterator.foreach(Iterator.scala:1157)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.run(Analyzer.scala:91)
at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1583)
at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1557)
at scala.tools.nsc.Global$Run.compileSources(Global.scala:1553)
at scala.tools.nsc.Global$Run.compile(Global.scala:1662)
at xsbt.CachedCompiler0.run(CompilerInterface.scala:123)
at xsbt.CachedCompiler0.run(CompilerInterface.scala:99)
at xsbt.CompilerInterface.run(CompilerInterface.scala:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:102)
at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:48)
at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:41)
at sbt.compiler.AggressiveCompile$$anonfun$3$$anonfun$compileScala$1$1.apply$mcV$sp(AggressiveCompile.scala:98)
at sbt.compiler.AggressiveCompile$$anonfun$3$$anonfun$compileScala$1$1.apply(AggressiveCompile.scala:98)
at sbt.compiler.AggressiveCompile$$anonfun$3$$anonfun$compileScala$1$1.apply(AggressiveCompile.scala:98)
at sbt.compiler.AggressiveCompile.sbt$compiler$AggressiveCompile$$timed(AggressiveCompile.scala:159)
at sbt.compiler.AggressiveCompile$$anonfun$3.compileScala$1(AggressiveCompile.scala:97)
at sbt.compiler.AggressiveCompile$$anonfun$3.apply(AggressiveCompile.scala:142)
at sbt.compiler.AggressiveCompile$$anonfun$3.apply(AggressiveCompile.scala:86)
at sbt.inc.IncrementalCompile$$anonfun$doCompile$1.apply(Compile.scala:38)
at sbt.inc.IncrementalCompile$$anonfun$doCompile$1.apply(Compile.scala:36)
at sbt.inc.Incremental$.cycle(Incremental.scala:73)
at sbt.inc.Incremental$$anonfun$1.apply(Incremental.scala:33)
at sbt.inc.Incremental$$anonfun$1.apply(Incremental.scala:32)
at sbt.inc.Incremental$.manageClassfiles(Incremental.scala:41)
at sbt.inc.Incremental$.compile(Incremental.scala:32)
at sbt.inc.IncrementalCompile$.apply(Compile.scala:26)
at sbt.compiler.AggressiveCompile.compile2(AggressiveCompile.scala:150)
at sbt.compiler.AggressiveCompile.compile1(AggressiveCompile.scala:70)
at sbt.compiler.AggressiveCompile.apply(AggressiveCompile.scala:45)
at sbt.Compiler$.apply(Compiler.scala:70)
at sbt.Defaults$.sbt$Defaults$$compileTaskImpl(Defaults.scala:722)
at sbt.Defaults$$anonfun$compileTask$1.apply(Defaults.scala:716)
at sbt.Defaults$$anonfun$compileTask$1.apply(Defaults.scala:716)
at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:42)
at sbt.std.Transform$$anon$4.work(System.scala:64)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
at sbt.Execute.work(Execute.scala:244)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:160)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:30)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)

Eugene Burmako

unread,
Aug 6, 2013, 12:22:21 PM8/6/13
to scala-l...@googlegroups.com
Thanks a lot for the crash report! It would be great if you could minimize the reproduction. I will gladly take a look.


--

Simon Ochsenreither

unread,
Aug 6, 2013, 12:39:24 PM8/6/13
to scala-l...@googlegroups.com

The arbitrary trees still need to be constructed somehow. The scalac parser cannot construct trees for things that there aren't production rules for.

Alright, of course!

The syntax in the type macro + untyped annotation code only worked because it was declared as varargs!

I think Rex' syntax of

@Enum class Days {
  Sunday; Monday; Tuesday; Wednesday; Thursday; Friday; Saturday
}


could be bearable ... especially if it would be written like this:


@Enum class Days {
  Sunday
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
}


The remaining question is whether the syntax scales, e.g.:

@Enum
class Days(val inGerman: String) extends HasName {
  Monday("Montag")
  Tuesday("Dienstag")
  Wednesday("Mittwoch")
  Thursday("Donnerstag")
  Friday("Freitag")
  Saturday("Samstag") { override def workingDay: Boolean = false }
  Sunday("Sonntag") { override def workingDay: Boolean = false }

  def abbreviation = name take 3
  def workingDay: Boolean = true
}

trait HasName { def name: String }


I think this looks quite nice, indeed!

I'll spend a few minutes to have a look whether I can re-organize my macro accordingly. What's the recommended way currently if one needs type annotations and untyped macros? The macro paradise branch for 2.11 trunk? Are there any current published artifacts?

Thanks,

Simon

Eugene Burmako

unread,
Aug 6, 2013, 12:42:29 PM8/6/13
to scala-l...@googlegroups.com
Currently you cannot mix macro annotations and untyped macros. What would be your use case? Looks like for enums macro annotations alone should be enough.


--

Simon Ochsenreither

unread,
Aug 6, 2013, 12:51:50 PM8/6/13
to scala-l...@googlegroups.com

Currently you cannot mix macro annotations and untyped macros. What would be your use case? Looks like for enums macro annotations alone should be enough.

So I can basically put arbitrary trees into the body of a class annotated with a macro as long as it can be parsed, without needing untyped macros?

Eugene Burmako

unread,
Aug 6, 2013, 1:04:08 PM8/6/13
to scala-l...@googlegroups.com
Yes


On Tue, Aug 6, 2013 at 6:51 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:


Currently you cannot mix macro annotations and untyped macros. What would be your use case? Looks like for enums macro annotations alone should be enough.

So I can basically put arbitrary trees into the body of a class annotated with a macro as long as it can be parsed, without needing untyped macros?

--

Simon Ochsenreither

unread,
Aug 6, 2013, 1:13:52 PM8/6/13
to scala-l...@googlegroups.com

Yes

Wow, impressive!

Sorry for not reading your explanations thoroughly enough. :-/

Now the remaining question is how to add the required type (java.lang.Enum) to the parents of the annotated class.

Eugene Burmako

unread,
Aug 6, 2013, 1:15:38 PM8/6/13
to scala-l...@googlegroups.com
It's also possible.

Macro annotations get the entire tree that represents an annottee. In our case that would be a ClassDef, and ClassDef contains a list of parents that you can freely change.


On Tuesday, 6 August 2013, Simon Ochsenreither <simon.och...@gmail.com> wrote:
>

Simon Ochsenreither

unread,
Aug 6, 2013, 1:25:02 PM8/6/13
to scala-l...@googlegroups.com

It's also possible.

Macro annotations get the entire tree that represents an annottee. In our case that would be a ClassDef, and ClassDef contains a list of parents that you can freely change.

Excellent!

I'll try to port the type-macro+untyped-macro-based enums to annotation macros and report back.

Simon Ochsenreither

unread,
Aug 6, 2013, 2:39:43 PM8/6/13
to scala-l...@googlegroups.com
In the documentation, it says:

[...] macro annotations in 2.0 are done right in the sense that they [...] allow to introduce static members (i.e. expansions of classes can affect companions). This opens a number of new possibilities in code generation land, including customizable case classes and better enums.

How would one do that?

Simon Ochsenreither

unread,
Aug 6, 2013, 3:16:08 PM8/6/13
to scala-l...@googlegroups.com
Ok, this is where I ended up: https://github.com/soc/enum-paradise/blob/java-enums-via-macro-annotations/macros/src/main/scala/scalax.scala

Something like

@Enum
class Day {

    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
}


already compiles successfully!

But as the println(show(newClassDef)) in line 171 shows, the static flag doesn't work:

class Day extends java.lang.Enum[Day] {
  private def <init>(name: String, ordinal: Int) = {
    super.<init>(name, ordinal);
    ()
  };
  private val Monday: Day = new Day("Monday", 1);
  private val Tuesday: Day = new Day("Tuesday", 2);
  private val Wednesday: Day = new Day("Wednesday", 3);
  private val Thursday: Day = new Day("Thursday", 4);
  private val Friday: Day = new Day("Friday", 5);
  private[this] val $VALUES: scala.Array[Day] = scala.Array.apply[Day]()(Predef.implicitly);
  def values: scala.Array[Day] = $VALUES.clone();
  def valueOf(name: String): Day = java.lang.Enum.valueOf(classOf[Day], name)
}

So the class is not usable, because it is missing the required static members (Monday, Tuesday, Wednesday, Thursday, Friday, $VALUES, values, valueOf should all be static).

Any ideas?

Tim Underwood

unread,
Aug 6, 2013, 4:12:26 PM8/6/13
to scala-l...@googlegroups.com

If you comment out the macro paradise plugin it compiles fine.  Also if you comment out the @BeanInfo annotation on the Bar object in Bar.scala it compiles fine.

Let me know if I can do anything else to help.

-Tim

Eugene Burmako

unread,
Aug 6, 2013, 4:41:03 PM8/6/13
to scala-l...@googlegroups.com
I meant generating members for companions. After a discussion with you I understood that it's a stretch, and I will correct it once I get back home.
--

Simon Ochsenreither

unread,
Aug 6, 2013, 5:36:18 PM8/6/13
to scala-l...@googlegroups.com
I think one could get away by emitting a companion object and profit from the fact scalac emits static forwarders for it in the companion class by default already.
What's the best way to define the class and get the object for free? I have looked into the Context API but didn't find an appropriate introduce??? method. Looks like this is 2.11 only.

Eugene Burmako

unread,
Aug 6, 2013, 5:41:38 PM8/6/13
to scala-l...@googlegroups.com
We don't have introduceXXX anymore, and introduceTopLevel will be removed before the 2.11 release. The only way to emit a companion object is to put an annotation on a class, as described here: http://docs.scala-lang.org/overviews/macros/annotations.html.

Please let me know whether the idea with forwarders works out. Keeping fingers crossed.


On 6 August 2013 23:36, Simon Ochsenreither <simon.och...@gmail.com> wrote:
I think one could get away by emitting a companion object and profit from the fact scalac emits static forwarders for it in the companion class by default already.
What's the best way to define the class and get the object for free? I have looked into the Context API but didn't find an appropriate introduce??? method. Looks like this is 2.11 only.

--

Eugene Burmako

unread,
Aug 6, 2013, 5:54:03 PM8/6/13
to scala-l...@googlegroups.com
Why are you not using quasiquotes?



--

Simon Ochsenreither

unread,
Aug 6, 2013, 6:09:48 PM8/6/13
to scala-l...@googlegroups.com

Why are you not using quasiquotes?

I was just porting existing, pre-quasiquote code; the changes to move from type macros + untyped macros to macro annotations were pretty small, so I didn't bother to rework the code yet:
https://github.com/soc/enum-paradise/commit/ec64e228dc934ec59c6bf68a4592d16fbc08da41

Simon Ochsenreither

unread,
Aug 6, 2013, 6:15:40 PM8/6/13
to scala-l...@googlegroups.com

We don't have introduceXXX anymore, and introduceTopLevel will be removed before the 2.11 release. The only way to emit a companion object is to put an annotation on a class, as described here: http://docs.scala-lang.org/overviews/macros/annotations.html.

Mhhh, maybe I'm misunderstanding the docs, but it reads like I would need to have the companion object already declared in the code to be able to emit members for that object. Is that correct?

Eugene Burmako

unread,
Aug 6, 2013, 6:18:02 PM8/6/13
to scala-l...@googlegroups.com
Nope, you can just expand a ClassDef into a ClassDef + ModuleDef, and the companion will be transparently created for you.


On 7 August 2013 00:15, Simon Ochsenreither <simon.och...@gmail.com> wrote:

We don't have introduceXXX anymore, and introduceTopLevel will be removed before the 2.11 release. The only way to emit a companion object is to put an annotation on a class, as described here: http://docs.scala-lang.org/overviews/macros/annotations.html.

Mhhh, maybe I'm misunderstanding the docs, but it reads like I would need to have the companion object already declared in the code to be able to emit members for that object. Is that correct?

--

Simon Ochsenreither

unread,
Aug 6, 2013, 6:18:21 PM8/6/13
to scala-l...@googlegroups.com
Ahhh, just saw your comments on GitHub. Will try it that way!

Thanks,

Simon

Eugene Burmako

unread,
Aug 6, 2013, 6:30:39 PM8/6/13
to scala-l...@googlegroups.com
Thank you very much for pointing out flaws in the documentation!

Based on your feedback I've just updated the docs: http://docs.scala-lang.org/overviews/macros/annotations.html. I'm still not satisfied with the expansion rules, but at least now the description should be helpful. What do you think?



--

Simon Ochsenreither

unread,
Aug 6, 2013, 9:35:54 PM8/6/13
to scala-l...@googlegroups.com
What do you think?

Yes, this makes the new possibilities much more explicit!

Update: https://github.com/soc/enum-paradise/commit/e19c0b2a1670fd642e759b9c08081ec470aca250

@Enum class Foo { Bar }

val bar = Foo.Bar


:-)

I think the next question now is how to set the ENUM flag for the enum class. Any ideas?

Eugene Burmako

unread,
Aug 7, 2013, 1:32:10 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia, Grzegorz Kossakowski
Doesn't look like we in scalac emit this flag at all. Probably you'll need to do it in a compiler plugin that injects into backend. Miguel, Greg, could you comment?



--

Grzegorz Kossakowski

unread,
Aug 7, 2013, 3:26:15 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia
On 6 August 2013 22:32, Eugene Burmako <eugene....@epfl.ch> wrote:
Doesn't look like we in scalac emit this flag at all. Probably you'll need to do it in a compiler plugin that injects into backend. Miguel, Greg, could you comment?

The ENUM is not set by Scala compiler anywhere (it's only used to represent types coming from Java) so backend doesn't support it in any way.

I wouldn't be very against adding support for it but one would have to figure out exact semantics. For the start:
  • should ENUM flag be pickled? (probably yes)
  • would class file parser be confused by the fact that there's a scala class that has an enum flag?
  • would referring to enums synthesized from scala files work?

--
Grzegorz Kossakowski
Scalac hacker at Typesafe
twitter: @gkossakowski

Eugene Burmako

unread,
Aug 7, 2013, 4:13:33 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither
Oh wow, looks like we're just one step from having enums just like you wanted, Simon.

I would probably not have time to implement that myself, but if you can give it a try, it would be very cool. I believe code freeze for 2.11 is just a month away, but on the other hand it's the entire month away!

Simon Ochsenreither

unread,
Aug 7, 2013, 6:59:10 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither


Oh wow, looks like we're just one step from having enums just like you wanted, Simon.

I would probably not have time to implement that myself, but if you can give it a try, it would be very cool.

Yes, I'll have a look at it.

 
I believe code freeze for 2.11 is just a month away, but on the other hand it's the entire month away!

Ouch ... that probably means I should start to prioritize things now. Off the top of my head, I come up with ...
  • Avian support
  • JavaScript backend
  • Being able to recognize a value type at runtime
  • Deprecating procedures
  • Deprecating dangerous implicit widening conversions
  • Deprecating “warning: Adapting argument list by inserting (): this is unlikely to be what you want.”
  • Trying to get a reliable definition of what's in the default scope (instead of the current “whatever version of Java you might run” free-for-all)
  • SI-5722 - classOf[T] support with Tags
  • SI-6634 - Fix and specify Collection's remove method
  • SI-6747 - Fix Range
  • SI-6632 - Fix ListBuffer
  • SI-7379 - Fix Option(null.asInstanceOf[Boolean])

... in addition to implementing @Enum (which includes writing tests, updating the SIP, getting it accepted, deprecating scala.Enumeration and documenting the migration) which I all want to ship/fix/implement for 2.11.

Some of those might be close to the finish line, but estimating the amount of time it takes for the politics associated with it hasn't been really reliable in the past.

Simon Ochsenreither

unread,
Aug 7, 2013, 8:35:41 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia
Hi Grzegorz!



The ENUM is not set by Scala compiler anywhere (it's only used to represent types coming from Java) so backend doesn't support it in any way.

I wouldn't be very against adding support for it but one would have to figure out exact semantics. For the start:
  • should ENUM flag be pickled? (probably yes)
  • would class file parser be confused by the fact that there's a scala class that has an enum flag?
  • would referring to enums synthesized from scala files work?
These are good questions!

I think a pretty idiomatic and minimal change would look like this: https://github.com/soc/scala/compare/topic;enums?expand=1
There are two isEnum methods, which aren't strictly required, so they could also be left out.

This way, we don't have any special-cased code anywhere in the compiler, it just wires up the a modifier flag to the appropriate ACC_ENUM, just like for all the other class file bits.
Creating enums would happen completely in the macro.

What do you think?

Thanks and bye,

Simon

Rex Kerr

unread,
Aug 7, 2013, 11:53:03 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither
On Wed, Aug 7, 2013 at 6:59 AM, Simon Ochsenreither <simon.och...@gmail.com> wrote:

 
I believe code freeze for 2.11 is just a month away, but on the other hand it's the entire month away!

Ouch ... that probably means I should start to prioritize things now. Off the top of my head, I come up with ...

I don't think all of these things are equally frozen.  Anything that's a bug or a drop-in replacement that doesn't require deprecation, interface changes, etc. should be fair game.

Maybe someone more in-the-know than I can specify which of these are subject to freeze and which are fine as post-major-feature-freeze tweaks.

Most or all of the things in JIRA look like they could be introduced post-freeze--they're randomly broken stuff that cannot be sanely used.  (Updating a negative number actually changes the SECOND thing in the list?!)

Something like deprecating procedures, in contrast, almost surely should go in.  As I'm sure too many people are aware, I dislike the change, but if the decision has been made to do it, someone please get it in before the freeze!
 
  • Avian support
  • JavaScript backend
  • Being able to recognize a value type at runtime
  • Deprecating procedures
  • Deprecating dangerous implicit widening conversions
  • Deprecating “warning: Adapting argument list by inserting (): this is unlikely to be what you want.”
  • Trying to get a reliable definition of what's in the default scope (instead of the current “whatever version of Java you might run” free-for-all)
  • SI-5722 - classOf[T] support with Tags
  • SI-6634 - Fix and specify Collection's remove method
  • SI-6747 - Fix Range
  • SI-6632 - Fix ListBuffer
  • SI-7379 - Fix Option(null.asInstanceOf[Boolean])

... in addition to implementing @Enum (which includes writing tests, updating the SIP, getting it accepted, deprecating scala.Enumeration and documenting the migration) which I all want to ship/fix/implement for 2.11.


Having a working Enum would be awesome, in my opinion.  Not quite as nice as having a for-construct that doesn't penalize you when you use it like a Java for loop, but it always irks me that Java enums in many ways are better-supported and lower-boilerplate than Scala's equivalents.

But this too should IMO really go in before the freeze.  Messing with code generation is something you want to do early in your release process.

  --Rex

Simon Ochsenreither

unread,
Aug 7, 2013, 1:06:43 PM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither
Hi Eugene, hi Grzegorz,

thanks a lot for your support.

I have created changes for enum support against trunk and I'm not sure whether backporting them to 2.10.x is an option ... so I would probably need to port the macro code from 2.10.x to 2.11 again. Is the functionality I'm using already in 2.11 trunk (afaik interpolation is in, but macro annotations aren't yet, correct?)?

Or should I compile against some macro paradise version + my changes?

What do you think?

Thanks and bye!

Simon

Edit: Mhhhh, looks like porting the enum support to 2.10.x might be the most painfree way in the end.

Simon Ochsenreither

unread,
Aug 8, 2013, 7:15:08 AM8/8/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither
Hey guys,

want do you think of this? :D

Enum declaration:

@Enum
class Day {
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
  Sunday
}


Enum usage:

  val mon = Day.Monday
  val tue = Day.Tuesday
  val days = Day.values.toList
  def monday(day: Day) = day match {
    case Day.Monday => "Monday!"
  }
  println(s"Day.Monday: \t\t\t\t $mon")
  println(s"Day.Monday.name: \t\t\t ${mon.name}")
  println(s"Day.Monday.ordinal: \t\t\t ${mon.ordinal}")
  println(s"Day.Monday.getClass: \t\t\t ${mon.getClass}")
  println(s"classOf[Day].isEnum: \t\t\t ${classOf[Day].isEnum}")
  println(s"Day.Monday  compareTo Day.Tuesday: \t ${mon compareTo tue}")
  println(s"Day.Monday  compareTo Day.Monday: \t ${mon compareTo mon}")
  println(s"Day.Tuesday compareTo Day.Monday: \t ${tue compareTo mon}")
  println(s"Day.Monday == Day.Tuesday: \t\t ${mon == tue}")
  println(s"Day.Monday == Day.Monday: \t\t ${mon == mon}")
  println(s"Day.Monday.getDeclaringClass: \t\t ${mon.getDeclaringClass}")
  println(s"Day.values: \t\t\t\t $days")
  println(s"""Day valueOf "Wednesday": \t\t ${Day valueOf "Wednesday"}""")
  println(s"""Enum.valueOf(classOf[Day], "Wednesday"): ${Enum.valueOf(classOf[Day], "Wednesday")}""")
  println(s"monday(Days.Monday): \t\t\t ${monday(mon)}")


Output:

Day.Monday:                              Monday
Day.Monday.name:                         Monday
Day.Monday.ordinal:                      1
Day.Monday.getClass:                     class scalax.Day
classOf[Day].isEnum:                     true
Day.Monday  compareTo Day.Tuesday:       -1
Day.Monday  compareTo Day.Monday:        0
Day.Tuesday compareTo Day.Monday:        1
Day.Monday == Day.Tuesday:               false
Day.Monday == Day.Monday:                true
Day.Monday.getDeclaringClass:            class scalax.Day
Day.values:                              List(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
Day valueOf "Wednesday":                 Wednesday
Enum.valueOf(classOf[Day], "Wednesday"): Wednesday
monday(Days.Monday):                     Monday!


The only limitation I found is that switch/case doesn't work in Java, because

an enum switch case label must be the unqualified name of an enumeration constant
             case Day.Monday():
                            ^


Not sure if we can fix that, considering that we basically depend on scalac's generation of static forwarders and don't have any power to tell it how it should be done. I don't think it matters that much anyway.

Thanks and bye,

Simon

Jason Zaugg

unread,
Aug 8, 2013, 7:21:30 AM8/8/13
to scala-l...@googlegroups.com
On Thu, Aug 8, 2013 at 1:15 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
Hey guys,

want do you think of this? :D

Enum declaration:

@Enum
class Day {
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
  Sunday
}

I like the brevity, but I don't like rewriting expressions in the constructor as members.

For example, you have to know about the @Enum annotation to know that this method isn't called:

def Monday = println("!"); @Enum class Day { Monday }

-jason

Simon Ochsenreither

unread,
Aug 8, 2013, 8:19:38 AM8/8/13
to scala-l...@googlegroups.com
Hi Jason!


I like the brevity, but I don't like rewriting expressions in the constructor as members.

I would have certainly preferred the syntax of the type macro/untyped macro approach, but that's not a viable option anymore.
I'm still looking for better solutions and I'm happy about all suggestions I can get in this regard!

For example, you have to know about the @Enum annotation to know that this method isn't called:

def Monday = println("!"); @Enum class Day { Monday }

Yes, but I think this is pretty much exactly like in Java. Anyway, if we follow Java's style guide for enums, the amount of all-upercase-with-underscores method names might be quite limited in 99.99% of the code bases in the wild.

Bye,

Simon

Simon Ochsenreither

unread,
Aug 8, 2013, 8:32:07 AM8/8/13
to scala-l...@googlegroups.com

Simon Ochsenreither

unread,
Aug 8, 2013, 10:49:08 AM8/8/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither, abor...@gmail.com
> The only limitation I found is that switch/case doesn't work in Java,
> because
>
> an enum switch case label must be the unqualified name of an enumeration
> constant
>              case Day.Monday():
>                             ^
In case it is from Java, the Day. part is part of the problem. You are
not allowed to put in a case statement. (And it is usually not a method....)

Yes, that's basically the remaining issue: scalac never emits public static fields, only methods. As far as I know, there is not much I can do about it. :-(

Simon Ochsenreither

unread,
Aug 9, 2013, 5:07:37 AM8/9/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither
Hi Eugene!

It seems like the STATIC flag started to work, too and I have added a different approach which generates static members in the class instead of generating a companion object.

The good news is that it works from Java, The bad news is that Scala can't seem to figure out that new approach and complains that "Day is not a value", although it clearly supports that scheme with class files coming from javac.

I already tried marking the symbols as coming from Java, but without success.

Any ideas what could cause the issue or how to make scalac understand static members coming from Scala code?

Here is the code: https://github.com/soc/enum-paradise/blob/java-enums-via-macro-annotations/macros/src/main/scala/scalax/EnumMacroUsingStaticMembers.scala

And here is what scalac claims to generate:

class Day extends java.lang.Enum[Day] {
  final private def <init>(name: String, ordinal: Int) = {

    super.<init>(name, ordinal);
    ()
  };
  <static> val Monday: Day = new Day("Monday", 1);
  <static> val Tuesday: Day = new Day("Tuesday", 2);
  <static> val Wednesday: Day = new Day("Wednesday", 3);
  <static> val Thursday: Day = new Day("Thursday", 4);
  <static> val Friday: Day = new Day("Friday", 5);
  <static> val Saturday: Day = new Day("Saturday", 6);
  <static> val Sunday: Day = new Day("Sunday", 7);
  <static> private[this] val $VALUES: scala.Array[Day] = scala.Array.apply[Day](Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)(Predef.implicitly);
  <static> def values: scala.Array[Day] = $VALUES.clone();
  <static> def valueOf(name: String): Day = java.lang.Enum.valueOf(classOf[Day], name)
}


Error messages:

[error] /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaConsumer.scala:4: not found: value Day
[error]   val mon = Day.Monday
[error]             ^
[error] /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaConsumer.scala:5: not found: value Day
[error]   val tue = Day.Tuesday
[error]             ^
[error] /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaConsumer.scala:6: not found: value Day
[error]   val days = Day.values.toList
[error]              ^
[error] /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaConsumer.scala:8: not found: value Day
[error]     case Day.Monday => "Monday!"
[error]          ^


Another issue I'm seeing is that the static fields are not initialized at all. Could it be that the initializers are put into the wrong place (e. g. into instance initialization, not into class initialization, despite STATIC being set)?

Any ideas about these issues?

Thanks!

Simon

Eugene Burmako

unread,
Aug 9, 2013, 5:11:18 AM8/9/13
to scala-l...@googlegroups.com
"Not found: value Day" means that you need to have a symbol with term name Day in scope. Since you're no longer generating a companion, you no longer have such a symbol.

Simon Ochsenreither

unread,
Aug 9, 2013, 6:36:26 AM8/9/13
to scala-l...@googlegroups.com

"Not found: value Day" means that you need to have a symbol with term name Day in scope. Since you're no longer generating a companion, you no longer have such a symbol.

But then calling a static method defined in Java wouldn't work, too, right? So scalac already knows how this is handled, I just don't find how I can trigger it for the enums.
I tried setting the JAVA flag for my class with static members, but that didn't help.

Eugene Burmako

unread,
Aug 9, 2013, 6:56:17 AM8/9/13
to scala-l...@googlegroups.com
Scalac sneakily converts symbols from java class files into class+module combos, pushing static parts to modules.


--

Simon Ochsenreither

unread,
Aug 9, 2013, 8:04:42 AM8/9/13
to scala-l...@googlegroups.com

Scalac sneakily converts symbols from java class files into class+module combos, pushing static parts to modules.

Thanks for the hint ... is there a way this mechanism can be leveraged for our use-case here? This way I might even get public fields to work, because they have to be represented somehow as symbols in the Scala AST.

Eugene Burmako

unread,
Aug 9, 2013, 10:54:46 AM8/9/13
to scala-l...@googlegroups.com
Not sure, needs investigation.


On 9 August 2013 14:04, Simon Ochsenreither <simon.och...@gmail.com> wrote:

Scalac sneakily converts symbols from java class files into class+module combos, pushing static parts to modules.

Thanks for the hint ... is there a way this mechanism can be leveraged for our use-case here? This way I might even get public fields to work, because they have to be represented somehow as symbols in the Scala AST.

--

Eugene Burmako

unread,
Aug 9, 2013, 1:23:50 PM8/9/13