Outlines

Objects

It’s finally time to find out what all the @ symbols we’ve been using mean. We’ve seen @ being used a few times so far, in expressions like ciorcal@stáitse and slánuimh_rand@mata. To find out what it means, we have to talk about objects.

What are they?

Objects can be thought of as a collection of values. An object can contain anything we want, numbers, text, actions etc. Each member of the object has a name, much like how variables have names.

For example we could have an object that represents a person. That object could have members called age, address or favourite_colour that store the persons age, address or favourite colour.

Objects can also have special behaviours that make them more useful to us, but we’ll see more on that soon.

How do we use objects? To do that we have to use the “@symbol.

What is “@”?

When we want to access a member of an object, we use its name and the “@” symbol to do that. The syntax is:

<member name>@<object>

Let’s say we have an object in a variable called tree, and it has a member called height that contains its height. We would write height@tree to access it.

When we write ciorcal@stáitse we are saying “Get the value with the name ciorcal from the stáitse object.”, then we can call the action as normal (e.g. ciorcal@stáitse(200, 200, 100)).

We can split the expression ciorcal@stáitse(200, 200, 100) into two steps to see this more clearly. The first step will be getting the ciorcal action, and the second will be calling the action.

First we can use ciorcal@stáitse to get the ciorcal action, then we can save it in a variable, let’s call that variable gníomh_ciorcal.

gníomh_ciorcal := ciorcal@stáitse

Now we can call that action as before by writing:

gníomh_ciorcal := ciorcal@stáitse
gníomh_ciorcal(200, 200, 100)

Try it out to see that it works!:

Creation

We make an object by first creating something called an outline. Then we can use the outline to create objects. One outline can be used to create several objects.

For example, we can make an outline for an object that represents a person, then we can create a whole family of people by creating person objects from that outline.

Outlines

An outline is exactly what it sounds like, it’s an outline of the structure of an object. When we make an outline we define the name of the outline, and a list of actions that define the objects behaviour.

We use the creatlach keyword to make an outline, “creatlach” is the Irish for “outline” (or “skeleton”). The syntax to make an outline is as follows:

creatlach <outline-name> {
    <list of actions>
    ...
}

For example, here’s how we would create an outline called Simplí that has no special behaviour. “Simplí” translates as “simple”.

>-- New outline with no actions
creatlach Simplí {
}

This creates a new action called Simplí. When you call Simplí, it will create an object from the Simplí outline and return it.

We can then make an object from the “Simplí” outline by calling Simplí like so:

>-- New outline with no actions
creatlach Simplí {
}

>-- Create an object from the Simplí outline
oibiacht_simplí := Simplí()

We created a new variable called “oibiacht_simplí” to store the object. “oibiacht simplí” means “simple object”. This object is exactly as it sounds, simple. It doesn’t have any members yet. Let’s add a member called “ball” (meaning “member”) now with the value "Ár mball nua" (“Our new member”). The syntax is just like updating a variable:

>-- New outline with no actions
creatlach Simplí {
}

>-- Create an object from the Simplí outline
oibiacht_simplí := Simplí()

>-- Add a new member to oibiacht_simplí
ball@oibiacht_simplí = "Ár mball nua"

Now we can use ball@oibiacht_simplí to access the value we stored in that member.

>-- New outline with no actions
creatlach Simplí {
}

>-- Create an object from the Simplí outline
oibiacht_simplí := Simplí()

>-- Add a new member to oibiacht_simplí
ball@oibiacht_simplí = "Ár mball nua"

>-- Access the member and write
scríobh(ball@oibiacht_simplí)

Try it out!

Challenge

Fill in the following code so that it prints “My age is <your age>”. e.g. “My age is 18”.

Click here to see the solution

Why Objects?

At this point you might be thinking “Why do we need objects? They seem just like variables”. Lets take a look at a quick example of why objects are useful.

The setup is this: We want to write a program that will draw a circle, print its area and its perimeter.

To start, let’s make an empty outline called Ciorcal, and make an action called déan_ciorcal that will create an empty object, and fill in some details. We want to record the x-coordinate, y-coordinate, radius and colour of our circle. We’ll put these in members called x, y, ga and dath.

>-- Empty outline definition
creatlach Ciorcal {
}

>-- Action to make a circle object with x, y, radius and colour
gníomh déan_ciorcal(x, y, ga, dath) {
    >-- Create blank circle
    c := Ciorcal()

    >-- Set our parameters
    x@c = x
    y@c = y
    ga@c = ga
    dath@c = dath

    >-- Return the circle
    toradh c
}

Now we can create three actions, one to draw the circle, one to get the area, and one to get the perimeter. We’ll call these actions tarraing, achar and imlíne. Each of these actions only needs to take one parameter, the circle.

>-- Draw the circle
gníomh tarraing(ciorc) {
    >-- Set the colour
    dath@stáitse(dath@ciorc)

    >-- Draw the circle
    ciorcal@stáitse(x@ciorc, y@ciorc, ga@ciorc)
}

>-- Return the area, area = pi * r^2
gníomh achar(ciorc) {
    toradh pi@mata * ga@ciorc * ga@ciorc
}

>-- Return the perimeter, perimeter = 2 * pi * r
gníomh imlíne(ciorc) {
    toradh 2 * pi@mata * ga@ciorc
}

Now we can use our déan_ciorcal action to create a circle, and our three other actions to draw it, print the area and print the perimeter.

c := déan_ciorcal(100, 100, 50, "dearg")
tarraing(c)
scríobh("Area is:", achar(c))
scríobh("Perimeter is:", imlíne(c))

Try out the whole program! Switch to the console to see the text output.

Behaviour

This is nice pattern, but it’s messy. Can we write this in a nicer way?

This is where outline actions come in, we can move our actions like “tarraing”, “imlíne” and “achar” into the definition of the outline.

First lets look at the tarraing action. We can move this action into the Ciorcal outline definition:

creatlach Ciorcal {

    >-- Draw the circle
    gníomh tarraing(ciorc) {
        >-- Set the colour
        dath@stáitse(dath@ciorc)

        >-- Draw the circle
        ciorcal@stáitse(x@ciorc, y@ciorc, ga@ciorc)
    }
}

Now when we create an object from the Ciorcal outline it will have the tarraing action as a member (with the name tarraing). For example we can access the action like this:

c := déan_ciorcal(100, 100, 50, "dearg")
tarraing@c

Then we can call the action like this:

c := déan_ciorcal(100, 100, 50, "dearg")
tarraing@c(c)

This code works, try it out!

However, this line is very strange: tarraing@c(c). We had to use c twice. Is there a better way? There is! We can use the seo keyword to write some simpler code.

Seo

seo translates as “this”. When we use an outline to create an object, we call this object an instance of the outline. We can use one outline to make many instances. When you access an action from an instance, the action is bound to that instance. We can use the seo keyword to take advantage of this connection.

We we create an action in an outline statement (as we did with tarraing above) we can use seo to reference the instance that is bound to the action when it is called. This is quite complex so we’ll look at example to understand the details:

Let’s look at the tarraing action again:

creatlach Ciorcal {

    >-- Draw the circle
    gníomh tarraing(ciorc) {
        >-- Set the colour
        dath@stáitse(dath@ciorc)

        >-- Draw the circle
        ciorcal@stáitse(x@ciorc, y@ciorc, ga@ciorc)
    }
}

We can remove this ciorc argument and use the seo keyword instead:

creatlach Ciorcal {

    >-- Draw the circle
    gníomh tarraing() {
        >-- Set the colour
        dath@stáitse(dath@seo)

        >-- Draw the circle
        ciorcal@stáitse(x@seo, y@seo, ga@seo)
    }
}

What happens now when we call this action (without the argument)?

c := déan_ciorcal(100, 100, 50, "dearg")
tarraing@c()

When tarraing is accessed with tarraing@c a connection is made between seo and the object in c.

Now when we we run the action, seo is equal to the object in c and the action works directly as it worked when it used an argument.

The Other Actions

Now we can change achar and imlíne into outline actions as well. We should move them into the outline definition statement and we should use seo instead of the one argument that they have.

creatlach Ciorcal {

    >-- Draw the circle
    gníomh tarraing() {
        >-- Set the colour
        dath@stáitse(dath@seo)

        >-- Draw the circle
        ciorcal@stáitse(x@seo, y@seo, ga@seo)
    }

    >-- Return the area, area = pi * r^2
    gníomh achar() {
        toradh pi@mata * ga@seo * ga@seo
    }

    >-- Return the perimeter, perimeter = 2 * pi * r
    gníomh imlíne() {
        toradh 2 * pi@mata * ga@seo
    }
}

Now we can use achar@c() and imlíne@c() instead of achar(c) and imlíne(c).

c := déan_ciorcal(100, 100, 50, "dearg")
tarraing@c()
scríobh("Area is:", achar@c())
scríobh("Perimeter is:", imlíne@c())

Try out the code now!

Constructor

Outline actions are useful, but can we get rid of the need for the déan_ciorcal action?

Yes we can! Setanta let’s you create a special outline action called a constructor. A constructor is an action that is called when an object is made from the outline.

To make a constructor, you create an outline action with the name “nua”, which means “new”. Then when you create an object from that outline, the arguments you use are passed into the constructor action.

Quick Example

Let’s see a quick example, we’ll make an outline called Person, and give it a constructor that takes one argument, name.

creatlach Person {
    gníomh nua(name) {
    }
}

Let’s add code into the constructor to write the name that’s passed in:

creatlach Person {
    gníomh nua(name) {
        scríobh("Created a person called", name)
    }
}

Now when we create a new person, you can pass in the name you want:

creatlach Person {
    gníomh nua(name) {
        scríobh("Created a person called", name)
    }
}

me := Person("Eoin")

Try it out! You’ll see that "Created a person called Eoin" is written on the console.

Ciorcal Constructor

Now we can get rid of our déan_ciorcal action, and use a constructor instead. Let’s create our constructor that takes the same arguments, x, y, ga and dath. We can use the seo keyword to store those arguments in the instance we are creating

gníomh nua(x, y, ga, dath) {
    x@seo = x
    y@seo = y
    ga@seo = ga
    dath@seo = dath
}

Now we can totally get rid of the déan_ciorcal action, and instead of creating c by writing:

c := déan_ciorcal(100, 100, 50, "dearg")

we can write:

c := Ciorcal(100, 100, 50, "dearg")

Our final program looks like this:

creatlach Ciorcal {

    gníomh nua(x, y, ga, dath) {
        x@seo = x
        y@seo = y
        ga@seo = ga
        dath@seo = dath
    }

    >-- Draw the circle
    gníomh tarraing() {
        >-- Set the colour
        dath@stáitse(dath@seo)

        >-- Draw the circle
        ciorcal@stáitse(x@seo, y@seo, ga@seo)
    }

    >-- Return the area, area = pi * r^2
    gníomh achar() {
        toradh pi@mata * ga@seo * ga@seo
    }

    >-- Return the perimeter, perimeter = 2 * pi * r
    gníomh imlíne() {
        toradh 2 * pi@mata * ga@seo
    }
}

c := Ciorcal(100, 100, 50, "dearg")
tarraing@c()
scríobh("Area is:", achar@c())
scríobh("Perimeter is:", imlíne@c())

Let’s give it a try!

We’ve seen here that we can use objects to write nicer code, but they are even more powerful than that!

Inheritance

In Setanta an outline can inherit behaviour from another outline. What does this mean? We can create a “parent” outline and some “child” outlines. Then every child-outline has every action that the parent-outline has. (We can also say “super-outline” and “sub-outline” for these outlines).

This is much clearer with an example:

Example

Look at this outline:

creatlach Animal {
    gníomh move() {
        scríobh("I'm moving")
    }
}

This outline only has one action (move), and when it is called it write “I’m moving” on the console.

Now lets create a new outline Dog that is a child-outline of Animal. We use the keyword ó to do this. ó translates as “from”.

creatlach Animal {
    gníomh move() {
        scríobh("I'm moving")
    }
}

creatlach Dog ó Animal {
}

Dog has no direct behaviour, but because it is a child of Animal it inherits the action move. To see this lets make an instance of Dog and call move:

creatlach Animal {
    gníomh move() {
        scríobh("I'm moving")
    }
}

creatlach Dog ó Animal {
}

d := Dog()
move@d()

Try it!

You should hopefully see “I’m moving” on the console.

We can still put actions directly in the Dog outline too, like this:

creatlach Animal {
    gníomh move() {
        scríobh("I'm moving")
    }
}

creatlach Dog ó Animal {
    gníomh eat() {
        scríobh("I love sausages")
    }
}

d := Dog()
move@d()

Now eat@m() would write “I love sausages” on the console. We are able to create another child-outline from Animal again:

creatlach Animal {
    gníomh move() {
        scríobh("I'm moving")
    }
}

creatlach Dog ó Animal {
}

creatlach Cat ó Animal {
}

d := Dog()
move@d()

The Cat outline also has the move action:

We can use inheritance to share actions with many outlines, isn’t that very useful?

tuis

Setanta has another keyword: tuis. We can use tuis to access the actions of the parent outline. tuis is short for “tuismitheoir” which means “parent”.

Think about this code here:

This program has an outline that defines a simple cake which has an action to list the ingredients.

Imagine that we have another type of cake: a lemon cake. The ingredients of this cake is exactly the same but we also need a lemon. How do we represent this in the program? We can create a new outline with another action to get this new list like this:

This code works, but we had to write the whole list of ingredients twice, can we use inheritance to write better code? We can!

Look at this code:

creatlach Cake {
    gníomh ingredients() {
        toradh ["egg", "flour", "sugar", "butter"]
    }
}

creatlach LemonCake ó Cake {
    gníomh lemon_ingredients() {
        base_ingredients := ingredients@seo()
        toradh base_ingredients + ["lemon"]
    }
}

c := Cake()
scríobh("The ingredients of a cake are:", ingredients@c())

cl := LemonCake()
scríobh("The ingredients of a lemon cake are:", lemon_ingredients@cl())

We changed the name of the action and we used inheritance to get the ingredients of the simple cake. This code works too, try it out:

I’m still not satisfied with this code however because we had to change the name of the action, can we do it without changing the name?

We can use tuis to do this. You use tuis just like you use seo but it has the actions of the parent outline; It doesn’t have the actions of the child outline. Because of this we can use the same name because we are able to use tuis to specify the action in the Cake outline.

Here is our new code:

creatlach Cake {
    gníomh ingredients() {
        toradh ["egg", "flour", "sugar", "butter"]
    }
}

creatlach LemonCake ó Cake {
    gníomh ingredients() {
        base_ingredients := ingredients@tuis()
        toradh base_ingredients + ["lemon"]
    }
}

c := Cake()
scríobh("The ingredients of a cake are:", ingredients@c())

cl := LemonCake()
scríobh("The ingredients of a lemon cake are:", ingredients@cl())

Try it out!