It looks like type-safe builder pattern. See this answer.
In your case:
trait TTrue
trait TFalse
class Myclass[TA, TB, TC] private(){
def withServiceA(x: => Unit)(implicit e: TA =:= TFalse) = {x; new Myclass[TTrue, TB, TC]}
def withServiceB(x: => Unit)(implicit e: TB =:= TFalse) = {x; new Myclass[TA, TTrue, TC]}
def withServiceC(x: => Unit)(implicit e: TC =:= TFalse) = {x; new Myclass[TA, TB, TTrue]}
def withClient(x: => Unit)(implicit e1: TA =:= TTrue, e2: TB =:= TTrue) = x
}
object Myclass{
def apply() = new Myclass[TFalse, TFalse, TFalse]
}
Usage:
Myclass()
.withClient(println("withClient"))
//<console>:22: error: Cannot prove that TFalse =:= TTrue.
// .withClient(println("withClient"))
// ^
Myclass()
.withServiceB(println("with B"))
.withServiceA(println("with A"))
.withClient(println("withClient"))
//with B
//with A
//withClient
Myclass()
.withServiceA(println("with A"))
.withServiceC(println("with C"))
.withServiceB(println("with B"))
.withClient(println("withClient"))
//with A
//with C
//with B
//withClient
Myclass()
.withServiceC(println("with C"))
.withServiceB(println("with B"))
.withServiceA(println("with A"))
.withServiceC(println("with C2"))
.withClient(println("withClient"))
//<console>:25: error: Cannot prove that TTrue =:= TFalse.
// .withServiceC(println("with C2"))
// ^
You could provide custom error messages with custom replacements for =:=
class.
If you want to be sure that after every Myclass.apply
withClient
will be called, you could call it manually like this:
sealed class Context private()
object Context {
def withContext(f: Context => Myclass[TTrue, TTrue, _])(withClient: => Unit) =
f(new Context).withClient(withClient)
}
object Myclass{
def apply(c: Context) = new Myclass[TFalse, TFalse, TFalse]
}
Usage:
Context
.withContext(
Myclass(_)
.withServiceA(println("with A"))
.withServiceC(println("with C"))
.withServiceB(println("with B"))
)(println("withClient"))
On ideone.
One can't create Myclass
outside of withContext
method and withClient
will be called at least once.