A Tour of Morfa

Declaring new operators

Infix ^ for exponentiation

Here is how the exponentiation operation ^ may be introduced to Morfa (there is no built-in one).

An operator declaration introduces ^ as a new infix operator with a high precedence (so that it binds stronger than any built-in arithmetic operators).

operator ^ { kind = infix, precedence = call }

Then we provide an implementation for integer exponentiation:

func ^ (base: int, power: int): int
{
    assert (power >= 0);

    if (power == 0)
        return 1;

    var x = base ^ (power / 2);
    var xx = x * x;

    return if (power % 2 == 0) xx else xx * base;
}

Time for some tests:

unittest
{
    assert (0 ^ 3 == 0);
    assert (2 ^ 0 == 1);
    assert (2 ^ 3 == 8);
    assert (2 * 2 ^ 2 == 8); // should be 2 * (2 ^ 2), not (2 * 2) ^ 2
}

One thing still needs fixing: 2 ^ 3 ^ 2 evaluates to (2 ^ 3) ^ 2, that is 64. But exponentiation is usually treated as right-associative (see for example the Wikipedia entry for Associative property), that is, xyz = x(yz) holds for all x, y and z. Thus 2 ^ 3 ^ 2 should be equal to 2 ^ (3 ^ 2) = 512.

To achieve this we add an associativity annotation to the operator declaration. We have to declare a new operator to avoid conflicts with ^ declared above as left-associative, which is the default when the associativity is not set explicitely:

operator ^^ { kind = infix, precedence = call, associativity = right }

alias ^^ = ^; // Use the same implementation for both operators

unittest
{
    assert (2 ^ 3 ^ 2 == 64);    // '^' is left-associative
    assert (2 ^^ 3 ^^ 2 == 512); // '^^' is right-associative
}

Overloading ^

We may add another implementation of ^ for text exponentiation:

func ^ (base: text, power: int): text
{
    assert (power >= 0);

    if (power == 0)
        return "";

    var x = base ^ (power / 2);
    var xx = x ~ x;

    return if (power % 2 == 0) xx else xx ~ base;
}

unittest
{
    assert ("la" ^ 3 == "lalala");
    assert ("la" ^ 2 ^ 2 == "lalalala");
}

Prefix ! for negation

In Morfa, boolean negation is written using the operator not: not true = false and not false = true. In many languages the prefix ! is used instead. This is easy to mimic in Morfa:

operator ! { kind = prefix, precedence = not }

func ! (b: bool)
{
    return not b;
}

unittest
{
    assert (!true == false);
    assert (! !true == true);
}

Note the space between two !'s in the last assert statement. The expression !!true would be treated as an application of the operator !! to true.

Instead of implementing the function ! we may create an alias as well:

unittest
{
    alias ! = not;
    assert (!false == true);
}

Postfix ! for factorial

The operator ! is also commonly used for the factorial function. Let us introduce this notation in Morfa:

operator ! { kind = postfix, precedence = call }

func $! (n: int)
{
    assert (n >= 0);

    if (n == 0)
        return 1;

    return n * (n-1)!;
}

unittest
{
    assert (0! == 1);
    assert (5! == 120);
}

Note that the function implementing the factorial is called $!, not !. This way implementations of the prefix ! and the postfix ! with arguments of the same type may exist in the same scope.

quoting attribute

The attribute quoting allows for some customizable syntactic sweetening when used in an operator declaration:

import morfa.base;
operator ' { kind = prefix, precedence = max, quoting }

Now, operator ' will wrap the identifier to the right in quoting marks "" to turn it into a string literal.

func '(t: text): void
{
    println(t);
}

unittest
{
    'Hello; 
    'World;
}