5

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?

bey
  • 51
  • 1
  • Related: https://stackoverflow.com/questions/59428409/how-are-nested-functions-and-lexical-scope-compiled-in-jvm-languages – user Jun 05 '20 at 19:00
  • Specifiers apply to "members of classes, objects, traits and packages". A locally defined function is none of those, so it has no specifier https://en.wikibooks.org/wiki/Scala/Access_modifiers – pidge Jun 05 '20 at 19:40

3 Answers3

4

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.

user
  • 7,435
  • 3
  • 14
  • 44
2

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.

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
  • 4
    Is that actually guaranteed by the Scala Language Specification and true for every implementation that exists, has ever existed, and will ever exist? Or is it an incidental private internal implementation detail of one specific version of one specific implementation? – Jörg W Mittag Jun 05 '20 at 17:35
2

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.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66