Re: Re: Set::setps error? Twitter.m's OOP-like approach?

• To: mathgroup at smc.vnet.net
• Subject: [mg104839] Re: [mg104813] Re: Set::setps error? Twitter.m's OOP-like approach?
• From: Leonid Shifrin <lshifr at gmail.com>
• Date: Wed, 11 Nov 2009 04:29:00 -0500 (EST)
• References: <200911030757.CAA01654@smc.vnet.net> <hcr7ps\$8th\$1@smc.vnet.net>

```Hi Erik,

If you really want to do OOP in Mathematica, you may also check out the
package classes.m by Roman Maeder (and of course  his books "The Mathematica
Programmer I,II" and "Computer Science with Mathematica"), and the book of
John Gray "Mastering Mathematica" for the detailed discussion of how one can
implement and use OOP in Mathematica.

If all you need is to create and use an ADT (no inhertiance, no
polymorphism),
you can do it relatively easily with UpValues. Here, for example, I define
a type "Pair", with operators <new> and <delete>, getters and setters.

Unprotect[pair, setFirst, getFirst, setSecond, getSecond, new, delete];
ClearAll[pair, setFirst, getFirst, setSecond, getSecond, new, delete];
Module[{first, second},
first[_] := {};
second[_] := {};
pair /: new[pair[]] := pair[Unique[]];
pair /: pair[tag_].delete[] := (first[tag] =.; second[tag] =.);
pair /: pair[tag_].setFirst[value_] := first[tag] = value;
pair /: pair[tag_].getFirst[] := first[tag];
pair /: pair[tag_].setSecond[value_] := second[tag] = value;
pair /: pair[tag_].getSecond[] := second[tag];
Format[pair[x_Symbol]] := "pair[" <> ToString[Hash[x]] <> "]";
];
Protect[pair, setFirst, getFirst, setSecond, getSecond, new, delete];

I beleive that implementing ADT in the above manner lets one avoid the
pass-by-reference problems you mentioned. Note that while definitions are
attached to <pair>, they redefine the action of Dot (.) on their patterns -
this is why we can use the usual OO notation.

Here is how we could use it :

pr = new[pair[]];
pr.setFirst[10];
pr.setSecond[20];
{pr.getFirst[], pr.getSecond[]}

{10, 20}

Creating a list of new pair objects :

pairs = Table[new[pair[]], {10}]

{"pair[430427975]", "pair[430428059]", "pair[430428060]", "pair[430428057]",
\
"pair[430428058]", "pair[430428063]", "pair[430428064]", "pair[430428061]",
\
"pair[430428062]", "pair[430428051]"}

Setting the fields :

Module[{i},
For[i = 1, i <= 10, i++,
pairs[[i]].setFirst[10*i];
pairs[[i]].setSecond[20*i];]]

Checking the fields :

#.getFirst[] & /@ pairs

{10, 20, 30, 40, 50, 60, 70, 80, 90, 100}

#.getSecond[] & /@ pairs

{20, 40, 60, 80, 100, 120, 140, 160, 180, 200}

Here is how you could  type-check the code that uses your data type: the
following function, for example, takes a sequence of <pair>-s, sums up
corresponding fields and produces a new pair with fields equal to these
sums. The pattern __pair constitutes a type check.

Clear[sumFields];
sumFields[args__pair] :=
With[{result = new[pair[]]},
With[{sums =
Total /@ Transpose[{#.getFirst[], #.getSecond[]} & /@ {args}]},
result.setFirst[sums[[1]]];
result.setSecond[sums[[2]]];
result]];

With this ADT construction, you will have to remember to explicilty use
<delete> on objects you no longer need, to avoid memory leaks, because the
internal variables <first>, <second> inside Module will not be automatically
garbage-collected by Mathematica (since they are referenced by global
symbols - <pair> in this case). Another thing: since Unique[] is used to
give each instance of our "object" an identity, it is only unique within a
single Mathematica session.

#.delete[] & /@pairs;

One good thing about UpValues is that the names <new>, <delete>,
<setFirst>,<getFirst>, etc are available should you wish to use them in
defining more than one ADT at the same time - the definitions are really
attached to the container (type) name - <pair> in this case.

Hope this helps.

Regards,
Leonid

On Tue, Nov 10, 2009 at 2:03 PM, Erik Max Francis <max at alcyone.com> wrote:

> Daniel Lichtblau wrote:
> > Erik Max Francis wrote:
> >> I was experimenting with vaguely OOP-like wrappers around data similar
> >> to how Twitter.m does things:
> >>
> >>
> >> I was experimenting with "mutable objects" and I'm running into a
> >> scoping problem of some type that I don't understand.  Here's some
> >> sample code creating a wrapper around an "account balance":
> >>
> >> In[1]:= {NAME, BALANCE} = Range[2]
> >>
> >> Out[1]= {1, 2}
> >>
> >> In[2]:= create[name_, balance_] := Account[name, balance]
> >>
> >> In[3]:= getName[account_Account] := account[[NAME]]
> >>
> >> In[4]:= getBalance[account_Account] := account[[BALANCE]]
> >>
> >> In[5]:= deposit[account_Account, amount_] :=
> >>   account[[BALANCE]] += amount
> >>
> >> The deposit function doesn't work, though:
> >>
> >> In[6]:= a = create["test", 100]
> >>
> >> Out[6]= Account["test", 100]
> >>
> >> In[7]:= getBalance[a]
> >>
> >> Out[7]= 100
> >>
> >> In[8]:= deposit[a, 25]
> >>
> >> During evaluation of In[8]:= Set::setps: Account[test,100] in the part
> >> assignment is not a symbol. >>
> >>
> >> Out[8]= 125
> >>
> >> In[9]:= getBalance[a]
> >>
> >> Out[9]= 100
> >>
> >> But here's what I'm confused; if I do what getBalance does manually, it
> >> works fine:
> >>
> >> In[10]:= a[[BALANCE]] += 25
> >>
> >> Out[10]= 125
> >>
> >> In[11]:= a
> >>
> >> Out[11]= Account["test", 125]
> >>
> >> The Set::setps error is about Blocks so I suppose I'm running into some
> >> scoping issue but I don't follow what it is.  What am I missing here?
> >> What's the right way to write getBalance?
> >
> > You are using the formal argument "account" in the manner of a
> > call-by-reference" parameter. One can emulate that in Mathematica by
> > making it a held argument.
> >
> > SetAttributes[deposit, HoldFirst]
> >
> > Now you have a new problem, which is that the head will not be
> > recognized as Account (it will be Symbol, because all that is seen
> > before the rule fires is "a", sans quotes). No big deal, we'll evaluate
> > it and check the head.
> >
> > deposit[account_, amount_] /;
> >    Head[Evaluate[account]] === Account :=
> >      account[[BALANCE]] += amount
> >
> > That should work for the purpose you have in mind.
>
> Okay.  I see that since Mathematica is far more functional a language
> than other languages that I've used and so what I was trying to do there
> was quite alien -- sufficiently so that I really shouldn't try.  (In
> this particular case, I was just taking a rather typical OOP example and
> playing with it in Python, it wasn't central to my question below ...)
>
> >> Do Mathematica users find Twitter.m's approach to wrapping around
> >> objects palatable in the first place?  (My eventual application is for a
> >> wrapper around tensor objects where multiple things have to be carted
> >> around and the normal display of such objects would be far too ungainly
> >> to be acceptable anyway.)
> >
> > I'll reread that blog, but I suspect it's one of those things that is
> > incomprehensible to those past a certain age.
>
> Well, when I boil down when I'm talking about here it's not so much OOP
> per se but rather opaque types, giving the ability to pack lots of stuff
> in an object, hide it with a nice face by using Format[..., Standard] :=
> ...., and then define a bunch of functions that take it at the first
> argument and manipulate it.
>
> The context I mentioned above is a tensor library where you don't really
> want to see the components of the tensor unless you specifically ask for
> it -- a 4-dimensional Riemann tensor, after all, has 4^4 components --
> but instead you just want to specify a set of coordinates, a metric
> using those coordinates, and then go to town creating and manipulating
> those tensors, then crank out specific calculations based on them.
>
> Does that make more sense as being something reasonably Mathematica-ish,
> using that approach for type opaqueness?
>
> --
> Erik Max Francis && max at alcyone.com && http://www.alcyone.com/max/
>  San Jose, CA, USA && 37 18 N 121 57 W && AIM/Y!M/Skype erikmaxfrancis
>    Can I walk with you / 'Till the day that my heart stops beating
>    -- India Arie
>
>

```

• Prev by Date: Another NMinimize w/ NDSolve question...
• Next by Date: Re: time dependent surface data