Re-posted from: http://thenewphalls.wordpress.com/2014/02/19/understanding-object-oriented-programming-in-julia-part-1/
Disclaimer #1: I’m new to Julia – so it’s possible that I may have missed or misunderstood aspects of the language.
Disclaimer #2: Julia is not necessarily intended to be an object-oriented language or even intended for use in professional/commercial software development.
Disclaimer #3: The OOP terminology used here is for familiarity with PHP and such, but may not be appropriate for Julia.
Coming from working with OOP in languages like PHP, Java and C++, it took me a while to adjust to the Julia approach to the concept of objects. Take the following paragraph from the documentation (relevant parts highlighted in bold):
In mainstream object oriented languages, such as C++, Java, Python and Ruby, composite types also have named functions associated with them, and the combination is called an “object”. In purer object-oriented languages, such as Python and Ruby, all values are objects whether they are composites or not. In less pure object oriented languages, including C++ and Java, some values, such as integers and floating-point values, are not objects, while instances of user-defined composite types are true objects with associated methods. In Julia, all values are objects, but functions are not bundled with the objects they operate on. This is necessary since Julia chooses which method of a function to use by multiple dispatch, meaning that the types of all of a function’s arguments are considered when selecting a method, rather than just the first one (see Methods for more information on methods and dispatch). Thus, it would be inappropriate for functions to “belong” to only their first argument. Organizing methods into function objects rather than having named bags of methods “inside” each object ends up being a highly beneficial aspect of the language design.
What this means is that objects have properties, but no methods. However, it’s possible to define a single function within the object’s type definition that will act as the object’s constructor. If you’re familiar with C, objects in Julia can be thought of as C structs with the addition of a constructor.
Having read through the docs and existing Julia source code, it seems that the way to encapsulate functionality within a Julia object is to assign functions manually – ideally (but not necessarily) from within the constructor – to properties within the object. The same applies to defining default values for properties. As an example, below is the current version of the Response class from my experimental web framework.
type Response data::String headers::Dict addContent::Function setHeader::Function getHeaders::Function getContents::Function getResponse::Function function Response() this = new () this.data = "" this.headers = Dict{String, String}() this.addContent = function (append::String) this.data = this.data * append end this.setHeader = function (header::String, value::String) this.headers[header] = value end this.getHeaders = function () headers = "" for (header, value) in this.headers headers = headers * header if length(value) > 0 headers = headers * ": " * value end headers = headers * "\n" end return headers end this.getContents = function () return this.data end this.getResponse = function () response = "" response = this.getHeaders() * "\n" * this.getContents() return response end return this end end
To expand on the noteworthy parts:
data::String headers::Dict addContent::Function setHeader::Function getHeaders::Function getContents::Function getResponse::Function
These are definitions for the properties of the object. The top two are intended for use as typical data properties, whereas the Function properties are intended to contain the methods for the object, which will be assigned in the constructor. This distinction is arbitrary; Julia itself makes no distinction between these two types.
function Response() ... end
This is the constructor definition. There are a number of different options for this within Julia’s syntax, but the style used here is the most familiar to me.
Constructors in Julia differ from the PHP-esque style of constructors, which exist within an instance of an object and are executed immediately after the instance has been created. In Julia, constructors are responsible for the actual creation of an instance, as you’ll see below.
this = new ()
This code is where the instance of the object is created and assigned to a “this” variable. My use of “this” here is again only for the sake of familiarity; any valid variable name would work the same.
this.data = "" this.headers = Dict{String, String}()
Here, default values are assigned to the new instance’s properties.
this.addContent = function (append::String) ... end
This code is an example of assigning a module to the object. Essentially, it’s the same as assigning a value to a property; it’s just that in this case, the value is a callable function.
return this
When the instance has been constructed, it’s returned to the caller.
Instances of the object are then created in user code as below:
response = Response()
Methods can be called as:
response.addContent("Something to print")
Bells and whistles
As mentioned before, access modifiers appear to be missing from Julia. Therefore, everything is public – and even methods can be overwritten if new functions are assigned to their respective properties. Also, as methods don’t “belong” to the object, it may not be possible to reproduce the behaviour of inheritance as in other languages (getting my head around Julia’s inheritance will be part 2).
It may be too early to say, but at this stage it appears that Julia may be lacking the necessary features for effective use of traditional OOP development patterns – though that doesn’t necessarily mean that the language itself or its implementation of objects is broken.
Update
Part 2 is now online here.