genie-logo

Genie functions

Page updated October 26, 2009
There is not yet an official logo for Genie/Vala, above image just a placeholder.

Introduction

A function, in relation to an object, is also called a method. But, I will stick with the more generic name, function. In some lanuages also there is a distinction from a procedure, which does not return a value whereas a function does -- again, an unnecessary proliferation of terminology.

If you are reading this you most likely do have some exposure to a programming language, so will know what a function is. Genie is more of the same, no surprises here.

One thing though, Genie has Python's simple clear syntax that designates all blocks of code purely by the indentation. No BEGIN ... END or { ... }!

Unlike Python though, Genie is a statically typed language, so the type of data has to be declared, though Genie does support type inference which is a mechanism by which the Vala compiler can guess the type based on first usage.
So, whereas in Python you might have had i=123, in Genie you would have i:int=123 the first time that i is used in the code. Or, if type inference is used, var i=123  -- pretty easy to get accustomed to!

Any other special features of Genie in relation to functions will be explained as we go along...

A simple function

def addon(x:int) : int
x += 1
return x

init
z:int=3
y:int = addon(z)
print("%d",y)
Pretty understandable, I think. The first line starts with the def keyword, and addon is the name I have chosen for the function. clarifying a few points about this example...

Passed parameters
That first line, see how the passed parameter is defined along with its type. If there are no passed parameters, then don't put anything between the brackets, or if more than one, put a comma delimiter between them.

Return value
If you don't want to return anything, then leave off the :int part.

Something else that you would need to know about is where the various variables are visible, that is, their scope...

Default variable visibility
In a nutshell, the only variable that code inside the function can see is x. It cannot see z or y. Similarly, the mainline code cannot see x. So, I could have declared another variable z inside the function and it is completely different from the mainline z.

Passing by value or reference

In some languages like C and C++ this topic can cause some confusion...

Passing by value
The above code is an example of pass-by-value. That is, the value of z has been passed into the function. The important point about this is that the z variable itself is not visible inside the function so cannot be modified.

Passing by reference
Instead of passing the value of z, if its address was passed then the function would have a means of writing to z directly. But how to do that?

Genie has two broad classifications of data types: value and reference.
These are introduced in my Genie data types page.

Summarising, these are all value types:
int, uint, char, uchar, unichar, bool, float, double, enum
int8 uint8 int16 uint16 int32 uint32 int64 uint64

And these are all reference types:
string, array, dict, list
struct
(see further explanation about struct in the data types page)

The default behaviour is that the value types are passed into a function by value, and the reference types passed by reference.

To illustrate, here is the above example slightly changed, to use strings rather than integers...
def addon(x:string) : string
x = x + "def"
return x

init
z:string = "abc"
y:string = addon(z)
print("%s %s",y,z)
However, when I try to compile this, the Vala compiler spits out this error message:
error: Invalid assignment from owned expression to unowned variable
x = x + "def"
^^^^^^^^^^^^^
Yikes, what's going on here? Isn't x supposed to be by default local to the function? problem is, its the address of the string that gets passed in, so writing to x is to attempt to write to the mainline z. The compiler picks up that z is not supposed to be visible inside the function, so forbids the operation.

Well, if I just want to get the thing to compile, the solution is...
def addon(x:string) : string
s:string = x + "def"
return s

init
z:string = "abc"
y:string = addon(z)
print("%s %s",y,z)
You see? I just declared a local variable inside the function to write to.

But, what if I actually wanted to write to z from inside the function?...

Forced passing by reference: ref

Genie has a neat little qualifier named ref, that you can use to force pass-by-reference. This is handy to use if you are not sure if a parameter is being passed by value or reference, but it also has another advantage...

That last example I gave, the string got passed by reference, but the compiler picked up that z is not supposed to be visible inside the function. However, if I add that ref qualifier, permission is given...
def addon(ref x:string)
x = x + "def"

init
z:string = "abc"
addon(ref z)
print("%s",z)
...hey, look, I didn't even bother with a return statement! Writing to x inside the function is actually writing to z!

Note that I had to place the qualifier in two places.

I can even apply this ref qualifier to the original example in this page:
def addon(ref x:int)
x += 1

init
z:int=3
addon(ref z)
print("%d",z)
That is, the default behaviour was to pass the value of z, but ref forced the address of z to get passed instead. So, writing to x inside the function actually writes to z.

The out qualifier

There is a variation on ref, named out. It does the same thing, but it tells the function that the references being passed in are uninitialised, that is, do not point to anything valid. But, something valid is expected to be written by the function. That is, we expect to "get something out" of the function. For example:
init
f:string = "/etc/rc.d/PUPSTATE"
s:string
len:ulong
FileUtils.get_contents(f, out s, out len)
print(s)
Note, the file /etc/rc.d/PUPSTATE exists in Puppy Linux systems. Whatever file you want to read, there is a system function available named get_contents(), and this particular function expects s and len to be uninitialised and the function will write to s and len. For more info, read my page File and console I/O.

In /usr/share/vala/vapi/glib-2.0.vapi, the get_contents() function is defined:
public bool get_contents (string filename, out string contents, out ulong length = null) throws FileError;
...see, it has the out qualifier. That means we have to put matching outs when we call the function. The compiler will not accept ref.


more stuff to come...


(c) Copyright Nov. 2008,2009 Barry Kauler puppylinux.com, all reproduction rights reserved.