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
There is now a dedicated technical tutorial slide trail section demonstrating how the Webel MAll MTools class extension and the All` package make working with MTools easier: Webel IT Australia has made massive use of MTools effectively on some very complex, demanding projects, see also under the Relates to links below this post for many examples.

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:

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 classes and OO in Mathematica (if you don't count Abstract Data Types using TagSetDelayed)

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 (with fully built-in notations and IDE support) would need to satisfy.

Con: It is not clear how well MTools will be supported longer term, although the fundamental mechanisms of the Wolfram Language it is based on are unlikely to change. There is no explicit vendor support; hopefully the existence of MTools has not given Wolfram Research an excuse to not develop a fully-fledged OO system.

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

Con: Mathematica: MTools: Argument Pattern strong type matching does not intrinsically respect inheritance (makes implementing design-by-contract and some Design Patterns less convenient). But you can use PatternTests with Webel MAll extensions.

Con: No obvious (simple) 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 no 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, which is not "method friendly".

Note that the Webel libraries for Mathematica offer a complete documentation solution for methods of classes via the Webel HelpO` package.

Con: The (otherwise completely awesome) Wolfram Language Plugin for IntelliJ IDEA also does not recognise MTools methods.

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