example/functions.primi

Summary

Maintainability
Test Coverage
import std.types
import ._helpers: assert_error

// No semicolons anywhere.

global_variable = 'globál'
c = 100

function add(x, y) {
    assert(global_variable == 'globál', 'Function block inherits variables from outer scope')
    assert(c == 100, 'Declaring variable inside function block overwrites the variable from outer scope')
    return x + y
}
assert(type(add) == types.func, 'Declaring ordinary function')

_ = "Function with parameters cannot be called without parameters"
assert_error(() => {
    add()
}, _)

function sub(x, y) {
    return x - y
}
assert(type(sub) == types.func, 'Declaring ordinary function')

a = 1 + add(sub(4, 3), 5)
assert(a == 7, 'Passing result of function as argument into another function yields correct result')

function nested(a) {
    assert(type(add) == types.func, 'Function block inherits functions from outer scope')
    assert(type(sub) == types.func, 'Function block inherits functions from outer scope')
    return add(a, sub(5, 2))
}

// Anonymous function assigned to variable.
// The result is the same as ordinary function definition.
double_nested = (input, fn) => {

    a = fn(input)
    counter = 0

    while (a > -5) {
        a = a - 3
        counter = counter + 1
    }


    return [a, counter]

}
assert(type(double_nested) == types.func)

result = double_nested(4, nested)
assert(result[0] == -5, "'a' variable")
assert(result[1] == 4, "'counter' variable")

decorator = (fn) => {
    // Decorator returns a new function that wraps some function passed into it.
    return (arg) => {
        return "<prefix>" + fn(arg) + "<suffix>"
    }
}
assert(type(decorator) == types.func)

function reverser(text) {
    result = ""
    for (c in text) {
        result = c + result
    }
    return result
}
assert(type(reverser) == types.func)

reversed = reverser('JELEN')
assert(reversed == 'NELEJ')

// Decorate the reverser function with decorator function.
decorated = decorator(reverser)
d = decorated('hello!')
assert(d == '<prefix>!olleh<suffix>', 'Decorated function returns correct value')

a = 0
function one() {
    return 1
}

function no_return(a, b, c) {
    a = a + 1
}

result = no_return(a, 2, 3)
assert(result == null, 'Function without explicit return returns null')
assert(a == 0, 'What happened in function, stays in function')

// Anonymous function, full syntax.
anon = function(x, y, z) {
    return x(1) + y + z
}

result = anon(function(num) {
    return num * 3
}, 4, 5)

// Anonymous function, short syntax.
anon_short = (i) => {
    if (i == 0) {
        return 1
    }
    return 2
}

result_2 = anon_short(1)
result_3 = anon(anon_short, 10, 100)

// Some example from the internet.
censor = (words) => {
    filtered = []
    for (word in words) {
        if (len(word) != 4) {
            filtered.push(word)
        }
    }
    return filtered
}

censored = censor([
    'haha',
    'very_safe',
    'voldemort',
    'damn',
])
assert(censored == ['very_safe', 'voldemort'])

//
// Calling function without all arguments raises error.
//

was_error = false
try {
    // Dummy function.
    fn = (a, b) => {
        return [a, b]
    }
    // Call without all expected arguments.
    fn(1)
} catch {
    was_error = true
}
assert(was_error, 'Calling function without all arguments raises error')

//
// Iterating over function raises error.
//

was_error = false
will_not_happen_A = true
will_not_happen_B = true
try {
    // Dummy function.
    fn = () => {}
    // Try to iterate over it.
    for (x in fn) {
        will_not_happen_A = false
    }
    will_not_happen_B = false
} catch {
    was_error = true
}
assert(was_error, 'Iterating over function raises error')
_ = 'No code after error was executed'
assert(will_not_happen_A, _)
assert(will_not_happen_B, _)