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…
@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.
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.
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.
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.
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.