[][src]Module wlambda::prelude

This module defines some default functions and operations available in the WLambda language.

For an example, refer to create_wlamba_prelude.

WLambda Reference

WLambda is a functional programming language. The main goal of this implementation is the extension of Rust applications with dynamic scripting. The syntax gravitates around the concept that everything is callable like a function. There is special syntax for composing arguments of functions, to give the programmer the ability to express his thoughts as they see fit.

You can use this document as reference or as cover to cover lecture. It starts out with functions and the base data types of WLambda, where I also explain some semantics of the language.

Please note: I expect you to know how to program and be familiar with at least one other dynamic language like JavaScript, Perl or at least Python. The syntax and semantics of WLambda are different from what you might know. Think of it more like a LISP without parenthesis. The syntax is loosely inspired from Smalltalk, LISP and Perl.

Syntax

A more formal introduction to the syntax can be found in the parser API documentation.

Functions (part 1/2)

A function can be defined using the { ... } syntax and the \ _statement_ syntax: To give functions a name, you need to assign them to a variable with the !_name_ = _expr_ syntax.

To call functions, you have at least 2 alternatives. First is the bare _expr_ arg1 arg2 arg3 arg4 syntax. And the second is the delimiter full variant: _expr_ (arg1, arg2, arg3, ...). You can always delimit the first variant using the [ ... ] brackets.

The arguments passed to the function are accessible using the _, _1, _2, ..., _9 variables. If you need to access more arguments the @ variable holds a list of all arguments.

!twoify = { _ * 2 };

wl:assert_eq twoify(2) 4;

!twoify2 = \_ * 2;

wl:assert_eq twoify2(2) 4;

# You may also call them directly, notice the bracket [ ... ] syntax
# for delimiting the inner function call:
wl:assert_eq [{ _ * 2 } 2] 4;

If you want to name arguments, you can use the destructuring assignment syntax:

!add = {!(a, b) = @;
    a + b
};

wl:assert_eq add(1, 2) 3;

Function arity checks

Functions check the number of arguments passed to them. The compiler tries to infer the number of arguments the function requires by looking at the parameter variables _ to _9 and @. If the compiler gets it wrong, you can:

  • Define minimum and maximum number of arguments with: {|min < max| ... }
  • Define exact number of arguments with: {|num_of_args| ... }
  • Accept any number of arguments: {|| ... }

For the shortened function syntax there is:

  • \|min < max| ...
  • \|num_of_args| ...
  • \|| ...

Here an example:

!dosomething = {|2 < 4|
    !a = _;
    !b = _1;
    !c = _2;
    !d = _3;

    # Please note: We have to assign _ to _3 here, because
    # the arms of the conditional below have
    # their own set of arguments.

    [is_none c] { a + b } { a * b + c * d }
};

wl:assert_eq dosomething(1, 2)         3;
wl:assert_eq dosomething(2, 2, 3, 4)  16;

Data Types

None sentinel value: $n or $none

This is a special sentinel value that is returned by functions and when a non existing field of a datastructure is accessed. It's semantic meaning is that there is no value.

Please note for API design: In case of errornous states you should not return a $none but an $error value.

wl:assert ~ $n        == $none;
wl:assert ~ int($n)   == 0;
wl:assert ~ float($n) == 0.0;
wl:assert ~ str($n)   == "$n";
wl:assert ~ is_none($n);

Error values: $e expr or $error expr

There are no exceptions in WLambda, except the panic, that halts all execution of the currently running WLambda program. To signal errors, you return an $error value.

These error values, if not handled, will cause a panic of your program. This means, you need to handle returned error values one way or the other.

The error value wraps any value you pass to the $error or $e constructor syntax.

wl:assert ~ is_err ~ $e "something went wrong!"

There are more routines except is_err to handle an error. _? will return from the currently executed function up until some given label. on_error executes a function if the second argument was an error value. Otherwise it just passes through the value. unwrap will explicitly cause an panic if an error value was passed to it. All other values will be passed through. And unwrap_err unwraps an error value, it's the opposite of unwrap because it will cause a panic if you don't pass an error value.

Return on error with _?

!func = { $e "this failed!" };

!other = {
    # some code ...

    _? func(); # If you would not catch the error value here,
               # the program would panic, as an error value
               # must not be ignored!

    # other code ...

    panic "this will never be reached!";

    # something here...
};

wl:assert ~ [unwrap_err other()] == "this failed!";

_? can take up to 2 arguments. If so, the first argument is interpreted as jump label. That is handy if you want to jump up multiple call frames:

!failing_func = { $e :FAIL };

!func = \:some_unique_label {
    [ _ == 42 ] {
        displayln "We got 42!";

        # The `then` branch we are currently in is a call frame.
        # To jump further up the call stack, we need the label
        # we defined for the function above.
        !val = _? :some_unique_label failing_func();

        displayln "Returned:" val;
    }
};

wl:assert_eq [unwrap_err ~ func 42] :FAIL;

Handle errors with on_error

!func = {
    [_ == 13] {
        $e "this failed!"
    } {
        "all ok!"
    }
};

!:ref x = $n;

# The first function of on_error will be called with the unwrapped
# error if an error occured.
on_error {|4| .x = _; } ~ func 13;
wl:assert_eq x "this failed!";

!ret = on_error {|4| .x = _; } ~ func 1;
wl:assert_eq ret "all ok!";
  • bool
  • int
  • floats
  • string
  • bytes
  • symbols
  • lists/vectors
  • maps

Operators

Arithmetics

  • +
  • -
  • *
  • /
  • %
  • ^

Comparison

  • ==
  • !=
  • <
  • >
  • <=
  • >=

Bitwise

  • &|
  • &
  • &^
  • <<
  • >>

Functions (part 2/2)

Function call composition

  • chaining
  • traditional () call syntax
  • ~ syntax
  • | syntax
  • || syntax
  • [...] syntax

Control Flow - Returning

  • :lbl { ... } syntax and returning

WLambda uses labelled blocks for control flow, as returning from the current function would not be very helpful for the control flow in wlambda in case of conditional execution.

!some_func = \:outer {
    !x = 10;
    # does stuff

    [x == 10] {
        return :outer 20
    }

    # more stuff that is not executed if x == 10.
}

Conditional Execution - if / then / else

WLambda has no if. Conditional execution is provided by the bool data type. As in WLambda everything can be called like a function, you can just pass other functions as arguments to $true and $false. If you pass a function as first argument to $true, it will be executed. If you pass a function as second argument to $false then that will be executed.

[10 == 10] { displayln "10 is 10" };         #=> prints "10 is 10"
[10 != 10] { displayln "10 is not 10" };     #=> doesn't print anything

!x = 20;

[x == 20] {
    displayln "x is 20";
} {
    displayln "x is 20";
}; # Do not forget the ";"!

Actually, as the values $true and $false can be called like any other function you may write it also like this, which is not the recommended syntax, but still works:

[10 == 10]({ displayln "10 is 10" });

!x = 21;
[x == 20]({ displayln "x is 20" }, { displayln "x isn't 20" }); #=> print "x isn't 20"

Lexical Scope and Variable assignment

  • !x = y variable definition
  • .x = y assignments
  • !:ref x = y upvalue references
  • !:wref x = y weak upvalue references
  • !(x, y) = list / map destructuring assignments
  • (x, y) = list / map destructuring assignments

Arithmetics

  • operator precedence syntax
  • prefix operator syntax

Modules

export


!expr = { _ + 30 };

!@export symbol expr; # exports symbol with value of expr (a function)

import


!@import x tests:test_mod; # prefixes everything from modixes with x:

wl:assert ~ [x:symbol 10] == 40;

Prelude

wl:assert bool [message]

Just a simple assertion function that panics if the first argument is not true. Returns the passed value if it is a true value. You can pass an optional message as second parameter.

wl:assert $false; #=> Panic
wl:assert 120;    #=> 120

wl:assert_eq actual expected [message]

This function check if the actual value is equal to the expected value and panics if not. The optional message is passed in the panic for reference.

!x = 30 * 2;
wl:assert_eq x 60 "30 * 2 == 60";

Optional Prelude

regex

chrono

chrono:timestamp [format]

For the documentation of format please consule the chrono Rust crate documentation: chrono crate strftime format.

!year_str = chrono:timestamp "%Y";
displayln :XXXX ~ [year_str | int] == 2019;
wl:assert ~ [year_str | int] == 2019;

!now_str = chrono:timestamp();

Functions

create_wlamba_prelude

Defines a new global Environment for running the compiler.