3
$\begingroup$

I like to use this simple OOP setup in Mathematica that I learned from reading posts here over the years and playing around with it. This makes a Class and implement basic OOP (nothing fancy like class extension and all that. Just a class to make objects, with methods on it)

It works well in notebook in Global context. But when I move it to inside a package, it no longer works. First will show a MWE of Person class that works in Global context

personClass[$name_, $age_] := Module[{name, age, self},
   SetAttributes[self, HoldAll];
   (*---public methods------*)
   self@getName[] := name;
   self@getAge[] := age;
   (*-----constructor code------*)
   age = $age;
   name = $name;
   self
   ];

To use the above, we first make an instance of Person class

p = personClass["jone doe", "jan 1, 1900"]

Now call the public methods

p@getName[]
(*jone doe*)

p@getAge[]
(*jan 1, 1900*)

Now I want the above to work inside a package. So I did the following

ClearAll["Global`*"]
BeginPackage["mypkg`"]
Unprotect @@ Names["mypkg`*"];
ClearAll @@ Names["mypkg`*"];
personClass::usage = "personClass. Makes a Person object instance"

Begin["`Private`"]
personClass[$name_, $age_] := Module[{name, age, self},
   SetAttributes[self, HoldAll];
   (*-----public methods-----------*)
   self@getName[] := name;
   self@getAge[] := age;
   (*---constructor code--------------------------*)
   age = $age;
   name = $name;
   self
   ];
End[];
Protect @@ Names["mypkg`*"];
EndPackage[];

And now

p = personClass["joe doe", "Jan 1, 1900"];
p@getName[]

Gives

mypkg`Private`self$9696[getName[]]

So it did not work.

I do not want to make all methods in the class to be exported at package level ofcourse, this defeats the whole idea of using class inside package.

It looks like this setup does not play nice inside a package. Is there a way to make it work? The whole idea I have is to make a package with number of classes in it. Right now the class work if they are all in Global context.

V 14 on windows 10

References

object_based_in_mathematica

How can you give a Module a context and have its local variables and Modules belong to that context?

https://groups.google.com/g/comp.soft-sys.math.mathematica/c/7ElYw1-ZdY0

update

This is updated implementation thanks to answer below, with new syntax for getter and added setter methods so it is all clear now

ClearAll["Global`*"]
BeginPackage["mypkg`"]
Unprotect @@ Names["mypkg`*"];
ClearAll @@ Names["mypkg`*"];
personClass::usage = "personClass. Makes a Person object instance"

Begin["`Private`"]
personClass[$name_, $age_] := Module[{name, age, self},
   SetAttributes[self, HoldAll];
   (*-----public methods--getters and setters ---------*)
   self[sym_[] /; SymbolName[sym] === "getName"] := name;
   self[sym_[] /; SymbolName[sym] === "getAge"] := age;
   
   self[sym_[theName_] /; SymbolName[sym] === "setName"] := name = theName;
   self[sym_[theAge_] /; SymbolName[sym] === "setAge"] := age = theAge;
   
   (*---constructor code--------------------------*)
   age = $age;
   name = $name;
   self
   ];
End[];
Protect @@ Names["mypkg`*"];
EndPackage[];

To use

enter image description here

OOP is cool.

$\endgroup$

1 Answer 1

5
$\begingroup$

The symbols getAge and getName aren't exported as package symbols so that is why the definitions don't fire. If you want to use the same syntax you are using then just change the lines

self@getName[] := name;
self@getAge[] := age;

with something like

self[sym_[] /; SymbolName[sym] === "getName"] := name;
self[sym_[] /; SymbolName[sym] === "getAge"] := age;

or, as suggested by Michael E2

self["getName"] := name;
self["getAge"] := age;
(* more methods here *)
self[sym_Symbol[args___]] := self[SymbolName[sym], args];

and it will work. I think JLink does something similar but I haven't really verified that.

Personally I find method definitions like

self["getName"] := name;
self["getAge"] := age;

to be much more natural, but the language is versatile enough to support both methods.

$\endgroup$
2
  • $\begingroup$ And if you like syntactic sugar, add self[symb_Symbol] := self[SymbolName[sym]] to the last pair of defs. $\endgroup$
    – Michael E2
    Commented May 17 at 19:32
  • $\begingroup$ That's a great idea Michael! $\endgroup$
    – Jason B.
    Commented May 17 at 20:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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