2
$\begingroup$

I'm settling in on a programming pattern for "constructors." I can illustrate the pattern with the following example, which defines a constructor newBar for "objects"-as-Associations of type bar:

ClearAll[bar, newBar];
Options[newBar] = {"a" -> 42, "b" -> 43};
newBar[OptionsPattern[]] := <|"a" -> OptionValue["a"], "b" -> OptionValue["b"]|>

?? newBar
Global`newBar
newBar[OptionsPattern[]]:=Association[a->OptionValue[a],b->OptionValue[b]]
Options[newBar]={a->42,b->43}
 newBar[]
<|"a" -> 42, "b" -> 43|>
 newBar["b" -> 117]
<|"a" -> 42, "b" -> 117|>

This does a nice job of supplying default values. The only downside is that there is LOTS of repitition in the boilerplate, and I'd like to abstract that out. Here's an almost-working attempt:

ClearAll[capitalize];
capitalize[string_String] :=
  With[{chars = Characters[string]},
   StringJoin[Prepend[Rest[chars], ToUpperCase[First[chars]]]]];

capitalize["foo"]
"Foo"
ClearAll[camelback];
camelback[strings_List] :=
  StringJoin@Prepend[capitalize /@ Rest[strings], First[strings]];

Symbol@camelback[{"new", "foo"}]
newFoo
ClearAll[constructor];
constructor[nym_Symbol, attributes_Association] :=
  With[{keys = Keys[attributes],
    values = Values[attributes],
    cNym = Symbol@camelback[{"new", ToString[nym]}]},
   ClearAll[cNym];
   Options[cNym] = Normal[attributes];
   cNym[OptionsPattern[]] :=
    Evaluate@Association[
      MapThread[Rule, {keys, OptionValue /@ keys}]]
   ];

constructor[foo, <|"a" -> 1, "b" -> 2|>]
?? newFoo
Global`newFoo
newFoo[OptionsPattern[]]:=<|a->OptionValue[a],b->OptionValue[b]|>
Options[newFoo]={a->1,b->2}

So far, so good. It looks very similar to the bar example. The most notable difference is the <|...|> notation instead of the call to Association, and that seems to be the clue, but I can't interpret the clue. In any event, it doesn't work, presumably because OptionValue doesn't get evaluated at the right time.

newFoo["a" -> 42]
<|"a" -> OptionValue["a"], "b" -> OptionValue["b"]|>

Any advice?

$\endgroup$
5
  • $\begingroup$ Don't have much time to test now, but it should work when you change OptionValue to (OptionValue[#]&), and has indeed to do with how / when OptionValue is evaluated. There has been a discussion on that, I will try to find a link. $\endgroup$ Oct 15, 2014 at 17:21
  • $\begingroup$ Ok, here it is. Not sure whether or not your question is a dupe of that, would need to study it closer to tell. $\endgroup$ Oct 15, 2014 at 17:22
  • $\begingroup$ Yup, changed Evaluate@Association[MapThread[Rule, {keys, OptionValue /@ keys}]] to Association[MapThread[Rule, {keys, OptionValue[#]&/@ keys}]] and it works :) Promote to an answer and I'll accept it. Thanks much! $\endgroup$
    – Reb.Cabin
    Oct 15, 2014 at 19:21
  • $\begingroup$ I actually think this is a dupe of that link, so I'd better vote to close. It's probably still worth to keep this question, perhaps the title could be changed a bit to reflect that the actual problem is in OptionValue. $\endgroup$ Oct 15, 2014 at 19:31
  • $\begingroup$ ok, concur. I changed the title & vote to close. $\endgroup$
    – Reb.Cabin
    Oct 15, 2014 at 23:21

0

Browse other questions tagged or ask your own question.