[−][src]Module wlambda::prelude
This module defines some default functions and operations available in the WLambda language.
You there are two WLambda modules provided by this module:
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 3 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 ( ... )
parenthesis around the whole call.
Third you can call a function with a vector as argument with _expr_[[_expr_]]
,
where the second expression should return a vector (if it doesn't it will use the
value as first argument).
Here are examples:
# All the second variant:
std:assert_eq[std:str:cat[1, 2, 3], "123"];
# Can also be written as:
std:assert_eq (std:str:cat 1 2 3) "123";
# As the third variant:
!some_args = $[1, 2, 3];
std:assert_eq std:str:cat[[some_args]] "123";
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 };
std:assert_eq twoify[2] 4;
!twoify2 = \_ * 2;
std:assert_eq twoify2[2] 4;
# You may also call them directly, notice the parenthesis ( ... ) syntax
# for delimiting the inner function call:
std:assert_eq ({ _ * 2 } 2) 4;
If you want to name arguments, you can use the destructuring assignment syntax:
!add = {!(a, b) = @;
a + b
};
std: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 }
};
std:assert_eq dosomething[1, 2] 3;
std: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.
std:assert ~ $n == $none;
std:assert ~ int[$n] == 0;
std:assert ~ float[$n] == 0.0;
std:assert ~ str[$n] == "$n";
std: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.
std: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.
Most functions don't accept errors in their arguments. If an error is encountered, a panic will occur. There are only a few functions that accept error values in their arguments:
- panic
_?
- unwrap_err
- error_to_str
- unwrap
- on_error
- return
- break
- bool
- type
- match
- assert
- assert_eq
- is_some
- is_none
- is_err
- is_map
- is_vec
- is_fun
- is_str
- is_wref
- is_ref
- is_bool
- is_bytes
- is_sym
- is_float
- is_int
- ==
- !=
- std:to_ref
- std:ref_id
All other functions don't accept errors as their argument.
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...
};
std: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 ) {
std: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[];
std:displayln "Returned:" val;
}
};
std:assert_eq (unwrap_err ~ func 42) :FAIL;
Handle errors with on_error
!func = {
(_ == 13) {
$e "this failed!"
} {
"all ok!"
}
};
!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;
std:assert_eq $*x "this failed!";
!ret = on_error {|4| .x = _; } ~ func 1;
std:assert_eq ret "all ok!";
Booleans
True and false are represented by $t
and $f
or $true
and $false
,
whatever suits your coding style better.
You can either use a boolean value with two arguments, where $true
will call the first argument, and $false
the second argument. So to
check for truthness you can just do:
!x = 10;
!some_num =
(x == 10) { "it is ten" } { "it is not ten" };
std:assert_eq some_num "it is ten";
.x = 20;
.some_num =
(x == 10) { "it is ten" } { "it is not ten" };
std:assert_eq some_num "it is not ten";
You can cast other values into a boolean with the bool
function:
std:assert_eq (bool 1) $true;
std:assert_eq (bool 0) $false;
std:assert_eq (bool $e :x) $false;
std:assert_eq (bool $n) $false;
std:assert_eq (bool "") $false;
std:assert_eq (bool "0") $false;
std:assert_eq (bool "1") $true;
std:assert_eq (bool :0) $false;
std:assert_eq (bool :1) $true;
std:assert_eq (bool 0.0) $false;
std:assert_eq (bool 0.1) $false;
std:assert_eq (bool 1.0) $true;
std:assert_eq (bool {}) $true;
std:assert_eq (bool $b"") $false;
std:assert_eq (bool $b"\x00") $false;
std:assert_eq (bool $b"\x01") $true;
You can also check if something is a boolean with is_bool
:
std:assert ~ is_bool $true;
std:assert ~ is_bool $false;
std:assert ~ not[is_bool $n];
std:assert ~ not[is_bool ""];
std:assert ~ not[is_bool 0];
64-Bit Integers
64-Bit Floats
Strings
Bytes (or Byte Vectors)
Bytes are a special kind of strings. Their literal form is:
$b"abc";
$b"\xFF\xFD\x00";
$Q/ABCDEF\xFD/; # \xFD is not an excape sequence here!
Call Properties oy Bytes
You can index inside a byte array by calling it with an integer:
std:assert_eq ($b"ABC" 1) $b"B";
You can extract a whole range when calling with 2 integers:
std:assert_eq ($b"ABCDEF" 2 3) $b"CDE";
If you call a bytes value with a map as argument, the bytes value is
converted to a string internally using str
and the value from the map
is returned:
!some_map = ${ a = 20, b = 30 };
std:assert_eq ($b"a" some_map) 20;
std:assert_eq ($b"b" some_map) 30;
std:assert_eq some_map.$b"a" 20; # with method call syntax
Byte Conversion Functions
You can convert bytes to strings in a multitude of ways:
- str bytes
std:assert_eq (str $b"abc") "abc"; std:assert_eq (str $b"abc\xFF") "abcÿ"; std:assert_eq (str $Q/ABCDEF\xFD/) "ABCDEF\\xFD";
- std:bytes:to_hex bytes [group-len [group-sep]]
std:assert_eq (std:bytes:to_hex $b"\xFF\x0A\xBE\xEF") "FF0ABEEF"; std:assert_eq (std:bytes:to_hex $b"\xFF\x0A\xBE\xEF" 2) "FF 0A BE EF"; std:assert_eq (std:bytes:to_hex $b"\xFF\x0A\xBE\xEF" 2 ":") "FF:0A:BE:EF";
- std:str:from_utf8 bytes
std:assert_eq (std:str:from_utf8 $b"\xC3\xA4\xC3\x9F\xC3\xBF") "äßÿ"; std:assert_eq (std:str:from_utf8 [std:str:to_bytes "äßÿ"]) "äßÿ"; # broken UTF8 will result in an error: std:assert ~ is_err (std:str:from_utf8 $b"\xC3\xC3\xA4\xC3\x9F\xC3\xBF");
- std:str:from_utf8_lossy bytes
std:assert_eq (std:str:from_utf8_lossy $b"\xC3\xC3\xA4\xC3\x9F\xC3\xBF") "�äßÿ";
You can even convert bytes to vectors of integers back and forth:
!v = std:bytes:to_vec $b"ABC";
std:assert_eq (str v) (str $[65, 66, 67]);
std:push v 64;
!b = std:bytes:from_vec v;
std:assert_eq b $b"ABC@";
There is also an invese operation to bytes:to_hex
:
std:assert_eq (std:bytes:from_hex ~ std:bytes:to_hex $b"ABC") $b"ABC";
Symbols
Vectors (or Lists)
Associative Maps (or String to Value mappings)
Operators
Arithmetics
- +
- -
- *
- /
- %
- ^
Comparison
- ==
- !=
- <
- >
- <=
- >=
Bitwise
- &|
- &
- &^
- <<
- >>
Functions (part 2/2)
Function call composition
- chaining
- traditional () call syntax
- ~ syntax
- | syntax
- || syntax
$[] || push 10 $[10] $[] || push 10 || push 20 $[10,20] !x = { push _1 _ }; $n $[] | x 10 | x 20 $[10,20]
- [...] 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) { std:displayln "10 is 10" }; #=> prints "10 is 10"
(10 != 10) { std:displayln "10 is not 10" }; #=> doesn't print anything
!x = 20;
(x == 20) {
std:displayln "x is 20";
} {
std: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)[{ std:displayln "10 is 10" }];
!x = 21;
(x == 20)[{ std:displayln "x is 20" }, { std: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
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:
std:assert ~ (x:symbol 10) == 40;
You can also skip the prefix:
!@import std;
!v = $[];
push v 10; push v 20;
std:assert_eq (str v) "$[10,20]";
Standard Library
std:eval code-string
Evaluates code-string in the current global environment and returns the generated value. If the code leads to any kind of evaluation error, an error object is returned.
std:assert_eq (std:eval "1 + 2") 3;
!:global X = 20;
std:assert_eq (std:eval "1 + X") 21;
std: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.
std:assert $false; #=> Panic
std:assert 120; #=> 120
std: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;
std:assert_eq x 60 "30 * 2 == 60";
I/O
std:io:file:read_text filename
Opens the file filename and returns it's contents interpreted as UTF8 text as string.
std:io:file:write_safe "prelude_test.txt" "abcäöü";
!t = std:io:file:read_text "prelude_test.txt";
std:assert_eq t "abcäöü" "reading text from file works";
std:io:file:read filename
Opens the file filename and returns it's contents as byte buffer.
std:io:file:write_safe "prelude_test.txt" "abcäöü";
!t = std:io:file:read "prelude_test.txt";
.t = std:str:from_utf8 t;
std:assert_eq t "abcäöü" "reading binary from file works";
std:io:file:write_safe filename bytes-or-string
Creates a new file with the given filename but with a "~" appended and writes the contents into it. After successful write, it renames the file to the given filename.
std:io:file:append filename bytes-or-string
Opens the given filename in append mode and appends bytes-or-string to the end of the file.
Optional Standarf Library
serialization
ser:json data [no_pretty]
Serializes the data and returns a JSON formatted (and pretty printed) string. Optionally not pretty printed if no_pretty is a true value.
!str = std:ser:json $[1,2.3,${a=4}] $t;
std:assert_eq str "[1,2.3,{\"a\":4}]";
deser:json string
Deserializes the JSON formatted string into a data structure.
!data = std:deser:json ~ std:ser:json $[1,2.3,${a=4}];
std:assert_eq data.0 1;
std:assert_eq data.1 2.3;
std:assert_eq data.(2).a 4;
ser:msgpack data
Serializes the data and returns a msgpack bytes value.
std:assert_eq (std:ser:msgpack $b"abc") $b"\xC4\x03abc";
deser:msgpack bytes
Deserializes the msgpack bytes value into a data structure.
std:assert_eq (std:deser:msgpack $b"\xC4\x03abc") $b"abc";
regex
chrono
chrono:timestamp [format]
For the documentation of format please consule the chrono Rust crate documentation: chrono crate strftime format.
!year_str = std:chrono:timestamp "%Y";
std:displayln :XXXX ~ (year_str | int) == 2019;
std:assert ~ (year_str | int) == 2019;
!now_str = std:chrono:timestamp[];
hash
hash:fnv1a arg1 ...
Hashes all the arguments as FNV1a and returns an integer.
rand
rand:split_mix64_new
Initializes the sm_state from the current time (seconds) and returns it.
The time is retrieved in seconds, so don't expect different seed states
if you call this multiple times in the same wall clock second.
The returned value is supposed to be passed to rand:split_mix64_next
or rand:split_mix64_next_open01
.
rand:split_mix64_new_from seed
Initializes the sm_state from the given seed and returns it.
The returned value is supposed to be passed to rand:split_mix64_next
or rand:split_mix64_next_open01
.
rand:split_mix64_next sm_state [count]
Returns the count next integer values generated from the given sm_state.
rand:split_mix64_next_open01 sm_state [count]
Returns the count next float values (in an open [0, 1) interval) generated from the given sm_state.
Functions
core_symbol_table | Returns a SymbolTable with all WLambda core language symbols. |
std_symbol_table | Returns a SymbolTable with all WLambda standard library language symbols. |