In Scala we can define functions/methods inside other functions/methods in same class, and those functions are locally scoped to the functions/methods they are defined to, unable to access outside of from the function they are defined inside. Would that make those locally defined functions private access specifier in Scala?
Those functions are basically local variables, and as such don't have access modifiers. There's no real need to give them modifiers since the method inside which they are defined is the only place where they can be accessed, and they're only used in a very small part of your application. I suppose they are private by default, if you take private to mean that they can only be accessed in the lowest scope they are in.
As #Luis Miguel Mejía Suárez and #Mario Galic pointed out, these local/nested functions get turned into private (final) methods in the same class. So in a way, yes, they are private (to the class they are in, not the enclosing method), but you cannot actually access them from other methods in the same class without using reflection or something else.
Executing scalac -Xshow-phases outputs compiler phases and the following seems interesting
lambdalift 17 move nested functions to top level
For example, running scalac -Xprint:lambdalift on
class O {
def f() = {
def nested() = 42
nested()
}
}
outputs on my machine with Scala 2.13.2 something like
class O {
def f(): Int = nested();
private def nested(): Int = 42;
}
where we see nested method became private member method of the enclosing class. You could try exploring what happens for inner functions using the same technique.
On language level according to Scala specification there are only the following access modifiers
https://scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#modifiers
<no modifier>
private
private[C]
private[this]
protected
protected[C]
protected[this]
So on language level the answer for your question is that there is no access modifier specific for nested methods.
On implementation level there are plenty of modifiers and flags (some of them are access modifiers)
https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/internal/Flags.scala
PROTECTED
PRIVATE
LOCAL
SEALED
METHOD
...
We can peep on compiler if we create an annotation
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
#compileTimeOnly("enable macro paradise")
class printMethodModifiers extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro PrintMethodModifiersMacro.impl
}
object PrintMethodModifiersMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
val traverser = new Traverser {
override def traverse(tree: Tree): Unit = {
tree match {
case q"${mods: Modifiers} def $_[..$_](...$_): $_ = $_" =>
println(s"tree=$tree, mods.flags=${mods.flags}")
case _ => ()
}
super.traverse(tree)
}
}
traverser.traverse(annottees.head)
q"..$annottees"
}
}
and use it to traverse the tree of class
#printMethodModifiers
class O {
def f() = {
def nested() = 42
nested()
}
}
//Warning:scalac: tree=def <init>() = {
// super.<init>();
// ()
//}, mods.flags=0
//Warning:scalac: tree=def f() = {
// def nested() = 42;
// nested()
//}, mods.flags=0
//Warning:scalac: tree=def nested() = 42, mods.flags=0
So when macro annotations are expanded in typer phase there is no difference in flags for methods and nested methods.
Related
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
I always thought that macro declarations and implementation need to be defined as in the tutorials:
object Impl {
def m(c: Context)(body: c.Expr[Unit]): c.Expr[T] = ???
}
class Usage {
def m(body: Unit): T = macro Impl.m = ???
}
However now I came across:
class RecordMacros(val c: Context) {
import c.universe._
def apply_impl[Rep: c.WeakTypeTag](method: c.Expr[String])(v: c.Expr[(String, Any)]*): c.Expr[Any] = ???
}
source: https://github.com/TiarkRompf/virtualization-lms-core/blob/macro-trans/src/common/Records.scala
What is the difference, is it about refactoring the context out from each method?
Also the class doesn't seem to be instantiated before the method is accessed.
Thanks
They are called macro bundles available in 2.11 only and yes that's what they are for. http://docs.scala-lang.org/overviews/macros/bundles.html
I have a trait with a member type, and want to have a macro with signature containing this type:
trait Foo {
class Bar[A] { ... }
def baz[A](x: Bar[A]): Bar[A] = macro bazImpl[A]
def bazImpl[A: c.WeakTypeTag](c: blackbox.Context)(x: c.Expr[Bar[A]]) = ...
}
This doesn't work, since bazImpl must belong either to a static (i.e. non-member) object or to a macro bundle. But in neither case do I have a foo: Foo so I could write foo.Bar[A].
One workaround I can think of is to use Foo#Bar[A] and add casts:
trait Foo {
class Bar[A] { ... }
def baz[A](x: Bar[A]): Bar[A] = Foo.baz1(x).asInstanceOf[Bar[A]]
def bazImpl[A: c.WeakTypeTag](c: blackbox.Context)(x: c.Expr[Bar[A]]) = ...
}
object Foo {
def baz1[A](x: Foo#Bar[A]): Foo#Bar[A] = macro bazImpl[A]
def bazImpl[A: c.WeakTypeTag](c: blackbox.Context)(x: c.Expr[Foo#Bar[A]]): c.Expr[Foo#Bar[A]] = ...
}
But I'd like to avoid it (both because it's not exactly type-safe and because the real case is more complicated). Any alternatives?
If you're using Scala 2.11, just write c.Tree everywhere instead of c.Expr. That will cut down the boilerplate of specifying the very same types in macro impls as in macro defs at virtually zero cost. The only potential downside of trees in comparison with exprs is reify, which requires splicees to be exprs, but since most of metaprograms in Scala are written with quasiquotes these days, chances are that this won't matter to you.
Is it possible to add method definition from macro call? Here is an example:
object Macros { def someMacro = ...}
trait Foo { def foo:Int }
class Bar extends Foo { someMacro() }
//calls to (new Bar).foo is possible and return value defined in someMacro
This is indeed possible with Scala macro annotations. One thing that macro annotations enable is adding members to class definitions. Since macros are compiled before other steps of type checking, you can use this functionality to satisfy a trait's constraints.
For example, the following macro annotation #foo adds a foo method to its annotated classes. Here, the method takes no arguments and returns an Int:
object Macros {
def fooImpl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val fooMethod = DefDef(NoMods, newTermName("foo"), List(), List(List()), TypeTree(), Literal(Constant(5)))
c.Expr[Any](annottees.map(_.tree).toList match {
case List(ClassDef(mods, name, tdefs, Template(parents, self, body))) =>
ClassDef(mods, name, tdefs, Template(parents, self, body :+ fooMethod))
})
}
}
class foo extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro Macros.fooImpl
}
Then you can use the #foo annotation to satisfy some Trait definition:
trait Foo { def foo: Int}
#foo
class Bar extends Foo //compiles
(new Bar).foo //5
You can read more about Macro annotations here.
You cannot override an abstract method with a macro. However, you can override a concrete method, even if that concrete method itself overrides an abstract method. So, if you introduce an intermediary trait with a default implementation, you can have the desired outcome. For example:
object Macros {
def someMacro(c: Context): c.Expr[Int] = {
import c.universe._
c.Expr[Int](Literal(Constant(5)))
}
}
trait Foo {
def foo: Int
}
trait FooWithDefault extends Foo {
override def foo: Int = 0
}
class Bar extends FooWithDefault {
override def foo: Int = macro Macros.someMacro
}
(new Bar).foo //5
This does lead to some unexpected behavior, so be careful:
val b: Foo = new Bar
b.foo //java.lang.AbstractMethodError
val b: FooWithDefault = new Bar
b.foo //java.lang.AbstractMethodError
This is explained in futher detail in the issue SI-7657, where it is explained:
Currently we allow macros to override non-abstract methods (in order
to provide performance enhancements such as foreach for
collections), and we also disallow macros to override abstract methods
(otherwise downcasting might lead to AbstractMethodErrors).
This patch fixes an oversight in the disallowing rule that prohibited
macros from overriding a concrete method if that concrete method
itself overrides an abstract method. RefCheck entertains all
overriding pairs, not only the immediate ones, so the disallowing rule
was triggered.
Now macros can override abstract methods if and only if
either the base type or the self type contain a matching non-abstract
method.
I'm trying to define a macro in a trait. For example,
trait Macro {
val magic: Int = 1001
def doMagic(x: Int): Int = macro Impl.doMagic
}
object Impl {
def doMagic(c: Context)(x: c.Expr[Int]): c.Expr[Int] = ???
}
What I'm trying to do here is, the doMagic implementation should be able to refer to the field magic in the trait Macro, type-safely.
I tried to refer to this by This(c.enclosingClass.asModule.moduleClass), but it seems to refer the tree of macro application, which resulting an anonymous class called $iw. So in my understanding the Enclosures is where the macro is being applied, not where it's being defined.
Is there anyway I can do this?
This does not answer the question directly, but maybe it helps nonetheless. You could pass the instance as parameter to the macro and to hide this from the caller you could add a corresponding method delegating to doMagic like this:
trait Macro {
val magic: Int = 1001
def doMagic(m: Macro, x: Int): Int = macro Impl.doMagic
}
object Impl {
def doMagic(context: Context)(m: context.Expr[Macro], x: context.Expr[Int]): context.Expr[Int] = {
import context.universe._
reify(m.splice.magic + x.splice)
}
}
the wrapper-trait:
trait Macro2 extends Macro {
def doMagic2(x: Int): Int = doMagic(this, x)
}
and the caller:
val z = new Macro2 {}
println(z.doMagic2(5))
I sorted it out. We should use c.prefix, like
val self = TermName(c.freshName("self"))
c.Expr[T] {
q"""
val $self = ${c.prefix.tree}
...
"""
}
And a self variable can be used to capture the prefix, so it's only evaluated once.