Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

A while ago I learned a trick which allows one to imitate object-oriented programming in MMA using SubValues:

makeObj[identification_Integer] = Obj[identification];
makeObj[5]
(* Obj[5] *)

These can have "instance fields":

Obj[_]["field"] = 0 (* Default*);

And member functions:

Obj[id_Integer]["increase"] := (Obj[id]["field"]++;)
Obj[5]["increase"];
Obj[5]["field"]
(* 1 *)

However, in order to make an object, I have to identify it in makeObj, otherwise my "field" values will go to the default every time. Is there any implementation which allows me to create objects without explicitly naming the pointers to them?

In other words, can I make the compiler internally assign pointers? In Java, something like:

java.util.ArrayList<myObj> a = new java.util.ArrayList<MyObj>();
for(int i = 0; i < 10; i++)
    a.add(new myObj());
a.get(5).doStuff();

can be easily done, but how would I do it in Mathematica? (without JLink, of course)

If this is not possible, is there some way to have garbage-handling? Like:

Obj["delete", id_] := 
   DeleteCases[SubValues[Obj], HoldPattern[Obj[id][_]] :> _]

(Except this doesn't work)

share|improve this question
1  
I'm not familiar with Java. Could you give an example that is Mathematica / English only? –  Mr.Wizard Oct 20 '12 at 19:51
    
I can't give an example in MMA - that's the problem. What I would like to know is if I can add an Obj to some list, for instance, with a compiler-assigned id. –  VF1 Oct 20 '12 at 19:53
1  
And my problem is I can't understand you. :^) "compiler-assigned id" means little to me. Could you describe the behavior of the conceptual construct? Is Unique[] anything useful? –  Mr.Wizard Oct 20 '12 at 19:56
    
Yes-Unique[] is something that definitely works. The only issue is that when I did use Unique[] in my actual problem, so many new symbols were created and defined that I can't help but imagine there's a solution which has garbage-handling. –  VF1 Oct 20 '12 at 19:58
1  
This is probably a duplicate of mathematica.stackexchange.com/q/990/5 –  rm -rf Oct 20 '12 at 19:59

1 Answer 1

up vote 4 down vote accepted

I don't know a way with subvalues, but you can use Module to create objects without explicit identifier in makeObj:

makeObj[] := Module[{field = 0},
               Switch[#,
                 "increase", field++; #0,
                 "field", field]&]

(Note that I slightly changed your "increase" function to return #0, that is, "self", so I can chain calls for illustrative purposes)

Now you can create anonymous objects:

makeObj[]["increase"]["field"]
(*
==> 1
*)

To see that different calls to makeObj create independent objects, you can try the following:

foo = makeObj[];
bar = makeObj[];
foo["field"]
(*
==> 0
*)
bar["field"]
(*
==> 0
*)
foo["increase"]["increase"];
foo["field"]
(*
==> 2
*)
bar["field"]
(*
==> 0
*)

Since I've noticed that figuring out how to allow method call arguments is not trivial, here's a version which allows to set field with an optional second argument to the "field" method call (and without returning self from "increase"):

makeObj[] := Module[{field = 0},
               Switch[#,
                 "increase",
                   field++;,
                 "field",
                   Module[{args = Hold[##]},
                     If[Length@args > 1,
                       field = args[[2]],
                       field]]]&]

This e.g. allows

foo = makeObj[];
foo["field"]
(*
==> 0
*)
foo["field", 4]
(*
==> 4
*)
foo["field"]
(*
==> 4
*)

Also note that to have the variables garbgage-collected, you have to make sure that there are no references to it; especially a colon at the end will not prevent storing in Out. For example see the following session:

In[2]:= ?Global`*
args    field   makeObj

In[3]:= foo = makeObj[];

In[4]:= ?Global`*
args     args$    field    field$81 foo      makeObj

In[5]:= foo=.

In[6]:= ?Global`*
args     args$    field    field$81 foo      makeObj

In[7]:= Do[bar = makeObj[]]

In[8]:= ?Global`*
args     args$    bar      field    field$81 field$82 foo      makeObj

In[9]:= bar=.

In[10]:= ?Global`*
args     args$    bar      field    field$81 foo      makeObj

For In[2] you see the symbols generated by the definition of makeObj (which I omitted, because it's the same as above). Then I define foo using a semicolon at the end; while this prevents printing, it still assigns the generated object to Out[3] in addition to foo. Therefore the field (field$81) is not removed when foo is cleared in In[5]. On the other hand, the Do in In[7] causes Out[7] to be set to Null, so the object is only referenced by bar. Therefore the corresponding field (field$82) disappears when clearing bar in In[9].

share|improve this answer
    
Really interesting. One question: Why do you need makeObj[]? Shouldn't just makeObj := .... work? –  VF1 Oct 20 '12 at 22:16
    
@VF1: That's because every execution gives a different object, and there I prefer the function call syntax. Also, it allows you to more easily pass it around without executing it (e.g. if you have a function which takes an object constructor as argument). And finally, you might want to add arguments (e.g. an optional starting value for field), which isn't possible without the function call syntax. –  celtschk Oct 20 '12 at 22:57

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.