Mathematica: TIPS for living with the user-contributed MTools for Object-Orientation (until a vendor-supported OO solution is eventually provided)

Webel IT Australia promotes the amazing Mathematica tool and the powerful Wolfram Language and offers professional Mathematica services for computational computing and data analysis. Our Mathematica tips, issue tracking, and wishlist is offered here most constructively to help improve the tool and language and support the Mathematica user community.
DISCLAIMER: Wolfram Research does not officially endorse analysis by Webel IT Australia.
Icon class
icon_class
far fa-sticky-note
icon_class_computed
far fa-sticky-note
Note kind
Policy level
Keywords

MTools is a Wolfram user community contribution kindly offered by Faysal Aberkane on GitHub:

Dr Darren of Webel IT Australia says 'many thanks Faysal'!

The good

MTools offers a degree (only) of object-orientation for Mathematica. It is not a fully-fledged OO system, nor does it claim to be. It is not clear how well it is supported anymore (if at all).

Pro: It's definitely better than nothing. Because OO is very useful together with Mathematica!

Pro: Some basic support for inheritance (promotes reuse, helps organisation of code and logic, and reduces typing).

Pro: You can prompt on the "method" functions available to an object within a Mathematica notebook and get some (not a lot) of info about each "method" function.

Pro: MTools fields can carry MTools objects, and you can chain calls: obj1.obj2.f2

Pro: Given that Objectica seems to be dead, MTools seems to be the only practical contender for use on a substantial modelling project requiring OO in Mathematica.

The not so good

Please note that identification of these short-comings it NOT a complaint about MTools! Rather, it serves to help identify requirements that a robust vendor-supported OO solution would need to satisfy.

Con: There is absolutely no vendor support, and it seems that the existence of MTools may have given Wolfram Research an excuse to not bother to develop a fully-fledged OO system.

Con: No obvious "built in" way to separate interface and implementation (although it might be possible to HACK with some coding gymnastics).

Con: Mathematica: MTools: Argument pattern matching does not respect inheritance (undermines design-by-contract and heaps of Design Patterns)

Con: No obvious way to achieve visibility control of attributes and function/method visibility (although custom naming conventions can be used to indicate intended visibility). So while you can mimic "by hand" using protected hooks and some of the Design Patterns that rely on visibility control, there is not built-in support or IDE guidance. It might be possible to HACK it in, but it's not elegant.

Con: The Wolfram Workbench editor only has a limited ability to see the method functions (and their arguments), which might be due to limitations in the not-so-spectacular Mathematica function documentation approach.

Con: The Mathematica Notebook editor does not see the field accessors, which is particularly annoying if you wish to enjoy chaining MTools objects - yet another reason Mathematica needs fully-fledged vendor-supported OO.

Con: While the MTools dot notation for navigating to fields and methods is nice, it is fragile, and in many cases either parentheses or other notation options must be used (see tips below).

Con: It does not always play nicely with the Mathematica Entity system (which Entity system has many limitations that render it unsuitable as an OO replacement). For example, if you have an MTools field that carries an Entity (so you can wrap an Entity) there are some cases where the MTools notation does not resolve well against the Entity.


Following are some tips for getting the most out of MTools provided in no particular order. This is NOT a tutorial about MTools (please visit the external links).

Use $ContextAliases within Packages to access the main MTools symbols

MTools offers functions NewClass and New and uses o to refer to the object being handled by a "method" defined on an MTools class. Outside a package a minimal usage would be:

NC = NewClass["Fields" -> {"a"}];
NC .f1[x_] := x + o["a"];
nc = New[NC][{"a" -> 1}];

nc.f1[2]
3

If you try this within a Package it will in fact create new symbols for NewClass, New, and o within the Package context. The clumsy workaround is to reference them fully qualified:

NC = MTools`Core`MPlusPlus`NewClass["Fields" -> {"a"}];
NC .f1[x_] := x + MTools`Core`MPlusPlus`o["a"];
nc = MTools`Core`MPlusPlus`New[NC][{"a" -> 1}];

nc.f1[2]
3

Not a lot of fun. It can be made less horrible using $ContextAliases within the Package:

BeginPackage["LessAwful`", { "MTools`"}]

newNC::usage = "newNC[a]";

Begin["`Private`"] 

$ContextAliases["M`"] = "MTools`Core`MPlusPlus`"; (* Super short *)

NC = M`NewClass["Fields" -> {"a"}];
NC .f1[x_] := x + M`o["a"];
newNC[a_] :=  M`New[NC][{"a" -> a}];

...

Use as:

<< LessAwful`

nc = newNC[1]

nc.f1[2]

3


Beware clashes between field names and inherited "method" names

MTools does not distinguish between "field" names and method names. So if you have a base class with a method "foo[]" and a field "foo" they will clash.

Limitations when wrapping an Entity with an MTools class

Because the Mathematica Entity is a bit limited - including EntityFunction currently being limited to scalars (as of MMA13) - it can be useful to wrap an Entity in an MTools class and value add with nicely organised methods (which can then leverage Entity queries behind the scenes), but there are a few "gotchas".

Con: If the MTools field for carrying the Entity is "e", you can't use the dot notation accessors. Say you have an object obj of class MyClass:


obj.e

sub::noFct: Class MyClass doesn't have a sub class with member function Entity.
Workaround: Just use the bracketed form instead:

obj["e"]


GOTCHA: Sometimes you need to use parentheses brackets () to quarantine the dot notation

Especially when using Part access [[]]] it's sometimes necessary to use parentheses:
MyClass = NewClass[{"Fields"->"array"}]
MyClass:mod[n_] := Module[
{item},
item := (M`o.array)[[n]];
...
Parentheses are sometimes also needed when mapping:
f[#] & /@ obj.field (* Sometimes fails *)
f[#] & /@ (obj.field)

GOTCHA: Avoid using local Module variable names the same as MTools field names

This can cause trouble that is hard to debug:
MyClass = NewClass[{"Fields"->"sameName"}]
MyClass:mod[] := Module[
{sameName},
sameName := M`o.sameName;
...
One approach is to just avoid the name clash:
MyClass = NewClass[{"Fields"->"sameName"}]
MyClass:mod[] := Module[
{sn},
sn := M`o.sameName;
...
(BTW: The example above is not advocating the use of such convenience variables, it's just an to illustrate the issue.)

In some cases you can use the explicit getter form with the String name of the field rather than the dot form. The following works:

MyClass = M`NewClass[{"Fields"->"sameName"}]

f[ob1_MClass1 := Module[ {obj2},
...
obj2.sameName  = obj.get["sameName"];
...
];

Relates to
Related notes
Related notes (backlinks)
Related snippets (extracts)
Visit also
Visit also (backlinks)
External links