Understanding object-oriented programming in Julia – Objects (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.

Advertisements

8 thoughts on “Understanding object-oriented programming in Julia – Objects (part 1)

  1. Pingback: Understanding object-oriented programming in Julia – Inheritance (part 2) | The New Phalls

  2. Matthew Browne

    Interesting post…I am just starting to look at Julia. To be clear, the methods that you are declaring as properties of “this” can still have more than one definition to support multiple dispatch, is that correct? Because multiple dispatch is one of the main advantages of using Julia in the first place…

    Reply
  3. Pingback: Revisiting emulated OOP behaviour and multiple dispatch in Julia | The New Phalls

  4. Jeff Bezanson

    I suspect you might realize this already, but julia’s object system works by defining methods separately from data, not by asking you to manually assign functions to object fields. Idiomatically, this code would look like

    “`
    type Response
    data::String
    headers::Dict

    function Response()
    new(“”, Dict{String, String}())
    end
    end

    function addContent(r::Response, append::String)
    r.data = r.data * append
    end

    r = Response()
    addContent(r, “something”)
    “`

    If the types in a function signature are abstract, then that function is inherited by subtypes. It is easy to inherit behavior this way. Methods are not called using dot syntax, but that is just a superficial difference.

    Reply
    1. thenewphalls Post author

      Aye, I was aware of this, but there’s no harm in clarifying.

      The dot syntax approach to calling methods may be superficial, but the implications of having data and methods separate is less so.

      Though not exactly language-breaking, one example of this is that method chaining is not possible. Unlike losing the dot syntax – where typing the variable name just moves from the beginning of the line to the first argument in the function call – the absence of method chaining does require more typing and repetition under circumstances where it could otherwise be avoided. This violates DRY on a microscopic scale.

      (I’ll also violate DRY here by reiterating: I know this isn’t a major issue that invalidates Julia in any way)

      There are other concerns and inconveniences that I’m used to not having to worry about in OOP languages such as C#, as well as design patterns and principles that I’m used to working with in those languages. Though I look for answers to these within Julia, it’s more likely that my findings will be left as an academic exercise and curiosity – and that in the real world, I’ll do things the “Julia way” (as seen in the Julia code I already have up on GitHub)

      I’m planning to follow up on the topic of inheritance either as a response to Jameson’s comment in part 2 (https://thenewphalls.wordpress.com/2014/03/06/understanding-object-oriented-programming-in-julia-inheritance-part-2/) or as a separate post.

      Thanks for the comment.

      Reply
  5. Pingback: Collections on Julia | Randomized.ME

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s