I have the following macro defining a class and returning an instance of that class (with Scala 2.10.2 and the macro plugin):
def test[T] = macro testImpl[T]
def testImpl[T : c.WeakTypeTag](c: Context): c.Expr[Any] = {
import c.universe._
val className = newTypeName("Test")
c.Expr { q"""
class $className {
def method = 1
}
new $className
"""}
}
When I call the macro:
case class Cat(name: String)
val t = test[Cat].method
I get the following error:
method method in class Test cannot be accessed in Test
val t = test[Cat].method
^
My overall goal is to use vampire methods and to use quasi-quotes to describe the generated class. How can I solve this error?
In my post on vampire methods I mention this workaround for this bug. For some reason you currently aren't able to see an anonymous class's methods on the instance returned from the macro unless you create a wrapper class that extends the class with the methods and return an instance of that, instead.
You're seeing the same bug from a slightly different angle. You've named the class with the methods you want to see on the returned instance's structural type, but you still need a wrapper. The following will work:
c.Expr { q"""
class $className {
def method = 1
}
new $className {}
"""}
Note that all I've done is add a pair of brackets to the line creating the instance, so that I get an instance of an anonymous class extending $className instead of just a $className.
I have no idea what's behind this bug, and I'm not sure if Eugene knows more. I did recently confirm that it's still around in the latest build of 2.11.
Related
For a mocked class I have a method for which I would like to test whether there are no more interactions then needed, which looks similar to:
def someMethod(someMandatoryParam: Int, canBeDefaultIds: Option[Ids] = None): Future[Failures] = {...}
when I am mocking to invoke this method without the default parameter and I verify it that way:
verify(someClass).someMethod(someInt)
and then check if there was no more interactions:
verifyNoMoreInteractions(someClass)
I am getting an error that here was some unexpected interactions.
But when in implementation I change this method to use None instead of default value and verify:
verify(someClass).someMethod(someInt, None)
verifyNoMoreInteractions(someClass)
It works correctly.
Is there a problem with Mocikto and default values in Scala?
Default arguments is Scala specific feature which Java Mockito is likely not aware of. Consider how Scala code looks after -Xprint:jvm phase
abstract trait SomeClass extends Object {
def someInt(a: Option): Option = a;
<synthetic> def someInt$default$1(): Option = scala.None;
}
Notice how the default argument became just another method someInt$default$1. Try using mockito-scala which is designed with Scala in mind, for example the following test passes
import org.mockito.{ArgumentMatchersSugar, IdiomaticMockito}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
trait SomeClass {
def someInt(a: Option[Int] = None) = a
}
class MockitoScalaDefaultArgsSpec extends AnyFlatSpec with Matchers with IdiomaticMockito with ArgumentMatchersSugar {
"mockito-scala" should "handle default arguments" in {
val someClass = mock[SomeClass]
someClass.someInt()
someClass.someInt() was called
someClass wasNever calledAgain
}
}
How can one access the annotation of a singleton object given a string?
I am using Scala version 2.11.6 and plan to upgrade to 2.12 after sorting this issue out.
Example
Given an (java) annotation
public #interface Marked {
String name();
}
and a simplified factory interface
trait Instance { def doSomething(): Unit }
trait Factory { def create(): Instance }
I would like to check whether a dynamically loaded singleton object is annotated and with what value. The singleton object will be put in the classpath and may be defined as such:
class A extends Instance { override def doSomething() { println("A is done.") } }
#Marked(name = "My A Factory")
object AFactory extends Factory {
override def create(): Instance = new A()
}
What I tried
I can only access the module (singleton object) dynamically and in this way fail to access the annotation. Mostly due to lack of comprehensive documentation I have tried the following expressions which all somehow return an empty list.
import scala.reflect.runtime.{universe => ru}
val name = "AFactory$" // This is automatically provided.
val rm = ru.runtimeMirror(getClass.getClassLoader)
val classSym = rm.staticClass(name)
println(classSym.annotations)
println(classSym.companion.annotations)
println(classSym.baseClasses(0).annotations)
println(classSym.baseClasses(0).companion.annotations)
val moduleSym = rm.staticModule(name)
println(moduleSym.companion.annotations)
println(moduleSym.asModule.annotations)
println(moduleSym.asModule.moduleClass.annotations)
println(moduleSym.asModule.moduleClass.companion.annotations)
You need to make the JVM retain the annotation during runtime, otherwise it wont work =>
#Retention(RetentionPolicy.RUNTIME)
public #interface Marked {
}
With this change, your code worked.
Edit:
The reason is, that the default retention is "Class", which will make the compiler retain the annotaion in the classfile, but it wont be accessible during the runtime.
SLS 5.1 says "Delayed Initializaton. The initialization code of an object or class (but not a trait) that follows the superclass constructor invocation and the mixin-evaluation of the template’s base classes is passed to a special hook, which is inaccessible from user code. Normally, that hook simply executes the code that is passed to it. But templates inheriting the scala.DelayedInit trait can override the hook by re-implementing the delayedInit method, which is defined as follows:"
def delayedInit(body: => Unit)
The ScallopConf command-line parser extends DelayedInit and using it according to the docs generates the warning Selecting value apples from class Conf, which extends scala.DelayedInit, is likely to yield an uninitialized value.
How should the following simple example be rewritten so the warning is not generated?
import org.rogach.scallop._
class Conf(arguments: Seq[String]) extends ScallopConf(arguments) {
val help = opt[Boolean](name = "help", short = 'h', descr = "Help me please.")
}
object Gen {
def main(args: Array[String]) {
val conf: Conf = new Conf(args)
if (conf.help()) {
println(s"""Usage: Gen [--help]""")
sys.exit(-1)
}
println("Do some work here")
}
}
Making help a lazy val and calling conf.afterInit() before accessing conf.help should clear the compiler warnings.
On my scala code, I want to be able to instantiate a new class. For instance, supose I have the code below:
class Foo { def foo=10 }
trait Bar { val bar=20 }
Ideally, I want to be able to do something like:
def newInstance[A <: Foo] = { new A with Bar }
newInstance[Foo]
But, of course this doesn't work. I tried to use reflection to instantiate a class, but it seems that I'm only able to instantiate a new class (and not mix-in with a trait). I think it would be possible to make this work using Macros, but I'm not sure even where to start.
What I'm trying to do is like the following Ruby code:
class SomeClass
def create
self.class.new
end
end
class Other < SomeClass
end
Other.new.create # <- this returns a new Other instance
Is it possible?
With a macro:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object MacroExample {
def newInstance[A <: Foo]: A with Bar = macro newInstance_impl[A]
def newInstance_impl[A <: Foo](c: Context)(implicit A: c.WeakTypeTag[A]) = {
import c.universe._
c.Expr[A with Bar](q"new $A with Bar")
}
}
This will work as expected, and will fail at compile time if you try to instantiate a class that doesn't have a no-argument constructor.
I've used quasiquotes here for the sake of clarity, but you could build the tree manually with a little more work. There's not really any good reason to, though, now that quasiquotes are available as a plugin for Scala 2.10.
I've looked at example of logging in Scala, and it usually looks like this:
import org.slf4j.LoggerFactory
trait Loggable {
private lazy val logger = LoggerFactory.getLogger(getClass)
protected def debug(msg: => AnyRef, t: => Throwable = null): Unit =
{...}
}
This seems independent of the concrete logging framework. While this does the job, it also introduces an extraneous lazy val in every instance that wants to do logging, which might well be every instance of the whole application. This seems much too heavy to me, in particular if you have many "small instances" of some specific type.
Is there a way of putting the logger in the object of the concrete class instead, just by using inheritance? If I have to explicitly declare the logger in the object of the class, and explicitly refer to it from the class/trait, then I have written almost as much code as if I had done no reuse at all.
Expressed in a non-logging specific context, the problem would be:
How do I declare in a trait that the implementing class must have a singleton object of type X, and that this singleton object must be accessible through method def x: X ?
I can't simply define an abstract method, because there could only be a single implementation in the class. I want that logging in a super-class gets me the super-class singleton, and logging in the sub-class gets me the sub-class singleton. Or put more simply, I want logging in Scala to work like traditional logging in Java, using static loggers specific to the class doing the logging. My current knowledge of Scala tells me that this is simply not possible without doing it exactly the same way you do in Java, without much if any benefits from using the "better" Scala.
Premature Optimization is the root of all evil
Let's be clear first about one thing: if your trait looks something like this:
trait Logger { lazy val log = Logger.getLogger }
Then what you have not done is as follows:
You have NOT created a logger instance per instance of your type
You have neither given yourself a memory nor a performance problem (unless you have)
What you have done is as follows:
You have an extra reference in each instance of your type
When you access the logger for the first time, you are probably doing some map lookup
Note that, even if you did create a separate logger for each instance of your type (which I frequently do, even if my program contains hundreds of thousands of these, so that I have very fine-grained control over my logging), you almost certainly still will neither have a performance nor a memory problem!
One "solution" is (of course), to make the companion object implement the logger interface:
object MyType extends Logger
class MyType {
import MyType._
log.info("Yay")
}
How do I declare in a trait that the
implementing class must have a
singleton object of type X, and that
this singleton object must be
accessible through method def x: X ?
Declare a trait that must be implemented by your companion objects.
trait Meta[Base] {
val logger = LoggerFactory.getLogger(getClass)
}
Create a base trait for your classes, sub-classes have to overwrite the meta method.
trait Base {
def meta: Meta[Base]
def logger = meta.logger
}
A class Whatever with a companion object:
object Whatever extends Meta[Base]
class Whatever extends Base {
def meta = Whatever
def doSomething = {
logger.log("oops")
}
}
In this way you only need to have a reference to the meta object.
We can use the Whatever class like this.
object Sample {
def main(args: Array[String]) {
val whatever = new Whatever
whatever.doSomething
}
}
I'm not sure I understand your question completely. So I apologize up front if this is not the answer you are looking for.
Define an object were you put your logger into, then create a companion trait.
object Loggable {
private val logger = "I'm a logger"
}
trait Loggable {
import Loggable._
def debug(msg: String) {
println(logger + ": " + msg)
}
}
So now you can use it like this:
scala> abstract class Abstraction
scala> class Implementation extends Abstraction with Loggable
scala> val test = new Implementation
scala> test.debug("error message")
I'm a logger: error message
Does this answer your question?
I think you cannot automatically get the corresponding singleton object of a class or require that such a singleton exists.
One reason is that you cannot know the type of the singleton before it is defined. Not sure, if this helps or if it is the best solution to your problem, but if you want to require some meta object to be defined with a specific trait, you could define something like:
trait HasSingleton[Traits] {
def meta: Traits
}
trait Log {
def classname: String
def log { println(classname) }
}
trait Debug {
def debug { print("Debug") }
}
class A extends HasSingleton[Log] {
def meta = A // Needs to be defined with a Singleton (or any object which inherits from Log}
def f {
meta.log
}
}
object A extends Log {
def classname = "A"
}
class B extends HasSingleton[Log with Debug] { // we want to use Log and Debug here
def meta = B
def g {
meta.log
meta.debug
}
}
object B extends Log with Debug {
def classname = "B"
}
(new A).f
// A
(new B).g
// B
// Debug