scala - How do I make a typesafe builder that doesn't need an explicit build method? -
i using slight abuse of builder pattern make fluent imperative execution chain. after way make compile error forget execute method @ end. goal following
withservicea { dostuff() } withserviceb { dostuff() } withclient client
withservicea
, withserviceb
can both return values, if return value used obvious if return type wrong, if used imperatively, whole object falls on floor silently. want ensure forgetting withclient
call compile error no matter context used in.
i want able skip blocks if unneeded , put them in arbitrary order looking replace nested inner class pattern using ala
def onservicea[a](body: servicea => a) = new { def onserviceb[b >: a](body: serviceb => b) = {b => { dostuff() } }
it looks type-safe builder pattern. see this answer.
in 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 tfalse =:= ttrue. // .withclient(println("withclient")) // ^ myclass() .withserviceb(println("with b")) .withservicea(println("with a")) .withclient(println("withclient")) //with b //with //withclient myclass() .withservicea(println("with a")) .withservicec(println("with c")) .withserviceb(println("with b")) .withclient(println("withclient")) //with //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 ttrue =:= tfalse. // .withservicec(println("with c2")) // ^
you provide custom error messages custom replacements =:=
class.
if want sure after every myclass.apply
withclient
called, call manually 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"))
one can't create myclass
outside of withcontext
method , withclient
called @ least once.
Comments
Post a Comment