43

Which Object-Oriented Paradigm (OOP) approach to use in Mathematica for:

  • general implementation, or

  • a particular project?

There are a lot of related questions and answers in MSE on doing OOP that concentrate mostly on the "How?" but not on discussing "Why/which OOP approach?" (Or "What are the pre-conditions and consequences of using ...?")

Here is a mind-map that shows a way to compare the proposed OOP implementations and styles. (The image links to a PDF that has clickable hyperlinks.)

OOP in Mathematica approaches

It is probably best the responses of this question to be summarized in a comparison table with columns and rows derived/related to that mind-map.

Links

Here is a link to a PDF of the mind-map with clickable/linked references also given below.

Core concepts of the implementations

Objects

Pure objects

  • ClasslessObjects, Jakub Kuczmarski, (2014), GitHub, MSE

  • JavaScript style

Struct object

  • MTools, Faysal Aberkane (2016), GitHub, MSE

Associations

Rules

  • “The Mathematica Programmer: Object-Oriented Programming”, Maeder (1990), WLA

Classes (types)

  • Type declarations, Leonid Shifrin (2012), MSE

Object-oriented design patterns

  • “Implementation of OOP Design Patterns in Mathematica”, Antonov (2016), GitHub, WordPress

  • Uses the same approach to classes as Leonid Shifrin’s

Signature overloading

Modeling interactions between classes / objects

UML diagrams

  • … included since it emphasizes the necessity of big-picture view in OOP designs
  • “UML diagrams creation and generation”, Antonov, (2016), GitHub, WordPress

Comparison table (example)

\begin{array} {|r|r|} \hline \textbf{Approach} & \textbf{Core concepts} & \textbf{Properties} & \textbf{When to use?} & \textbf{Why use it?} \\ \hline ClasslessObjects \\ \hline MTools \\ \hline Class \; types \; definitions \\ \hline ... \\ \hline \end{array}

user64494
  • 26,149
  • 4
  • 27
  • 56
Anton Antonov
  • 37,787
  • 3
  • 100
  • 178
  • 3
    SciDraw is a very nice package for making publication quality figures. It is based on an (unpublished) OOP framework. While the framework is unpublished, the code is well commented. This project is fairly unique because it does use OOP extensively while being one of the largest and most complex freely available Mathematica packages. There are many discussions of OOP in M, but not many truly large scale applications. – Szabolcs Jul 04 '16 at 16:05
  • 2
    One problem with SciDraw is that it is quite slow. I don't know if that has anything to do with the OOP framework. It could be, but I suspect option handling to be the culprit instead. Anyway, performance should be a prime concern for any OOP framework which aims to be usable in a large project. – Szabolcs Jul 04 '16 at 16:07
  • @Szabolcs Can you put your comments in an answer and gather some links? I will update the mind map, etc. – Anton Antonov Jul 04 '16 at 16:34
  • 4
    I should say that I have another version of OOP in the works, which I have been using for my purposes for some time, and which builds on my earlier developments. This new version will arguably be much more "grown-up" version, suited for production. I should be able to publish it relatively soon. – Leonid Shifrin Jul 04 '16 at 16:42
  • @LeonidShifrin Great, looking forward to see / experiment with it! – Anton Antonov Jul 04 '16 at 17:03
  • @Szabolcs I have seen couple of OOP packages implemented in Mathematica, that are/were quite slow, and because of this not that useful. This is one of the reasons I refer real life applications and success stories in the mind map. – Anton Antonov Jul 04 '16 at 21:46
  • It's a biased view, but MTools is already a success story for me, as I've developed an application of 20k lines (and growing) with it. MTools represents the language development part of this project. – faysou Jul 05 '16 at 07:52
  • @faysou I understand that the developer(s) behind a certain OOP approach would be biased. That is why I started this discussion. Please consider providing an answer describing why you selected the approach you developed and used, and what the observable consequences. Some other questions to consider follow. Do you think the code would be shorter using standard FP? Is it easy to get other people to do development in the project? Is there any impact on (i) computational efficiency, and (ii) readability? – Anton Antonov Jul 05 '16 at 10:08
  • The reason why I didn't want to answer is that I don't know much about the internal architecture of SciDraw. I can't comment on how it's OOP framework (called MathObject) works or differs from other approachers. Users of SciDraw are not exposed to this, the only hint is some error messages about failed construction, etc. All I really have is the link to the package, and the knowledge that it heavily uses OOP. – Szabolcs Jul 05 '16 at 10:13
  • 5
    To the people who voted or consider voting to close the question as too broad. The question in the title is broad, but examine the mind-map. The comparison proposed there narrows down the question fairly well. The question can be answered with specific prescriptions of when and why for at least several of the approaches listed in it. – Anton Antonov Jul 05 '16 at 10:14
  • Why no one have answered it – Eric Nov 16 '16 at 17:30
  • @Eric I got busy with several projects like presentations for WTC-2016 and MathematicaVsR at GitHub. For the latter project I do want to compare OOP approaches in Mathematica with those in R. So, I will have enough material to post an answer. – Anton Antonov Nov 17 '16 at 02:22
  • Glad to see it! By the way, with the Association, namespace mechanism and the highy flexible rule-replacing system(contain functional programming as its small feature) in Mathematica, I roughly guess that there would be no need to think with or implement OOP anymore. OOP, for me, is an industrial thing that improves the procedure programming by higher abstraction. But Mathematica is a masterpiece of programming paradigm, much more higher than other else, that advance our mind for great level. So I'm always of great interest that is implementing OOP in MMA still necessary? – Eric Nov 17 '16 at 05:11
  • 2
    @Eric I think there is a still good reason to have proper OOP to work with, as otherwise one will be simply adapting oop-like features with Association all the time. Fortunately as you mention OOP implementations are now pretty easy with the advent Association. – b3m2a1 Jul 21 '17 at 06:21
  • 1
    @b3m2a1 I strongly consider posting an answer/comment on the subject OOP objects vs associations... – Anton Antonov Jul 22 '17 at 21:47
  • @Eric, AntonAntonov I just posted my thoughts on OOP vs/with Association – b3m2a1 Aug 06 '17 at 05:25
  • There isn't much here about why not to do this. To answer the question in the title, I would say none of them, and shun OOP in general. By all means use Association to resemble structs, but don't do inheritance based polymorphism or any of that - it's just cumbersome and leads to excessive "abstraction" and poor performance. It also scatters state everywhere instead of centralizing it like in a data oriented approach or eliminating it through immutability like in a functional approach. OOP encourages a 'domain modelling' mindset, instead of thinking about what the computer actually has to do – flinty Nov 23 '23 at 11:29

2 Answers2

12

Faithful OOP vs. Association

So per @AntonAntonov's suggestion I'm going to chime in on OOP vs. Association. In a comment, @Eric notes that:

"By the way, with the Association, namespace mechanism and the highy flexible rule-replacing system(contain functional programming as its small feature) in Mathematica, I roughly guess that there would be no need to think with or implement OOP anymore."

While this is partially true, I think there are a few things to consider (and note that I'm not really a programmer, so I might be spouting garbage here):

  • OOP is a paradigm, not a framework, so OOP can still be useful even if an OOP-framework is less useful that it would once have been
  • OOP and Association are fundamentally complementary

I think that last point bears some discussion, and to ground it, I'll disclose that I am biased, as most of my user-accessible code in Mathematica is object-oriented in some way. It might be code-as-data level psuedo-OOP, it might be true mutable OOP, but the paradigm is simply too useful to abandon altogether.

To start, let's consider struct-level OOP. Association makes this super easy. Just do something like:

<|
 "Username"->"b3m2a1",
 "Password"->"uh-oh-security-leak"
 |>

and you have a struct. I use this type of deal a bunch, for example here to represent notebook tabs in the TaggingRules.

To make this more useful, maybe we'll take our OOP one step further and attach some head to it, to make it a code-as-data level object:

CoolAppUser[
 <|
  "Username"->"b3m2a1",
  "Password"->"uh-oh-security-leak"
  |>
 ]

Then we can stick Format code on CoolAppUser and UpValues and junk like that. This is a powerful tool, and one of the most common ones I use.

This is most useful when you don't need to mutate your data. Generally you can get away without mutability, but sometimes it's just too useful to do without. And here's where you generally need some form of OOP framework. And here is also where we should take a detour to look at the (other) favorite language of jokes and hacks like me, python.

OOP association-wise in Python

Python is a fully-OOP language. With perhaps the exception of the most primitive of primitives, there is nothing but OOP in python. And what do python's objects look like in practice? Glorified hash-maps, which are implemented in Mathematica as associations. And if you pretty much just copy python's structure but copy it over to Mathematica, it's easy and powerful. And if you take some time to optimize, that's even better. To give a sense of just how easy, over about a year-and-a-half I've implemented 4 or so such frameworks.

Concrete implementations

The basic idea I've used is either that you make a wrapper head that holds a Symbol that can be mutated or, as I've been doing recently, making a single centralized holding symbol that tracks object and type data in a core Association and making a head that holds a pointer to an entry in that. I've found the latter to be somewhat more efficient and manageable. Note that we can do this in either a typed or untyped manner.

We need some mutability, but since we're working with a Symbol in either case we can get that out of a combo of AssociateTo and Set / SetDelayed. We can even vectorize the AssociateTo for efficiency.

And this truly is useful. It's got overhead for sure, it's relatively slow, yes, but as with everything Mathematica, if you know how to use it right, it can take you far. I use one such framework in a large chemistry package of mine, I used another version in a now-abandoned set of classes for a specific type of QM computation with random additions for databases and parallel objects and junk, and I developed one just as an exercise which I'm gonna get around to putting through its paces at some point.

Remarks

  • Just to comment on Anton Antonov's first question, I think if some OOP framework were integrated into the language it would be incredible, but I also totally understand why WRI doesn't put it in.

  • Additional comparison between Association and OOP over handling of "global" variables can be seen in this MSE answer of "Functions with changeable global variables".

Related

I made another few implementations here and here for the question How can I implement object oriented programming in Mathematica?

b3m2a1
  • 46,870
  • 3
  • 92
  • 239
  • 1
    This is good -- thanks for answering. Some experienced Python users do say that the Object-Orientation (OO) is kind of forced in Python or bolted-in. That aligns with your explanations that OO in Python is done by glorified hash-maps. – Anton Antonov Aug 07 '17 at 13:57
  • @AntonAntonov Thanks, the extra structure is welcome. While I would agree the Python OO isn't as strongly bound as it is in, say, Java, I do think it's an effective approach for dynamic languages such as Mathematica. – b3m2a1 Aug 07 '17 at 19:09
2

I want to talk about why I specifically made my short realization and what format I chose.

Lately I have been developing small paclets that can be used for general purpose. For example a client for servers that support WebSocket protocol, a library for working with TelegramBot API or TCPServer that can be flexibly modified on the fly.

When I was working on these projects I realized that I just needed modifiable objects and I ended up creating a small package called Objects. It implements a general approach for implementing OOP It was more cumbersome at first, but gradually I removed a lot of the checks and conditions that were slowing it down.

Eventually the implementation of changeable objects just became an add-on to symbols and associations. I created mutable associations that are most like JS, but with overloading capabilities.

A typical object from my package looks like this:

obj1 = Object[]
(*Object[KirillBelov`Objects`Object`$11]*)

obj1@PropName = "property" obj1@PropName ("property") Object[KirillBelovObjectsObject`$11]@PropName ("property") Object[KirillBelovObjectsObject`$11]@PropName = "new" obj1@PropName ("new")

What does this ultimately give me compared to an association? The fact that I can use type checking in a much simpler way and forget about the fact that I always have to check the form of the expression - is it a symbol or an expression that was derived from OwnValues? Now I don't think about setting Hold attributes and just work with the object and it doesn't matter if it's another reference or the object itself. In the end it just made my work in specific projects fantastically easier.

The next cool thing I apply to these objects and am happy with is inheritance. It has also made writing code in my projects a lot easier. Even the fact that I no longer need to define getters and setters for a header in every package is a plus. And the fact that I can inherit all the definitions is fantastic. For example, I've created one general kind of server for myself, and if I need to make several servers for specific purposes, I just inherit from the general one and add a small number of specific functions to it. All in all I see only one plus. I believe that choice is always better than no choice. Let WL be able to use both OOP and functional programming. Let there be immutable objects and changeable objects too. I have found a great use for both.

In the future I plan to add some new features to my project, such as multiple inheritance for example ;-) Of course if it is there and it is dangerous - you don't have to use it, but again it just gives you a choice

Kirill Belov
  • 618
  • 6
  • 17