I wrote a guide about Object Orientation and Polymorphism in Julia. opinions wanted!

I think that the Wikipedia page is just being overly inclusive here. CL has CLOS, but it is not OO by the modern widespread usage of the term, despite the title of the Keene book and the O in CLOS.

This is one of those weird instances of the typical Lisp pattern of having some insanely powerful language feature from the dawn of time, that is mostly unknown outside the language community, where the very same name is used for something related but fundamentally different. Consider, for example, macros, which in the C/C++ world mean simple replacement patterns.

CLOS was introduced in 1996. Its predecessors, CommonLoops and New Flavors are from the late '80s, when the OO craze was in full swing. This is not one of the many cases of a preexisting feature in Lisp having a namespace clash with conventions in other languages. They were aiming to replicate functionality from languages like Smalltalk and its disciples, but not without putting their own Lisp-y spin on it.

You can argue that their interpretation of OOP is incompatible with or less valid than others, but they were trying to facilitate a similar set of design patterns and consciously used the same terminology.

However, I’ve rewritten the introduction because (for the purposes of the tutorial), this taxonomical question is irrelevant and not the kind of thing I want people opening issues about–though I do think it is overall an interesting discussion in other contexts, like this one.

3 Likes

In a programming language you need to support the two forms of polymorphism: parametric and ad-hoc. A simple case is a list structure. You need to express type of elements in the list (parametric polymorphism) AND you need to express the idea that there are different structures that act like list, such as LinkedList, ArrayList… (ad-hoc polymorphism).

OO chooses encapsulation as solution to ad-hoc polymorphism. Julia on the other hand uses dynamic dispatch to implement the ad-hoc polymorphism. Haskell has done a nice job of showing how the ad-hoc polymorphism should be implemented and how it can be used with parametric polymorphism. Julia has made this simpler and more approachable. Julia has shown how along with macros, and type stability you can write code that is modular and efficient.

OO has be sold to the masses as a snake oil to cure the ills of software development. It simply hasn’t worked. Coding as in software is no different that what any other science. There is no other science that has used the notion of encapsulation somehow codify its domain.

It is interesting that even when you want to draw objects the number one advice is to draw relationships and not to name the objects. So if you are drawing a face, the last thing you want to think about is that there are two eyes, one nose…

It is time to stop pretending that OO has ever worked.

Daryoush

Often, “scientific” programming includes performance-critical mathematical programming, but that’s not the full extent of it. When building any complex system – even one that supports performance-critical math – one often needs to build infrastructure that is not performance-critical. In this case, programmer time is more important than run-time performance. In some of these cases, an OOP approach can be beneficial.

In the process of co-developing the Mimi.jl modeling platform, I’ve tried different “julian” implementations, but felt that they put too much burden on the programmer (e.g., forwarding methods to support encapsulation gets quite tedious across several layers). In the end, I developed Classes.jl to provide structural inheritance in a julian way, based on a constructed abstract type hierarchy. This has proved very useful and adequately convenient.

There are also cases where generic functions perform badly compared to internally-stored pointers. Again, I ran into this with Mimi.jl, in which we create model components with their own “run” method generated via macro. It turns out that iterating over a list of these to call each function performs very poorly owing to dynamic dispatch. The solution was to store a pointer to the function in the component, as in a typical OOP system. This performs far better, but rolling ones own OOP framework is a drag.

In my view, something like my @class macro that basically imports the superclass’ fields to avoid redundancy, and maintains a type hierarchy to support polymorphism, and an easier way to handle calling methods on a heterogenous sequence could easily be part of the language.

5 Likes

They got 3rd-party packages for that.

I assume you’re referring to the heterogenous sequences. Yes, there is FunctionWrappers, which is described as a “proof of concept” and hasn’t been touched in 10 months.

This reinforces my point: partially-complete, under-documented, sometimes abandoned 3rd party packages are quite different than including something in the language.

I have more than 50 years of experience programming. I have been using OOPS for 40 years. I have 2 decades of experience with C++. I have been using Go for about 9 years. I have been using Julia for about 2 hours. Some say that Go does not support OOPS or multiple inheritance, but I do it all the time.

Being a brand new Julia user means that I may be the very audience you were trying to help. I just want to say; Well Done! You helped me.

[ ] I would suggest in your introduction that you briefly state the Version of Julia being used. If you ever are late in doing an update, this can be a good warning to new readers that information may be obsolete.

I am thinking of some basic CS concepts that you should also demonstrate in a simple way to assist new users, perhaps as I learn more, I can be of some assistance. Different people think in different ways, one manual that uses only the best approach will alienated a significant population. So ignore the critics, implement suggestions, keep adding to your manual, what you have is a great start.

If some naysayer thinks I cannot have 40 years of OOPS experience, they should know that Object oriented design started right from the moment computers were invented. When I was a teenager, I would bring my girlfriend to Coffee Call for beignets, Professor Chen of LSU would be there with his CS students discussing OOPS, he would draw primitive ERDs on napkins. I already had 5 years of experience writing machine code, he was very patient with me. Later at MIT he wrote a book on the subject which influenced C, Pascal, UniComal, and others. OOPS is how you think, the language can make implementation easy, SmallTalk purist and users of C++ templates have discovered that strict OOPS constructs that do not represent useful abstractions create all sorts of problems causing some to hate OOPS.

I really like how you just avoid the entire argument simply show how to easily implement solutions.

11 Likes

I’m really happy to hear that! I’ve been programming for maybe nine or ten years or something, when the OOP craze was long over and the FP craze was in full swing, but I completely agree that OOP is a way of thinking (a way of thinking that is orthogonal to and compatible with FP). OOP is a good tool and a bad religion, like most tools.

It was my goal to show people how to use familiar patterns for software design in Julia. I originally did have a little ideological stuff in the introduction about the nature of OOP, but @Tamas_Papp helpfully pointed out that this was unnecessary, which much of the ensuing discussions on this thread confirmed!

I will add a version number, and I look forward to any suggestions.

3 Likes

That’s not the specific package I was talking about, but your point is taken and is also applicable to the packages I thought about. On the other hand, this stuff is relatively trivial to implement with macros. If you need it, nothing is stopping you from rolling your own.

I’m not sure how to apply your criticisms to my tutorial, as they are not very specific. Which parts do you think could benefit from change?

I have around 35 years programming, starting with a C-64 in my living room (I’ve never worked as a programmer, though). I have many years of experience as a hardware (ASIC and FPGA) designer, and I learned OOP (meaning C++ and Python) after learning VHDL and Verilog.

As a hardware designer, OOP made perfect sense: every logic gate is an instance of a class; each instance is slightly different than the others and has some internal state, but it comes from an ideal “template”.

More recently, I developed a small network simulator for my students (GitHub - mbaz/TinyNS: A small and simple network simulator). I did it in Python, which is what they know (or should know, anyway :slight_smile: ). The OOP philosophy also works well here, since you have classes for hosts, switches and packets, and the network is a set of instances of those.

I’ve also done lots of “scientific” or “numerical” programming, and in this area OOP has never made sense to me. If a matrix is an object, it might seem reasonable to attach functions like trace to it. But things like matrix multiplication don’t make as much sense. And this is just scratching the surface.

After using Matlab and Octave (I never could get into SciPy) for many years, I learned of Julia and it was like a gift from above. Everything just made sense. I don’t use anything else anymore (except for some work that has to be done in Matlab, and then I have to correct all my X[i]'s and my size(X)[1]'s).

I’m (slowly) working on re-writing my network simulator in Julia and I haven’t had any trouble translating the OOP approach to multiple dispatch. In fact, I’ve found that I can write the code much more more concisely, and easily add more powerful functionality, in Julia than in Python – but that may be because of my personal bias towards Julia.

6 Likes

I have always loved Python for certain solutions but Julia has what it takes to replace Python. I actually prefer R-R for data mining but I am thinking Julia can beat out R as well. I was doing matrix stuff on the Intel 8008, decomposition of array and determinates, it was painfully slow. FORTH was a major improvement. The 6502 was great platform learning. When I was using matrix transforms to rotate graphics, they were not objects, they were attributes. Trying to make everything an object can get a bit silly.

4 Likes

I read through the rest of this properly, it’s a nice piece.

A further comment on data hiding. You write

Julia does not enforce data hiding, so anyone can access your fields directly. The convention is like Python: If a field’s name starts with an underscore, you should consider it an implementation detail, and it is subject to change and produce strange effects if you mess with it.

On average I’ve observed that the julia community doesn’t actually follow this convention, though some packages do. Base takes the lead here; consider fieldnames(Dict) for example. I think it’s perfectly fine to recommend underscores but it would be clearer if you mention that the ecosystem is inconsistent.

I did find the intro paragraphs of “Polymorphism and Code Reuse” a bit confusing. I wasn’t quite clear on the points you were trying to convey there.

Some more piecemeal comments:

const Opt = Union{T,Nothing} where T

can be written

Opt{T} = Union{T,Nothing}

You write:

So why are types used to implement traits rather than other kinds of values? That’s an easy one: Types are known at compile-time. Values are only known at runtime. This means that traits are computed only when the dispatch compiles and have no cost thereafter.

It’s a bit more subtle than that. Both types and values may or may not be known at compile time, and both can be used during type inference as of ~julia 0.7. However, the ability to use values for further type inference depends on constant propagation and inlining heuristics (I think. I’m a bit hazy on the details). On the other hand, types have been practically usable as traits for much longer. More importantly, the heuristics about how types propagate during type inference are quite different and make it possible to pass trait types between functions without depending on inlining decisions. At least, I think it’s something like this; again I’m not expert on this part of the code.

1 Like

Thanks for the thorough reading and suggestions. I’ll do something about the issues you bring up.

Broadening the concept of OOP to be very inclusive can make practical discussions difficult, since it is hard to argue about concrete approaches.

I think that the bottom line is that the approach that can be considered idiomatic in C++ and Java does not mesh well with Julia (or multiple dispatch in general). What to call OO or not is a red herring here.

The problem I have with that post is that that isn’t really what OOP in Julia is about. The piece doesn’t even mention multiple dispatch - which is extremely important as a language feature in Julia. It basically writes single dispatch code in Julia - but one of the powerful things about Julia is that it breaks that mold and frees the programmer to write more expressively and simply about what is in their head instead of using bizarre single dispatch OOP tricks to do this.

In addition inheritance from abstract types tags as Julia does it is a strength not a weakness. It is a simple elegant design.

3 Likes

Thanks for reading dataSurfer. I’ll be happy to consider any actionable suggestions you have, but I’m not sure what you’re wanting.

edit: realized after I posted this that you said “that isn’t what OOP is in Julia is about”, not “OOP isn’t what Julia is about”. I leave my response for posterity and because so many people commenting on this thread seem to be allergic to OO in general.

What is Julia about, in your view?

In the initial Julia announcement, we have this statement of purpose:

We want a language that’s open source, with a liberal license. We want the speed of C with the dynamism of Ruby. We want a language that’s homoiconic, with true macros like Lisp, but with obvious, familiar mathematical notation like Matlab. We want something as usable for general programming as Python, as easy for statistics as R, as natural for string processing as Perl, as powerful for linear algebra as Matlab, as good at gluing programs together as the shell. Something that is dirt simple to learn, yet keeps the most serious hackers happy. We want it interactive and we want it compiled.

Julia is a language that’s trying to be useful for all kinds of problems, and composition and encapsulation are useful for some kinds of problems.

From the julialang.org landing page:

Julia uses multiple dispatch as a paradigm, making it easy to express many object-oriented and functional programming patterns.

While there appears to be a fair amount antipathy towards OO patterns in the Julia community (or at least towards the term “object oriented”), it does not appear to be an affliction from which the creators of the language suffer. Julia may not be “about OO” more than it’s about anything else, but it makes a conscious effort to facilitate “OO patterns”, which is what my guild is supposed to be about.

It’s a guide about using OO patterns in Julia and therefore focuses on interfaces for data structures. If you have some suggestions about how to integrate more multiple dispatch into the guide, I’d be happy to hear them. I also love multiple dispatch and use it frequently for resolving different kinds of arguments down to a base case or similar, but that seems more closely related to functional programming patterns. It’s very possible that there are other OO multiple dispatch patterns I haven’t understood very well, and I’d love to learn about them.

Which “bizarre tricks” did you have in mind?

It’s relative, I guess. It’s a lot more elegant than classical inheritance. On the other hand, behavioral sub-typing as in Go or Haskell is a lot simpler and more elegant that Julia’s implementation of abstract types, in my opinion. Hierarchies are a very confining way to structure a classification system. Luckily, we also have the trait pattern.

2 Likes

It is telling that your quote from the Julia website about multiple dispatch (MD) is under “General” and right on the JuliaLang front page. My assertion is that MD is a fundamental part of the language and you can’t write about OOP and polymorphism in Julia without talking about MD - you might as well be talking about OOP in any 3rd gen language for example C++/Java and so on. Julia has more than that to offer and MD is one of those crucial items.

If you are interesting in MD backgound and examples this multimethods paper in C++ is instructive (http://www.stroustrup.com/multimethods.pdf). Also check this out: Open Methods: From C++ to D | The D Blog which is an article about open-methods implementation in D.

MD is a natural way of writing methods particularly in data science/scientific computing applications. Its importance can be easy to miss because programmers are unused to seeing it in action or using it - because many languages tend to be semantically speaking, virtual carbon copies of each other and so people are not really exposed to MD.

4 Likes

Thanks! I’ll look at the papers and see what I can add to the guide.

That could be. I don’t use Julia for numeric computing, but as a general programming platform. I’m not very familiar with workflows or patterns in numeric/scientific domains.

Also, Julia is a 3rd-gen language.

Yes. My mistake - but you get my meaning.