Maybe someone has some clever way for me to do the following.
package MyLib
class Foo {
lazy val dontTouchInside : Int = ???
}
Foo is library class that I’m exposing to users from which they can inherit.
The dontTouchInside lazy value must only be accessed post-construction of Foo or internally with [MyLib] privileges.
Is there some way I can enforce with the compiler’s help that new Foo{}.dontTouchInside works, but new Foo { dontTouchInside} fails?
A side note: the above would not be needed if Scala had some kind of onCreate capability.
package MyLib
trait Post
@scala.annotation.implicitAmbiguous("This definition may be called only post-construction")
implicit object GeneralPost extends Post
class Foo {
implicit object ForceAmbiguity extends Post
private lazy val _postConstruction = {
//Something to be used once at Post-Construction
0
}
def postConstruction(implicit post : Post) : Int = _postConstruction
}
User code
new Foo {}.postConstruction //compiles
new Foo {postConstruction} //Error: This definition may be called only post-construction
So all public members that must be called post-construction must have a Post implicit (so the implicit also helps to document the constraint for the member).
The implementation above did not allow multiple definitions with the implicit Post calling one another. Here is a fixed implementation that still uses ambiguity slightly differently with the help of shapeless’s LowPriority:
Generic solution
trait PostConstruction
object PostConstruction {
implicit def ev(implicit lp : shapeless.LowPriority) : PostConstruction = new PostConstruction {}
}
trait HasPostConstructionOnlyMembers {
@scala.annotation.implicitAmbiguous("This is a post-construction definition only!")
final protected implicit def __PostConstruction1(implicit lp : shapeless.LowPriority) : PostConstruction = new PostConstruction {}
final protected implicit def __PostConstruction2(implicit lp : shapeless.LowPriority) : PostConstruction = new PostConstruction {}
}
Library
class DSLClass extends HasPostConstructionOnlyMembers {
private lazy val runOncePostConstruction : Int = 0 //Do something here
protected def post1(implicit p : PostConstruction) : Int = runOncePostConstruction
def post2(implicit p : PostConstruction) : Int = post1
}
User code
new DSLClass{}.post2 //compiles
new DSLClass{post1} //error: This is a post-construction definition only!
new DSLClass{post2} //error: This is a post-construction definition only!