Scala check compilation time for location of constructor calls

I have code that has an invariant that the object must be constructed in the function in which it is ultimately used (for various reasons related to the global state, which are not ideal, but are part of the assumption).

eg. Suppose there is a boo function below that is responsible for controlling moo.

def boo(mooGen: () => Moo) { val m = mooGen() // a new MOO must be created HERE m.moo() } 

Boo clients who want to use boo must pass type () => Moo, where the function generates the desired Moo.

Ideal customer behavior:

 boo( () => new Moo(// specific parameters here) ) 

Moo is not created until inside the body is boo.

However, the client can easily make a mistake with the following code:

 val myMoo = new Moo(// specific parameters here) boo( () => myMoo) 

This violates the invariant where we want the moo construct to take place only in boo.

So basically, I want to determine if the return value of mooGen was created in the function call stack or whether it was created in advance.

There are many ways to check this at runtime. However, is there a way to force this pattern at compile time? Using implicits or anything else smart?

Any ideas appreciated!

+8
scala
source share
2 answers

Put boo and Moo in your own object along with a Token class that cannot be created outside of 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 do not want cannot 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 him to, unfortunately, still get his Moo:

 val ireallywantone = new Foo.Moo(null.asInstanceOf[Foo.Token]) 
+12
source share

I think if both your Moo constructor and the boo method are under your control and should not be written by clients, then you can make Moo accept an implicit parameter and arrange this the only place where a suitable implicit value is in scope is in boo .

This is not ideal ... and you cannot make the type of the implicit parameter completely private (which would make it much more certain that the client does not create an instance of Moo outside of boo ), since I suspect the compiler will complain about the private type flowing in the definition of Moo . But even without this, it should at least help prevent accidental creation of Moo outside of boo ; the client will have to intentionally get an implicit value to allow them to create a Moo .

+3
source share

All Articles