While this won't win the beauty contest, something like this can be done in a reasonably simple way using the Object-Oriented Mathematica extension described here.
To keep this post self-contained, I will include the code to install the actual version of the OO extension I will use here (be aware that it will also install the ProjectInstaller package of mine):
Import["https://raw.github.com/lshifr/ProjectInstaller/master/BootstrapInstall.m"]
Needs["ProjectInstaller`"]
ProjectInstall[URL["https://gist.github.com/4266126/download"]]
This has to be done only once. Alternatively, you can go directly to the gist using the above URL, copy OO.m
from there and place into $UserBaseDirectory / Applications
, or any other place where Mathematica can find it.
Now, it can be loaded as
Needs["OO`"]
Here is the code for the toy classes Bond
and BondBuilder
:
DeclareType[Bond][
getNominale[] := $content[[1]]
,
getStartDate[] := $content[[2]]
]
and
DeclareType[BondBuilder][
Nominale[x_?NumericQ] :=
Module[{copy = $content},
copy[[1]] = x;
$content = copy;
$self
]
,
StartDate[date : {__Integer}] :=
Module[{copy = $content},
copy[[2]] = date;
$content = copy;
$self
]
,
Build[] := With[{data = $content}, Bond[data]]
]
admittedly, the syntax for setting class fields is currently rather ugly. The first field holds the nominal, and the second holds the date. The system variable $content
is assumed to keep a list of the two.
As you can see, I have incorporated the argument checks, just to show how this is done - pretty much in the same way as usually done with patterns in Mathematica.
Here is how we create an empty builder:
builder = BondBuilder[{Null, Null}]
(* « BondBuilder[]» *)
Since I did not bother with left associativity in OO.m
, the chained method application will look ugly, since we would need parentheses. Alternatively, one could use Fold
and Compose
, which is what I will do here:
bond = Fold[Compose, builder, {Nominale[1.], StartDate[{2010, 10, 10}], Build[]}]
(* « Bond[]» *)
This is a complete bond, which we can query:
bond@getNominale[]
(* 1. *)
bond@getStartDate[]
(* {2010, 10, 10} *)
We can also construct a partial builder:
octBuilder = BondBuilder[{Null, Null}]@StartDate[{2010, 10, 10}];
so that
bond1 = (octBuilder@Nominale[1.])@Build[];
bond2 = (octBuilder@Nominale[2.])@Build[];
and now
bond1@getNominale[]
(* 1. *)
bond2@getNominale[]
(* 2. *)
bond1@getStartDate[]
(* {2010, 10, 10} *)
bond2@getStartDate[]
(* {2010, 10, 10} *)
EDIT
As a light-weight alternative (which uses similar implementation ideas though), here is a builder which won't depend on any framework:
ClearAll[BondBuilder,Bond];
BondBuilder[] :=
Module[{builder, props = {}},
builder@property_[value_] :=
(
builder /: property[builder] = value;
If[! MemberQ[props, property], AppendTo[props, property]];
builder
);
builder@Build[] := Bond[#[#[builder]] & /@ props];
builder
];
Here is how it can be used:
builder = BondBuilder[]
(* builder$16211 *)
builder@Nominale[1.]
(* builder$16211 *)
builder@StartDate[{2010, 10, 10}]
(* builder$16211 *)
builder@Build[]
(* Bond[{Nominale[1.], StartDate[{2010, 10, 10}]}] *)
you get an immutable expression representing your bond. You should also be able to use the partial builder in the way you outlined. The associativity problem for chained method application remains, however, and you will either have to use several statements, or use parentheses, or something like the Nest
- Compose
construct.