I have some code that has the invariant that an object must be constructed in the function where it is ultimately used (for various reasons related to global state that aren't ideal but are part of the assumption).
e.g. Suppose there is the function boo below that is responsible for manipulating moo.
def boo(mooGen: () => Moo) {
val m = mooGen() // a new MOO must be created HERE
m.moo()
}
Clients of boo that want to use boo must pass in a type of () => Moo, where the function generates the desired Moo.
Ideal client behavior:
boo( () => new Moo(// specific parameters here) )
The Moo is not created until inside the boo body.
However, a client can easily misstep with the following code:
val myMoo = new Moo(// specific parameters here)
boo( () => myMoo)
This breaks the invariant where we want the moo construction to occur only in boo.
So basically, I want to determine if the return value of mooGen is created within the call stack of the function or whether it was created beforehand.
There are many ways to verify this at runtime. However, is there any way to force this pattern at compile time? Using implicits or anything else clever?
Any ideas are appreciated!
Put boo and Moo into their own object, together with a Token class, that can't be instantiated outside the object.
scala> object Foo {
| class Moo(token:Token) {}
| class Token private[Foo]()
| def boo(mooGen: (Token) => Moo) {val m = mooGen(new Token)}
| }
defined module Foo
Now what you want can be done:
scala> Foo.boo(new Foo.Moo(_))
And what you don't want can't be done:
scala> val mymoo = new Foo.Moo(new Foo.Token)
<console>:8: error: constructor Token in class Token cannot be accessed in objec
t $iw
val mymoo = new Foo.Moo(new Foo.Token)
^
But if the client really wants to he can - unfortunately - still get his Moo:
val ireallywantone = new Foo.Moo(null.asInstanceOf[Foo.Token])
I guess if both the constructor of Moo
and the method boo
are under your control, and don't need to be written by clients, then you can make Moo
take an implicit parameter, and arrange that the only place that a suitable implicit value is in scope is in boo
.
It's not ideal... and you may not be able to make the type of the implicit parameter fully private (which would make it a lot more certain that the client doesn't instantiate a Moo
outside of boo
), because I suspect the compiler will complain about a private type leaking out in the definition of Moo
. But even without that, it should at least help you prevent accidental creation of Moo
s outside of boo
; the client would have to deliberately get an implicit value to allow them to create a Moo
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With