Smudge

The modern and fun way to program.

View the Project on GitHub

The Smudge Programming Language - Functions

Function usefulness

To prove the awesomeness of functions, I’ll give you an example. Let’s write an application that says hello to our friend Marco in a funny way.

import std.io;

func main(){
    /*
     * We could've written simply (with the same
     * number of dashes in the string):
     * io.println("---\nHello, Marco.\n---");
    */
    io.println(" ---------- ");
    io.println("Hello, Marco.");
    io.println(" ---------- ");
}

Now, we want to greet also our friends David, Aaron and Bill, but we want to reuse as much as possible our code. How we can do? With a function obviously:

import std.io;

func greet(name){
    io.println(" ---------- ");
    io.println("Hello, " + name + ".");
    io.println(" ---------- ");
}

func main(){
    greet("Marco");
    greet("David");
    greet("Aaron");
    greet("Bill");
}

Note: on the fifth line we performed a concatenation between strings via the operator +, this is because the operator overloading is widely supported by Smudge.

Actually there are ways much more elegant than this one, but, for now, this is OK for the explanation.

As you see, sometimes it’s actually more convenient to write some code inside a function and call that function.

Arguments

Functions can take arguments (sometimes called also parameters) (in our case there was an argument called name) which are objects passed by the function’s caller that will probably influence the working of the function. We call pure a function which depends only on the given arguments. And we call inpure the ones which depend also on the environment settings (practically, the global variables).

When a function doesn’t have arguments, you can omit the round brackets after its name:

func function_without_arguments () { /* code */ }
// it's the same as:
func function_without_arguments { /* code */ }

However, is good practise to always type them, to make a clearer code.

Smudge is not finicky about the arguments: function callers, in fact, can give more or less of them than the expected number, or even not give arguments at all. The following example is valid code:

import std.io;

func my_function(first_arg, second_arg, third_arg){
    io.println(first_arg, " ", second_arg, " ", third_arg);
}

func main(){
    my_function("A", "B", "C");
    my_function("A", 2, 10.5);
    my_function("A", null);
    my_function();
}

OUTPUT:

A B C
A 2 10.5
A <null> <null>
<null> <null> <null>

Note: <null> is the result converting null to a string.

Default argument values

As you can see, arguments not given by default are null, but you can customize the default value of a function argument by adding = and the desired value after the argument name. When an argument has a specified default value, it’s called optional. Let’s see an example:

import std.io;

func sum(a, b = 1){
    return a + b;
}

func main(){
    io.println(sum(2, 5), " ", sum(1), " ", sum(sum(5), 10));
}

OUTPUT:

7 2 16

Note: As shown in this example, functions can return a value via the return statement which is very simple: return VALUE;.

Plus, we can set as default values some more complex expressions, for example:

import std.io;

func f2 (lhs, rhs){
    return lhs + lhs*rhs;
}

/*
 * random calculates :D
*/
func f(a, b, c = f2(a, b*2) -1){
    return a + b / c;
}

func main(){
    // using floating-point operations!
    io.println(f(1.0, 2.0));
}

OUTPUT:

1.5

VARARGs

At the end, functions can accept a variable number of arguments: those are called VARARGs functions and can be implemented by adding as last argument a special argument ending with three points:

func varargs_func (arg0, arg1, /* argN, */ specialArg...) {}

Now, specialArg won’t never be null. When there are no arguments to pass to specialArg it will be an empty list. Otherwise it will be a list containing all the arguments in excess. Let’s see a simple example:

import std.io;

func foo(args...){
    io.println("args: ", args);
}

func bar(a = 1, b = a+1, c...){
    io.println("a: ", a, "\nb: ", b, "\nc: ", c);
}

func main(){
    foo(0, 1, 2);
    bar(0, 1, 2, 3, 4, 5);
    foo(0, "Hello", [1, 2]);
    foo();


    bar(0, 1, 2);
    bar(0, 1, 2, 3, 4, 5);
    bar(0, "Hello", [1, 2]);
    bar();
}

OUTPUT, as expected:

args: [0, 1, 2]
args: [0, 1, 2, 3, 4, 5]
args: [0, Hello, [1, 2]]
args: []
a: 0, b: 1, c: [2]
a: 0, b: 1, c: [2, 3, 4, 5]
a: 0, b: Hello, c: [[1, 2]]
a: 1, b: 2, c: []

NOTE: We’ve alredy used a lot VARARGS: in fact, the std.io::println function is implemented via VARARGS!

/*
 * in box std.io, natively
*/

// [...]

func println(objects...){
    /* CODE to print each element in 'objects' */
    /* CODE to print new-line character ('\n') */
}

// [...]

This allows writing io.println("Hello"), io.println("Hello ", "World"), io.println("a", "b", "c") and so on.

Statement functions

When the code inside a function is only a statement, we can not use the braces:

func add(a, b)      // we take 2 arguments
    return a + b;

func get            // we don't take arguments
    return 0;

// same as above, but in a single line
func div(a, b) return a / b;
func get2 return 0;

/*
 * The following functions don't do anything,
 * but they can be helpful in some situations.
*/

func f(a, b, c); // arguments
func g(); // no arguments
func g; // no arguments again

We’ll call that feature statement functions to don’t mix them up with in-line functions.

     
Previous Home Next