I've made an enum macro, which ended up having a strange behavior.
This is the main implementation:
object Enum {
def values[A]: Seq[A] = macro Impl.values[A]
class Impl(val c: Context) {
import c.universe._
def values[A: c.WeakTypeTag]: c.Expr[A] = {
val elemType = weakTypeOf[A]
val elemSymbol = elemType.typeSymbol.asSealedClass
val elemSymbols = elemSymbol.knownDirectSubclasses.toList
val elemIdents = elemSymbols.map(s => Ident(s.asCaseClass.asModuleClass.module))
val elemSeqSymbol = weakTypeOf[Seq[A]].typeSymbol
c.Expr(Apply(Ident(elemSeqSymbol.companion), elemIdents))
}
implicit class SymbolOp(s: Symbol) {
def asSealedClass = s.asClass.ensuring(_.isSealed, s"$s is not sealed")
def asCaseClass = s.asClass.ensuring(_.isCaseClass, s"$s is not a case class")
def asModuleClass = s.asClass.ensuring(_.isModuleClass, s"$s is not an object")
}
}
}
And this is the test:
object EnumTest extends App {
sealed trait Foo
case object Moo extends Foo
case object Noo extends Foo
val values = Enum.values[Foo]
println(values)
println(Enum.values[Foo])
println({
val v = Enum.values[Foo]
v
})
}
The console should print out three same outputs, but:
List()
List(Moo, Noo)
List(Moo, Noo)
Wny does this happen? How do you deal with this?
I would appreciate if you can test it in your SBT: https://github.com/ryo0ka/enum
Thank you!
This is indeed a strange behavior, but it is hard for Scalac to do a good job here: The problem is that you use nested objects. Consider the following:
object EnumTest extends App {
sealed trait Foo
case object Moo extends Foo
case object Noo extends Foo
println(Enum.values[Foo])
case object Bar extends Foo
}
This prints List(Moo, Noo). If we move Bar before the println statement, it prints List(Moo, Noo, Bar) as we would expect.
If we move the sealed hierarchy out of the object, everything works fine.
Therefore, it seems it is probably not a smart idea to have the macro reify at the location where you define your case classes. You could make values itself a macro (whose implementation simply calls Impl.values with the right WeakTypeTag). That would probably work.
Related
I'm trying to refactor an Enumeration to a sealed trait with concrete classes because I need to pack more functionality into them. With the sealed trait, I'd like to have functionality similar to Enumeration's withName(String) method. I've come up with the following code to do so and am looking for feedback:
sealed trait Foo {
def name: String
}
object Foo {
case object FooA extends Foo {
override val name: String = "a"
}
case object FooB extends Foo {
override val name: String = "b"
}
val values = Seq(FooA, FooB)
def withName(name: String): Option[Foo] = {
values.find(value => value.name.equals(name))
}
}
I can then use the withName(String) method to get the corresponding concrete object of type Foo as an Option:
val testFooAName = "a"
val testFooA = Foo.withName(testFooAName) // Will yield Some(FooA)
testFooA match {
case Some(Foo.FooA) => println("Matched Foo.FooA!")
case Some(Foo.FooB) => print("Matched Foo.FooB!")
}
val testFooNoneName = "none"
val testFooNone = Foo.withName(testFooNoneName) // Will yield None
Output:
Matched Foo.FooA!
Is this approach correct?
Yes, it looks fine
Minor simplification: Have a map for fast lookup
val values = Seq(FooA, FooB)
val fastLookup = values.map(v => v.name -> v).toMap
def withName(name: String): Option[Foo] = fastLookup.get(name)
There is a handy library by beachape which can be used to provide some of the functionality of enums to your sealed trait
the package you need to include in your sbt is:
"com.beachape" %% "enumeratum" % "1.5.15"
then extend your your object with the Enum like so:
import enumeratum._
sealed trait Foo
object Foo extends Enum[Foo] {
case object FooA extends Foo
case object FooB extends Foo
}
The you can use the withName function (amongst others) to get the sealed trait you want:
Foo.withName("FooA")
I'm attempting to build a scala class based on some java classes generated by a third party annotation pre-processor.
I'd like to be able to "point" to a class from an annotated object, for example:
#MyAnnotation(classOf[GeneratedJavaClass]) object MyObject
or
#MyAnnotation object MyObject extends PlaceHolderTrait[GeneratedJavaClass]
Once I'm in the actual macro implementation, I would like to reflect on GeneratedJavaClass to find it's members which I'll use to build the implementation of MyObject
My starting point so far is based on https://github.com/travisbrown/type-provider-examples/blob/master/rdfs-public/src/main/scala/public/PrefixGenerator.scala.
I've tried to understand how I could take a Class[T] as the argument to the annotation and then match on c.macroApplication with Apply(Select(Apply(_, List(TypeApply(_, List(catalog)))), _), _) but the type I get is a TypeApply(_, List(Trees$Ident) and I don't see a way to get the class from there (I assume classOf[T] isn't a literal).
As an alternative, I thought I'd try to extract the type I want from a trait that I have the object extend. I tried matching the annotee against case List(q"object $name extends PlaceHolderTrait[$parent] { ..$body }") but again end up with a Trees$Ident and am not sure how to get the class that is being referenced.
I realize I could probably just pass a String of the fully qualified name and use reflection to get the class, but I was hoping for a nicer alternative. Note that I'm not tied to those 2 alternatives for specifying the class, they are just the 2 options I've been able to come up with.
Ok, finally got something working, based on https://github.com/scalamacros/paradise/issues/69 I realized that at the stage I'm running in the typer hasn't run and therefore expecting to be given a type is a bit silly. The macro api does provide the typeCheck method however, which lets you run the typer on a tree, as follows:
class AnnotationArgument[T] extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnotationArgumentImpl.impl
}
class AnnotationArgumentImpl(val c: blackbox.Context) {
import c.universe._
def impl(annottees: c.Expr[Any]*): c.Tree = {
val macroTypeWithArguments = c.typeCheck(q"${c.prefix.tree}").tpe // my.package.AnnotationArgument[my.package.MyClass]
val annotationClass: ClassSymbol = macroTypeWithArguments.typeSymbol.asClass // my.package.AnnotationArgument
val annotationTypePlaceholder: Type = annotationClass.typeParams.head.asType.toType // T
val argumentType: Type = annotationTypePlaceholder.asSeenFrom(args, annotationClass) // my.package.MyClass
println(s"the argument's type is $argumentType")
q"..${annottees}"
}
}
import my.package.MyClass
#AnnotationArgument[MyClass]
class AnnotationArgumentTestClass
another thought :
use another class annotation save class info
class AnnotationArgumentClass[T](clazz: Class[T]) extends StaticAnnotation
class AnnotationArgument extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnotationArgumentImpl.impl
}
class AnnotationArgumentImpl(val c: blackbox.Context) {
import c.universe._
def impl(annottees: c.Expr[Any]*): c.Tree = {
val classValue = annottees.head.tree match {
case x: MemberDefApi => x.mods.annotations collectFirst {
case q"new $annotation($classValue)" => classValue
}
}
/*edit :*/
val x = (c.eval(c.Expr[Type](c.typecheck(classValue.get))))
println(x.typeSymbol.fullName)
q"..${annottees}"
}
}
test :
package aaa
class AAA
//
import aaa.AAA
#AnnotationArgument
#AnnotationArgumentClass(classOf[AAA])
class AnnotationArgumentTestClass
Suppose I have a trait Foo with several methods. I want to create a new trait which extends Foo but "wraps" each method call, for example with some print statement (in reality this will be something more complicated / I have a couple of distinct use cases in mind).
trait Foo {
def bar(x: Int) = 2 * x
def baz(y: Int) = 3 * y
}
I can do this manually, by overriding each method. But this seems unnecessarily verbose (and all too easy to call the wrong super method):
object FooWrapped extends FooWrapped
trait FooWrapped extends Foo {
override def bar(x: Int) ={
println("call")
super.bar(x)
}
override def baz(y: Int) ={
println("call")
super.baz(y)
}
}
scala> FooWrapped.bar(3)
call
res3: Int = 6
I was hoping to write a mixin trait, that I would be able to reuse with other traits, and use as:
trait FooWrapped extends Foo with PrintCall
That way I don't have to manually override each method (the mixin would do this for me).
Is it possible to write such a mixin trait in Scala? What would it look like?
Update Here is the macro. It was much less painful than I thought it will be because of quasiquotes. They are awesome. This code does only a little and you probably will have to improve it. It may not account some special situations. Also it assumes that neither parent class nor it's method has type params, it wraps only the methods of the given class or trait, but not it's parents methods, it may not work if you have auxilary constructors etc. Still I hope it will give you an idea of how to do that for your specific needs, making it working for all of the situations unfortunately is too big job for me right now.
object MacrosLogging {
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def log_wrap[T](): T = macro log_impl[T]
def log_impl[T : c.WeakTypeTag](c: blackbox.Context)(): c.Expr[T] = {
import c.universe._
val baseType = implicitly[c.WeakTypeTag[T]].tpe
val body = for {
member <- baseType.declarations if member.isMethod && member.name.decodedName.toString != "$init$"
method = member.asMethod
params = for {sym <- method.paramLists.flatten} yield q"""${sym.asTerm.name}: ${sym.typeSignature}"""
paramsCall = for {sym <- method.paramLists.flatten} yield sym.name
methodName = member.asTerm.name.toString
} yield {
q"""override def ${method.name}(..$params): ${method.returnType} = { println("Method " + $methodName + " was called"); super.${method.name}(..$paramsCall); }"""
}
c.Expr[T] {q""" { class A extends $baseType { ..$body }; new A } """}
}
}
If you do not want to create an instance, but you do want to add logging only for your trait so you could mixin further, you can do this with relatively the same code, but using macro paradise type annotations: http://docs.scala-lang.org/overviews/macros/annotations These allow you to tag your class definitions and perform modifications right inside the definitions
You could do something like you want with Dynamic, but there is a catch - you can't make it of original type, so it's not a mixin. Dynamic starts to work only if type checks fails, so you can't mixin real type (or I do not know how to do that). The real answer would probably require macros (as #AlexeyRomanov suggested in comments), but I am not sure how to write one, maybe I'll come up with it later. Still Dynamic might work for you if you are not looking for DI here
trait Foo {
def bar(x: Int) = 2 * x
def baz(y: Int) = 3 * y
}
import scala.reflect.runtime.{universe => ru}
import scala.language.dynamics
trait Wrapper[T] extends Dynamic {
val inner: T
def applyDynamic(name: String)(args: Any*)(implicit tt: ru.TypeTag[T], ct: ClassTag[T]) = {
val im = tt.mirror.reflect(inner)
val method = tt.tpe.decl(ru.TermName(name)).asMethod
println(method)
val mm = im.reflectMethod(method)
println(s"$name was called with $args")
mm.apply(args:_*)
}
}
class W extends Wrapper[Foo] {
override val inner: Foo = new Foo() {}
}
val w = new W // Cannot be casted to Foo
println(w.bar(5)) // Logs a call and then returns 10
You can read more about Dynamic here: https://github.com/scala/scala/blob/2.12.x/src/library/scala/Dynamic.scala
This is probably covered by the blog entry by Jesse Eichar—still I can't figure out how to correct the following without resorting to lazy vals so that the NPE is fixed:
Given
trait FooLike { def foo: String }
case class Foo(foo: String) extends FooLike
trait Sys {
type D <: FooLike
def bar: D
}
trait Confluent extends Sys {
type D = Foo
}
trait Mixin extends Sys {
val global = bar.foo
}
First attempt:
class System1 extends Mixin with Confluent {
val bar = Foo("npe")
}
new System1 // boom!!
Second attempt, changing mixin order
class System2 extends Confluent with Mixin {
val bar = Foo("npe")
}
new System2 // boom!!
Now I use both bar and global very heavily, and therefore I don't want to pay a lazy-val tax just because Scala (2.9.2) doesn't get the initialisation right. What to do?
You can use an early initializer:
class System1 extends {
val bar = Foo("npe")
} with Mixin with Confluent {
// ...
}
scala> new System1
res3: System1 = System1#1d0bfedd
Could you please explain why the following code is not working as expected.
The actor is not printing the message
Thanks.
class Base {
def f() = { "This is Base" }
}
class Sub extends Base {
override def f() = { "This is Sub" }
}
case class myCase(x: Base)
import scala.actors._
object myActor extends Actor {
def act()
{
loop {
react {
case myCase(x) => x.f()
case msg => "unknown"
}
}
}
def main(args: Array[String]): Unit = {
this ! myCase(new Base)
this ! myCase(new Sub)
}
}
Answering the question
The problem has nothing whatsoever to do with inheritance or case classes (I have re-titled the question to reflect this). The code does not print anything for two reasons:
Because your code does not actually make any calls to println! Replace:
case myCase(x) => x.f()
with
case myCase(x) => println( x.f() )
Because you do not start your actor. I think your program would make more sense if the actor were an inner class:
object myActor extends App {
class MyActor extends Actor {
def act() {
loop {
react {
... // <-- You need to print stuff
}
}
}
}
val a = new MyActor
a.start() // <-- You need to start it
a ! myCase(new Base)
a ! myCase(new Sub)
}
Advice: case classes and inheritance
I would, however, offer the advice that using inheritance in the presence of case classes is a bad idea. I usually use the approach of declaring common behaviour/state in a trait:
sealed trait Base {
def f(): Unit
}
case class Sub() extends Base
Why is it a bad idea? Well, one of the contracts that case-classes give you is a rigid definition of equivalence (that is, an equals and hashCode implementation). In the presence of inheritance, this could well be misleading. That is, your code will probably not do what you expect. Consider the following;
scala> abstract class Base { val x: Int }
defined class Base
scala> case class Sub(s: String) extends Base { val x = util.Random.nextInt(100) }
defined class Sub
Now if I create 2 instances...
scala> Sub("Hey")
res2: Sub = Sub(Hey)
scala> Sub("Hey")
res3: Sub = Sub(Hey)
They are equivalent
scala> res2 == res3
res4: Boolean = true
But they do not have the same state
scala> res2.x
res5: Int = 28
scala> res3.x
res7: Int = 15
Note, I am not saying this is a bug. I'm just saying it's an area where you might find that you introduce a bug in your code because you have made the assumption that any state of the case class is included in its equivalence.