Visibility of methods defined in an anonymous object expression

Here’s a toy example of a problem that bit me:

import collection.mutable

trait PublicRelationsFace:
  def meetAndGreet(fee: BigInt): String
  def issueQuote: String

trait Organisation:
  private var slushFund: BigInt = 0

  def spinIt(dealingWithThem: PublicRelationsFace => Unit): Unit =
    val scarfolkCouncilSpokesperson = new PublicRelationsFace:
      private val thoughts = mutable.ListBuffer.empty[String]

      def meetAndGreet(fee: BigInt): String =
        slushFund += fee

        thoughts += "Is Elon on our gift list yet? Do we have New Zealand passports, even?"

        "Everything is fine. Trust your owners. For more information, please re-read."
      end meetAndGreet

      def reportBackAssessment: String = thoughts.mkString("\n")

    dealingWithThem(scarfolkCouncilSpokesperson)

    println(
      scarfolkCouncilSpokesperson.reportBackAssessment
    ) // Why isn't this accessible?
  end spinIt

The intent is to provide a public API visible within a callback where the state of object providing the API is local to the method making the callback, and has some additional action(s) or queries() interacting with the state that shouldn’t be accessible within callback code.

In Java, such public methods defined only in anonymous class instances are accessible provided the ascribed type of the instance isn’t explicitly given as the superclass - so PublicRelationsFace in this example.

It seems this isn’t the case in Scala 3 (possibly not even in Scala 2 either?).

I tried fiddling with making the trait transparent, but that doesn’t work.

Is this supported in the language? If so, what’s the way of expressing this?

Note, I can always refactor to introduce an intermediate or mix-in trait for reportBackAssessment, or even hoist the state out into the body of spinIt / make reportBackAssessment a local helper function. I’m curious as to whether this is ruled out by design, or is a bug or loose end in the language specification…

not a direct answer, but you can replace that with a method local object, i.e. something like:

object scarfolkCouncilSpokesperson extends PublicRelationsFace

and have access to all members of that object in that method.

it works in scala 2, but doesn’t work in scala 3. try following code in https://scastie.scala-lang.org/ with worksheet mode on:

val x = new Object {
  def printHello(): Unit = println("Hello, World!")
}
x.printHello()

in ‘build settings’ panel you can choose between scala 2 and scala 3.

p.s. ok, i’ve found it - here’s the situation in scala 3: Type Inference | Scala 3 Migration Guide | Scala Documentation so i guess it’s a full answer now :slight_smile:

1 Like

@tarsa Thanks - as it happens, I often use the local object approach for other reasons, but I had no idea that it afforded the extra visibility into the bargain; that’s a nice solution.

Regarding that link to structural types, I’m not completely sure that would cover this - see this link about instance creation expressions. There is a line:

In the latter case, the concrete self type of the expression is the compound 
type T with x.type.

I read that to mean the flattened public signatures of the anonymous class, including the additional ones added in the instance creation syntax, accessible directly without reflection / selectable or whatever.

Perhaps this is deliberate, down to some subtlety about path-dependent types; I’m not sure what the meaning of:

    val aliased = scarfolkCouncilSpokesperson
    aliased.reportBackAssessment

would be if the type of scarfolkCouncilSpokesperson was actually something more specialised than PublicRelationsFace.

Anyway, that’s one for the lawyers to debate - your solution works for me.

Note that accessing those methods would incur in reflection, since there is no a proper type for it.

The object approach also avoids that.

if you have object myObject extends Whatever { ...extra stuff... } then the type of myObject is myObject.type, so you can write e.g.

object myObject extends Whatever {
  def extraMethod = 5
   ...extra stuff...
}
val myVal: myObject.type = myObject
myVal.extraMethod // not only works, but also doesn't use reflection iirc

i forgot to add that initialization of scala objects is lazy, so you need to pay the performance price for that on local objects (laziness implies reads from volatile variables, initialization locks etc). if the code is on hot path then that could matter. i don’t know, but it’s possible that global objects (i.e. true singletons) are implemented using Initialization-on-demand holder idiom - Wikipedia which has good perf in java due to (let’s say) smart jit compiler.

1 Like

Not to worry there - it is true that these objects are constructed on a hot path, but it’s an outer loop (each object is executes three lumps, to use the terminology of my other post about threading). The lumps are where the overwhelming majority of time is spent.

As it happens, execution time has improved modestly with the changes that introduced this: Comparing main...issue-132-longest-common-subsequence · sageserpent-open/kineticMerge · GitHub. Local object swathes is the perpetrator.

If anyone is of a fainting disposition, they should not follow that link. It’s a long way off from beautiful code. :laughing:

In Scala 2 you could do new { def bar = 42 }.bar without reflective calls because the compiler can use the “anonymous” class that it just created. But only if the method access is in the same expression as the creation of the object. I believe it’s the same in Java, but perhaps Java expanded it to cross-expression access when they added var.