[−][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.
Table Of Contents:
- 1 - Syntax
- 2 - Variable Definition and Assignment
- 2.1 - Global Variables
- 3 - Functions (part 1/2)
- 4 - Data Types
- 4.1 - None sentinel value:
$n
or$none
- 4.2 - Error values:
$e expr
or$error expr
- 4.3 - Booleans
- 4.4 - 64-Bit Integers
- 4.5 - 64-Bit Floats
- 4.6 - Strings
- 4.7 - Bytes (or Byte Vectors)
- 4.8 - Symbols
- 4.9 - Vectors (or Lists)
- 4.10 - Associative Maps (or String to Value mappings)
- 4.10.1 - Splicing
- 4.11 - References
- 4.12 - Calling Semantics of Data Types
- 4.1 - None sentinel value:
- 5 - Functions (part 2/2)
- 6 - Conditional Execution - if / then / else
- 7 - Loops And Iteration
- 8 - Operators
- 9 - Modules
- 10 - Core Library
- 11 - Standard Library
- 11.0.1 - std:shuffle rand_func vec
- 11.0.2 - std:copy vec_or_map
- 11.0.3 - std:sort [compare_fun] vec
- 11.0.4 - std:cmp:num:asc a b
- 11.0.5 - std:cmp:num:desc a b
- 11.0.6 - std:displayln arg1 ...
- 11.0.7 - std:writeln arg1 ...
- 11.0.8 - std:str:write arg
- 11.0.9 - std:eval code-string
- 11.0.10 - std:assert bool [message]
- 11.0.11 - std:assert_eq actual expected [message]
- 11.1 - I/O
- 12 - Optional Standard Library
1 - Syntax
A more formal introduction to the syntax can be found in the parser API documentation.
2 - Variable Definition and Assignment
As this manual assumes you have some programming knowledge, we will just take a short look at the variable definition and assignment syntax:
!a = 10; # variable definition & initialization
.a = 20; # assignment of a new value to a variable
WLambda also supports destructuring assignment of vectors:
!v = $[1,2,3];
!(a, b, c) = v; # destructuring definition of variables
.(a, b, c) = v; # destructuring assignment
std:assert_eq a 1;
std:assert_eq b 2;
std:assert_eq c 3;
This also works with maps, where the key names are matched to the variable names:
!m = ${ a = 10, b = 20, c = 30 };
!(a, b, c) = m; # destructuring definition by map
.(a, b, c) = m; # destructuring assignment by map
std:assert_eq a 10;
std:assert_eq b 20;
std:assert_eq c 30;
2.1 - Global Variables
You can define global variables that are not bound to a lexical scope as follows:
{
!:global a = 13;
}[];
std:assert_eq a 13;
Global variables however do not live beyond file or module boundaries.
3 - 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.
3.1 - Closures
Functions take values from the outer scope by copying their value:
!a = 10;
!b = 20;
!add_a_and_b = { a + b }; # function copies the values 10 and 20
!result = add_a_and_b[];
std:assert_eq result 30;
This also means, that functions can not modify the values of the scope they were created in. To do that, you need a referential data type, that is described further down this document.
Here is an example how we would write the above example by mutating
the value in the result
variable:
!a = 10;
!b = 20;
!result = $& $none; # Create a weakly captured reference
# function copies the values 10 and 20
# but result is captured by reference. As the weakable reference
# type `$&` is used, it's only weakly captured.
!add_a_and_b = { .result = a + b; };
add_a_and_b[];
std:assert_eq $*result 30; # $* dereferences referential types
About the weakly capturing of result
:
It means, that if the outer reference value in result
goes
out of scope, the reference in the closure does
not keep it alive. This is important to prevent cyclic refences
where closures keep captured values unneccessarily alive.
You will also need this to make referential types such as maps ${ }
and vectors $[ ]
weakly referenced by closures for OOP.
3.1.1 - Object Oriented Programming with Closures
This is how you can use a map data type as object which stores methods:
!new_Cat = {!(name) = @;
!self = ${
name = name,
};
# Captures refer to the value in the `self` reference
# weakly now. `self` has been converted implicit to a _weakable_
# `$&` reference.
self.meow = { std:displayln self.name " meows!"; };
self.get_name = { self.name };
# Because access to _weakable_ references is always implicitly
# dereferenced we need the `$:` capture reference operator to
# prevent the reference to the map in `self` from being freed
# once the `new_Cat` function returns:
$:self
};
!my_cat = new_Cat "Spot";
my_cat.meow[]; # Prints 'Spot meows!'
std:assert_eq my_cat.get_name[] "Spot";
Alternatively you can just make the cat name private:
!new_Cat = {!(name) = @;
# Make a strong reference, so the closures DO keep cat_name alive!
# This does not make cycles, because name does not store a closure.
!cat_name = $&& name;
!meow = { std:displayln cat_name " meows!"; };
!get_name = { $*cat_name };
!set_name = { .*cat_name = _; };
# Just holds the methods
${
meow = meow,
get_name = get_name,
set_name = set_name,
};
};
!my_cat = new_Cat "Spot";
my_cat.meow[]; # Prints 'Spot meows!'
std:assert_eq my_cat.get_name[] "Spot";
my_cat.set_name "Spotty";
std:assert_eq my_cat.get_name[] "Spotty";
3.2 - Function calling
To call functions, you have at least 3 alternatives. First is the bare
_expr_ arg1 arg2 arg3 arg4
syntax. And the second is the fully delimited
variant: _expr_[arg1, arg2, arg3, ...]
. You can always delimit the first
variant using the ( ... )
parenthesis around the whole call,
i.e. (_expr_ arg1 arg2 arg3 arg4)
.
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 vector 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;
3.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, c, d) = @;
# Please note: We have to assign the
# parameters to named values 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;
3.3.1 - std:to_no_arity function
This function disables all arity checks of a function. Use this with care and diligence.
!f = { _ }; # accepts exactly 1 param
# f keeps it's arity checks, but f2 will
# call the same function, but without arity checks.
!f2 = std:to_no_arity f;
std:assert_eq (f2 1 2 3) 1;
3.4 - Calling fields / Method calling
If you use the '.' for accessing fields in a map, the object the most recent field is accessed of is passed to the called function. The object the function/method was called upon can be accessed using the special value '$self'.
!some_map = ${
some_func = { $self.a_value },
a_value = 11,
};
std:assert_eq some_map.some_func[] 11;
This in combination with the special key '_proto'
can be used to
implement a basic form of object orientation with prototype inheritance.
It can also be combined with the closure OOP approach or used for other purposes.
You can also use a vector/list as object, in that case the _proto
field that holds the class method map is the first element of the
vector. The second element of the vector can be accessed using $data
.
3.4.1 - Object Oriented Programming with Prototypes
Instead of using closures for OOP the preferred way is to use
maps of functions as classes and form an inheritance hierarchy
by using the '_proto'
key of a map:
!class_a = ${
# $self is set by any key access using the '.' calling form:
new = { ${ _proto = $self } },
generate = { "I am A" }, # A method
};
!a_instance = class_a.new[];
std:assert_eq a_instance.generate[] "I am A";
The special key '_data'
can be used (and is encouraged to be used)
as storage for data members of your objects. This is useful to separate
method name space inside objects from the data member namespace.
To quickly access the data members you can use the special value $data
,
which will evaluate to $self._data
in case $self
is a map, and
to $self.1
in case $self
is a vector.
Here is an example with a map and data:
!class_b = ${
new = {
${
_proto = $self, # $self is class_b
_data = ${
a = 10
},
}
},
gen = { _ * $data.a }, # $data is equivalent to `$self._data` here
gen2 = { _ * $self._data.a },
};
!inst = class_b.new[];
std:assert_eq inst.gen[2] 20;
std:assert_eq inst.gen2[2] 20;
You can also use vectors as objects, which can be beneficial as they are
a bit slimmer and access to _proto
and _data
are reduced to a single
vector index lookup instead of an array lookup.
!class_b = ${
new = {
$[ # return a vector
$self, # $self is class_b
${ a = 10 },
]
},
gen = { _ * $data.a }, # $data is equivalent to `$self.1` here
gen2 = { _ * $self.1.a },
};
!inst = class_b.new[];
std:assert_eq inst.gen[3] 30;
std:assert_eq inst.gen2[4] 40;
4 - Data Types
4.1 - 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.
Most functions that expect a string value will turn a $none
into an
empty string. If you need an unambigous representation use std:str:write
for dumping WLambda data structures.
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] == "";
std:assert ~ std:str:write[$n] == "$n";
std:assert ~ is_none[$n];
4.2 - 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
- std:write_str
All other functions don't accept errors as their argument.
4.2.1 - _? [label] value
Unwind the call stack from the current function to a given label if value is an error value. If no label is given only the current function is returned from with the error value. If there is no error, the given value is returned.
The best usecase is, if you just want to hand any errors that might be returned further upwards the call stack for the parent functions to handle.
!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;
A more elaborate example:
!do_fail = $false;
!maybe_fails1 = { 10 };
!maybe_fails2 = {
do_fail { $error "something is wrong" }
{ .do_fail = $true; 2 };
};
!a = {
!x = _? maybe_fails1[];
.x = x + (_? maybe_fails2[]);
x
};
!first = a[];
!second = a[];
std:assert_eq first 12;
std:assert (is_err second);
4.2.2 - on_error handler maybe-error-value
The first parameter to on_error
should be a handler function,
which will be called with four parameters.
The first of these parameters is the error text,
followed by the line number, column number and file name
from which the error originates.
The given handler is called when an error value is encountered as second argument, the maybe-error-value.
An example to demonstrate the handler arguments:
on_error {!(func, line, col, filename) = @;
# ...
} ($e "test");
A usage example:
!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!";
4.2.3 - error_to_str value
This function accepts an error value in contrast to str
, but does
not panic but transform the error value into it's string representation.
!r = error_to_str $e "TEST";
std:assert_eq r "$e[1,22:<wlambda::eval>(Err)] \"TEST\"";
WARNING: The string representation might change between wlambda versions.
Please use on_error
to access the individual parts
(line, column, filename, error value) of the error.
4.3 - 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 one or two arguments, where $true
will call the first argument, and $false
the second argument. If a second argument
isn't provided and the value is $false
, $none
is returned. 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";
4.3.1 - is_bool any-value
You can 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];
4.3.2 - bool any-value
You can cast any-value 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;
4.3.3 - not value
This function negates the boolean value. If it is not a boolean, it will be casted into one before negating.
std:assert ~ not $false;
std:assert ~ not 0;
std:assert ~ not $none;
4.3.4 - Boolean List Indexing
Booleans can also be used to pick a value from a list by calling the boolean with a list as first argument:
std:assert_eq ($true $[:a, :b]) :b;
std:assert_eq ($false $[:a, :b]) :a;
4.4 - 64-Bit Integers
4.5 - 64-Bit Floats
WLambda supports 64-Bit floating point numbers, aka f64 in Rust. Like with other numbers multiple radix literal forms are supported:
# Decimal:
std:assert_eq 10r9.92 9.92;
# Hexadecimal:
std:assert_eq 0xFF.1 255.0625;
# Binary:
std:assert_eq 0b1011.101 11.625;
# Radix 4:
std:assert_eq 4r3.3 3.75;
4.5.1 - float value
This function casts value into a float:
std:assert_eq (float 10) 10.0;
std:assert_eq (float $t) 1.0;
std:assert_eq (float $f) 0.0;
std:assert_eq (float :"32.2") 32.2;
std:assert_eq (float "5.42") 5.42;
std:assert_eq (float "5.42") 5.42;
std:assert_eq (float $b"\xFF") 255.0;
4.5.2 - is_float value
Returns $true
if value is a float, otherwise $false
is returned.
std:assert ~ is_float 4.4;
std:assert ~ is_float 1.0 + 1;
std:assert ~ not ~ is_float 1 + 1.0;
std:assert ~ not ~ is_float 4;
std:assert ~ not ~ is_float $true;
4.6 - Strings
4.7 - 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 escape sequence here!
4.7.1 - Call Properties of 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
4.7.2 - 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 inverse operation to bytes:to_hex
:
std:assert_eq (std:bytes:from_hex ~ std:bytes:to_hex $b"ABC") $b"ABC";
4.8 - Symbols
4.9 - Vectors (or Lists)
The literal syntax for vectors (or sometimes also called lists in WLambda)
is $[...]
. You may write any kind of expression in it and you will get
a vector from it.
For iteration over a vector please refer to 7.2 Collection Iteration.
To access the elements of a vector you have to call a number with a vector as first argument. The field syntax is a more convenient shorthand syntax. The following example demonstrates it:
!add20 = { _ + 20 };
!some_vec = $[1, 2 * 10, add20 10];
# Index calling:
std:assert_eq (0 some_vec) 1;
std:assert_eq (1 some_vec) 20;
std:assert_eq (2 some_vec) 30;
# Field syntax:
std:assert_eq some_vec.0 1;
std:assert_eq some_vec.1 20;
std:assert_eq some_vec.2 30;
4.9.1 - Splicing
You can splice vectors directly into their literal form with the $[..., * vec_expr, ...]
syntax. Here is an example:
!make_some = { $[_ + 1, _ + 2] };
!some_vec = $[ 0, *make_some 1 ];
std:assert_eq some_vec.1 2;
std:assert_eq some_vec.2 3;
# There can be any expression after the `.` if you wrap it into `(...)`:
std:assert_eq some_vec.(1 + 1) 3;
# A more direct example:
std:assert_eq (str $[1,2,*$[3,4]]) "$[1,2,3,4]";
4.9.2 - std:append vec-a value-or-vec ...
Appends value-or-vec and all following items to vec-a. If value-or-vec is a vector, all it's items will be appended to vec-a.
!v = std:append $[1,2,3] :a :b $[:c, :d];
std:assert_eq (str v) "$[1,2,3,:\"a\",:\"b\",:\"c\",:\"d\"]";
If vec-a is not a vector, a vector containing it will be created:
!v = std:append 1 :a :b $[:c, :d];
std:assert_eq (str v) "$[1,:\"a\",:\"b\",:\"c\",:\"d\"]";
4.10 - Associative Maps (or String to Value mappings)
Aside from vectors there are associative maps in WLambda. Their syntax is
${ key = expr, ... }
. The keys of these maps have to be strings,
the values in the literals can be any expression.
For iteration over a map please refer to 7.2 Collection Iteration.
You can call a symbol or a string with an associative map to get the value in the map with the string value as key. There is also, like vectors, the field calling syntax. Here are some examples:
!some_map = ${ a = 1, b = 2 };
# Symbol calling:
std:assert_eq (:a some_map) 1;
std:assert_eq (:b some_map) 2;
std:assert_eq ("a" some_map) 1;
std:assert_eq ("b" some_map) 2;
# Field syntax:
std:assert_eq some_map.a 1;
std:assert_eq some_map.b 2;
# There can be any expression after the `.` if you wrap it into `(...)`,
# also strings:
std:assert_eq some_map.("a") 1;
std:assert_eq some_map.("b") 2;
Keys can also be computed at runtime in the literal form:
!some_map = ${ (std:str:cat "a" "b") = 10 };
std:assert_eq (str some_map) "${ab=10}";
If you call a field that is being accessed directly using
the field accessing syntax some_map.a
, the function is passed the map some_map
via the special value $self
. There is another special variable $data
that allows you to access the $self._data
field.
4.10.1 - Splicing
Like vectors you can splice map values directly into map literals:
!map_gen = { ${ (std:str:cat "_" _) = _ } };
!some_map = ${ a = 10, *map_gen "x" };
std:assert_eq some_map.a 10;
std:assert_eq some_map._x "x";
std:assert_eq (str ${*${a=10}}) "${a=10}";
# As a reminder, a full expression can come after the '*':
std:assert_eq (str ${*map_gen "y"}) $q/${_y="y"}/;
4.11 - References
Some data structures already have reference characteristics, such as strings, vectors and maps. There are 3 types of references in WLambda that handle different usecases. These referential types are neccessary to mutate lexical variables from a parent scope. To give a rather natural example:
!x = 10;
{ .x = 20; }[];
std:assert_eq x 20;
The example works rather intuitively. There is however lots of implicit
referential stuff going on. Once x
is captured by a closure it ise implicitly
changed in to a weakable $&
reference and the closure stores only a weak
reference to x
. This is done to maintain lexical scope and prevent accidental
cyclic references when closures from a scope are leaked.
These types of references exist:
$&
- A weakable reference, that is captured weakly by closures.$(&)
- A weak reference, can't be constructed literally, only indirectly as upvalue of a closure or bystd:weaken
.$&&
- A strong reference, that is captured stongly by closures. Inside closures they are also implicitly dereferenced by assignment and access by variable name.
The weakable reference is captured weakly by closures and does not keep the referenced value alive if the value reference count drops to zero. The strong references will stay strong and need explicit care to handle in the function where they are stored directly in a local variable. But if strong references are caught, they are also implicitly handled.
!x = $& 10;
{ .x = 20; }[]; # Closures implicitly handle weak references
std:assert_eq x 20;
And the same with strong references:
!x = $&& 10;
.*x = 11;
{ .x = 20; }[]; # Closures implicitly handle strong references too
std:assert_eq $*x 20;
Strong references can also be created using the std:to_ref
function:
!x = std:to_ref 10;
std:assert_eq (std:write_str x) "$&&10";
4.11.1 - Weaken References
You can weaken any of those two types of references manually using the
std:weaken
function.
!drop_check = $& $f;
# Make a reference to the value 10 and set `drop_check` to $true
# when all (non weak) references to it are gone.
!x = $&& (std:to_drop 10 {|| .drop_check = $true });
# Create a weakened reference to the value referred to by x:
!y = std:weaken x;
# Deref y gives you 10:
std:assert_eq $*y 10;
# The reference to 10 is removed and this means that the weak reference
# in y is invalidated and returns $n in future.
.x = $n;
# Deref y now gives you $n:
std:assert_eq $*y $n;
std:assert drop_check;
4.11.2 - Strengthening References
You can convert a weak reference (weakened by std:weaken
) or a captured weak
reference $&
to strong with std:strengthen
.
TODO: Example
4.12 - Calling Semantics of Data Types
You can call almost all basic data types of WLambda. Here is an overview of the data type calling semantics:
Type | Args | Semantics |
---|---|---|
$none | - | Any call to $none will result in a panic. |
$error | - | Any call to $error will result in a panic. |
function | * | Will call the function with the specified arguments. |
$true | f1, f2 | Will call f1 . |
$false | f1, f2 | Will call f2 or return $n if f2 is not provided. |
$true | $[1,2] | Will return the second element 2 of the list. |
$false | $[1,2] | Will return the first element 1 of the list. |
symbol | map, userval | Will retrieve the value in the map at the key equal to the symbol. |
map | anything | Will call anything for each value and key in the map and return a list with the return values. |
5 - Functions (part 2/2)
5.1 - Function call composition
- chaining
- traditional () call syntax
- ~ syntax
- || syntax
$[] || push 10 $[10] $[] || push 10 || push 20 $[10,20] !x = { push _1 _ }; $n $[] | x 10 | x 20 $[10,20]
- [...] syntax
5.1.1 - '|' Tail Argument Function Chaninig
This syntax is useful if you have following function call composition:
(fn arg1 arg2 (fn2 arg_b1 arg_b2 (fn3 arg_c1 arg_c2 ...)))
These can be written more comfortably like this:
fn3 arg1 arg2 | fn2 arg_b1 arg_b2 | fn arg1 arg2
An example with actual values:
!x = 10 | { _ * 4 } | { _ + 2 };
std:assert_eq x 42;
Think of it as if the value 10
was piped through the
functions on the right.
The call reordering of the |
operator looks like this:
fn1 a1 a2 | fn2 b1 b2 ( ) => fn2 b1 b2 (fn1 a1 a2)
""""""""" ^
v |
--------------------|
5.1.2 - '|>' Left Hand Function Chaining
This syntax is useful if you want to make deep call chains like these:
(((fn arg1 arg2 ...) arg_b1 arg_b2 ...) arg_c1 arg_c2 ...)
These can be written more comfortably like this:
fn arg1 arg2 |> arg_b1 arg_b2 |> arg_c1 arg_c2
or nicer formatted:
fn arg1 arg2
|> arg_b1 arg_b2
|> arg_c1 arg_c2
Here an actual example:
!res = $@v
1 + 1
|> $["abc", "def", "ceg"]
|> { $+ ~ std:str:cat "|" _ "|" };
std:assert_eq res.0 "|c|";
std:assert_eq res.1 "|e|";
std:assert_eq res.2 "|g|";
The call reordering of the |>
operator looks like this:
fn1 a1 a2 |> b1 b2 => (( ) )
""""""""" """"" ^ ^
v v | |
-----------|--------------| |
-------------------|
5.2 - Control Flow - 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.
}
5.2.1 - return [label] value
Returns value from the current function if no label is given.
If label is given, the call stack will unwind until either a block
or a function with the given label is encountered.
!f = {
10;
return 20;
30
};
std:assert_eq f[] 20;
Here an example for unwinding two call frames:
!f = \:x {
10;
{ return :x 20 }[];
30;
};
std:assert_eq f[] 20;
The labels do not adhere to lexical scoping and are dynamically scoped:
!g = { return :x 30 };
!f = \:x { 20; g[]; 40 };
std:assert_eq f[] 30;
5.2.2 - block [label] function
Calls the function with the given label for return
to jump to.
If you just want to setup a point inside a function to jump to
with return
the block
function is more convenient to use:
!y = 1;
!res = block :x {
.y = y + 1;
(y >= 2) \return :x 20;
.y = y + 1;
.y = y + 1;
};
std:assert_eq res 20;
The alternative is the less clear syntax would be in this case:
!y = 1;
!res = \:x {
.y = y + 1;
(y >= 2) \return :x 20;
.y = y + 1;
.y = y + 1;
}[];
std:assert_eq res 20;
6 - 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"
Often, you may want to choose one variable or another based on some predicate.
For these situations, the pick
function is available.
For example, perhaps you want to make a function which can take any number of parameters,
or a single list parameter.
!sum = \|| std:fold 0 { _ + _1 } ~ pick (is_vec _) _ @;
Booleans can also be used to index into lists.
When this is done, $t
represents 1
and $f
represents 0
.
This means that we can also express our sum
function as:
!sum = \|| std:fold 0 { _ + _1 } $[@, _].(is_vec _);
Furthermore, as a.b
is equivalent to b[a]
, one can also write this sum
function
by simply invoking (is_vec _)
and passing in the list of options as a parameter.
!sum = \|| std:fold 0 { _ + _1 } ~ (is_vec _) $[@, _];
When comparing the pick
and indexing approaches it is important to note
that the two possible return values are inverted:
!x = 20;
!res = pick (x == 20) "x is 20" "x isn't 20";
std:assert_eq res "x is 20";
.res = $["x isn't 20", "x is 20"].(x == 20);
std:assert_eq res "x is 20";
With pick
, the value to return in the $t
case comes first, followed by the $f
case's value,
whereas with indexing approach, the opposite is true.
7 - Loops And Iteration
WLambda has many ways to loop and iterate:
- Counting loop with
range
- While some condition is
$true
withwhile
- Over the items in a vector with either
for
or by calling the vector with a function as first argument. - Over the items in a map with either
for
or by calling the map with a function as first argument. - Over the characters in a string with either
for
or by calling it with a function. - Over the bytes in a byte vector with either
for
or by calling it with a function.
for
just iterates through the value and provides the individual items as first argument to the
iteration function. But if you call the value with a function as first argument a mapping iteration
is done. That means, the return value of the operation is a list with the return values of the
iteration function. If you don't need that list you should use for
.
7.1 - Control Flow
7.1.1 - while predicate fun
while
will call fun until the predicate function returns $false
.
This is the most basic loop for iteration:
!i = 0;
!out = $[];
while { i < 10 } {
std:push out i;
.i = i + 1;
};
std:assert_eq (str out) "$[0,1,2,3,4,5,6,7,8,9]";
If you need an endless loop you can pass $true
as predicate:
!i = 0;
while $true {
(i >= 4) break;
.i = i + 1;
};
std:assert_eq i 4;
7.1.2 - range start end step fun
range
counts from start to end by increments of step and calls fun with the counter. The
iteration is inclusive, this means if start == end the function fun will be called once.
!out = $[];
range 0 9 1 {!(i) = @;
std:push out i;
};
std:assert_eq (str out) "$[0,1,2,3,4,5,6,7,8,9]";
The construct also works for floating point numbers, but be aware of the inherent floating point errors:
!out = $[];
range 0.3 0.4 0.01 {
std:push out ~ std:num:round 100.0 * _;
};
# 40 is not in the set because the accumulation of 0.01 results
# in a value slightly above 0.4 and ends the range iteration:
std:assert_eq (str out) "$[30,31,32,33,34,35,36,37,38,39]";
7.1.3 - break value
break
stops the inner most iterative construct, which then will return value.
This should work for all repeatedly calling operations, such as
for
, while
and when calling lists directly. Also most library functions
that iteratively call you react to it, like std:re:map
and std:re:replace_all
.
!ret = range 0 9 1 {!(i) = @;
(i > 4) { break :DONE };
};
std:assert_eq ret :DONE;
An example where the list iteration is stopped:
!val = $[1,2,3,4] { (_ > 3) { break :XX }; _ };
std:assert_eq val :XX;
7.2 - Collection Iteration
7.2.1 - Iteration over vectors
Iterating over a vector is the most basic iteration supported by WLambda. You just call the vector with a function as first argument:
!sum = 0;
$[1, 2, 3] {
.sum = sum + _;
};
std:assert_eq sum 6;
You can also use for
if you like.
7.2.2 - Iteration over maps
Iterating over a map is as simple as iterating over a vector. The map can be called with a function as first argument and it starts iterating over it's key/value pairs. The first argument of the function is the value, the second argument is the key.
!sum = 0;
!keys = $[];
${a = 10, b = 20, c = 30} {
!(v, k) = @;
.sum = sum + v;
std:push keys k;
};
std:assert_eq sum 60;
std:assert_eq (std:str:join "," ~ std:sort keys) "a,b,c";
You can also use for
if you like.
7.2.3 - for iteratable-value function
Calls function for every element of iteratable-value. Iteratable values are:
- Vectors
!product = 1;
for $[3,4,5] {
.product = product * _;
};
std:assert_eq product 60;
- Maps
!product = 1;
!keys = $[];
for ${a = 10, b = 20, c = 30} {
!(v, k) = @;
.product = product * v;
std:push keys k;
};
std:assert_eq (std:str:join "," ~ std:sort keys) "a,b,c";
std:assert_eq product 6000;
- Byte Vectors
!byte_sum = 0;
for $b"abc" {
.byte_sum = byte_sum + (int _);
};
std:assert_eq byte_sum 294;
- Strings
!str_chars = $[];
for "abc" {
std:push str_chars _;
};
std:assert_eq (str str_chars) (str $["a", "b", "c"]);
- Symbols
!str_chars = $[];
for :abc {
std:push str_chars _;
};
std:assert_eq (str str_chars) (str $["a", "b", "c"]);
7.3 - Accumulation and Collection
WLambda provides special syntax and semantics for accumulating or collecting values while iterating through lists. There are following special syntax constructs:
Syntax | Semantics |
---|---|
$@v expr | Setup collection of values in a vector, evaluates expr and returns the vector. |
$@vec expr | Same as $@v |
$@m expr | Setup collection of key/value pairs in a map, evaluates expr and returns the vector. |
$@map expr | Same as $@m |
$@s expr | Setup appending of values to a string, evaluates expr and returns the string. |
$@string expr | Same as $@s |
$@b expr | Setup collection of values in a byte vector, evaluates expr and returns byte vector. |
$@bytes expr | Same as $@b |
$@i expr | Setup accumulation in an integer, evaluates expr and returns the integer sum. |
$@int expr | Same as $@i |
$@f expr | Setup accumulation in a float, evaluates expr and returns the float sum. |
$@flt expr | Same as $@f |
$+ | Evaluated to a function that can be called to add/append a new value to the current collection/accumulation. |
$@@ | Access the current accumulation value. |
7.3.1 - Transforming a vector
If you just want to do something with items in a vector and construct a new one from the results:
!result = $@vec $[1,2,3,4] \$+ _ * 2; # multiply each item by 2
std:assert_eq (str result) "$[2,4,6,8]";
7.3.2 - Example of $@@
Here is an interesting example how $@@ might be used:
!list_of_lists = $[];
!result = $@vec $[1,2,3,4] {
$+ 2 * _; # put the value into the list
std:push list_of_lists
~ std:copy $@@; # construct a list of intermediate results
};
std:assert_eq (str result) "$[2,4,6,8]";
std:assert_eq (str list_of_lists)
"$[$[2],$[2,4],$[2,4,6],$[2,4,6,8]]";
7.3.3 - Transforming a vector to a map
For constructing maps the $@map
construct is available.
In the following example we transform a vector of pairs into a map:
!result = $@map $[ $[:a, 10], $[:b, 33], $[:c, 99] ] {
!(key, value) = _;
$+ key value;
};
std:assert_eq result.a 10;
std:assert_eq result.b 33;
std:assert_eq result.c 99;
7.3.4 - Iteratively concatenating strings
In case you need to construct a longer text the $@string
construct allows
you to efficiently create a long string. For demonstration purposes
we compare the following inefficient code with the usage of $@string
:
# Inefficient example:
!accum = "";
$["abc", "def", "ghi", "XXX"] {
.accum = accum _; # allocates a new string each iteration
};
std:assert_eq accum "abcdefghiXXX";
In theory for this constructed example the quickest way would
be to use std:str:join
:
!accum = std:str:join "" $["abc", "def", "ghi", "XXX"];
std:assert_eq accum "abcdefghiXXX";
But maybe you need to transform or construct the strings before joining:
!transform = { ">" _ };
!accum = $@string $["abc", "def", "ghi", "XXX"] {
$+[transform _] # appends the string to the accumulation string
};
std:assert_eq accum ">abc>def>ghi>XXX";
7.3.5 - Accumulating sums
The following examples show how accumulation of values with $@int
and $@float
work.
!sum = $@int $[1,2,3,4] {
$+ _
};
std:assert_eq sum 10;
And with floats:
!sum = $@float $[1.2,1.3,2.2,3.4] {
$+ _
};
std:assert_eq (std:num:round 10.0 * sum) 81.0;
7.4 - Utilities
7.4.1 - std:accum collection a b ...
This function accumulates all it's arguments in the collection.
It does the same form of accumulation as $+
does.
std:assert_eq (str ~ std:accum $[] 1 2 3) "$[1,2,3]";
std:assert_eq (std:accum "" 1 2 3) "123";
std:assert_eq (str ~ std:accum $b"" 1 2 3) "\x01\x02\x03";
std:assert_eq (str ~ std:accum 10 1 2 3) "16";
7.4.2 - std:zip vector map-fn
Creates a generator that calls map_fn with the consecutive elements of vector as the first argument of map-fn. All arguments passed to std:zip are appended to the argument list.
This is useful for combining the iteration over two vectors or collections.
!l = $@v $[13, 42, 97] ~ std:zip $["Foo", "Bar", "Baz"] { $+ @ };
std:assert_eq (str l) (str $[$["Foo", 13], $["Bar", 42], $["Baz", 97]]);
7.4.3 - std:enumerate map-fn
Creates a generator that calls map-fn with a counter that is incremented after each call, starting with 0. All received arguments are appended to the argument list after the counter.
!l = $@v $["lo", "mid", "hi"] ~ std:enumerate { $+ @ };
std:assert_eq (str l) (str $[$[0, "lo"], $[1, "mid"], $[2, "hi"]]);
8 - Operators
8.1 - Arithmetic
The output type (float vs. integer) of the numerical arithmetic operators is defined
by the first operand of the operation. Use the casting functions float
or
int
if you are unsure.
Please note that not all operators are available as plain identifiers and need
to be quoted when used in their prefix form or as functions, some of them are
*
, /
, %
and some others.
8.1.1 - + operand-1 operand-2 ...
This function implements arithmetic addition. If the first operand is a float number, the substraction will return a float result. If it is an integer or anything else (like a string), an integer result is returned.
std:assert_eq (+ 5.5 0.5) 6.0;
std:assert_eq (5.5 + 0.5) 6.0;
std:assert_eq (+ 5 2) 7;
std:assert_eq (+ "5" 2) 7;
std:assert_eq (+ :5 2) 7;
8.1.2 - - operand-1 operand-2 ...
This function implements arithmetic substraction. If the first operand is a float number, the substraction will return a float result. If it is an integer or anything else (like a string), an integer result is returned.
std:assert_eq (- 5.5 0.5) 5.0;
std:assert_eq (5.5 - 0.5) 5.0;
std:assert_eq (- 5 2) 3;
std:assert_eq (- "5" 2) 3;
std:assert_eq (- :5 2) 3;
8.1.3 - * op-a op-b
Returns the multiplication of the two operands.
std:assert 10 * 4 == 40;
std:assert 10.1 * 4 == 40.4;
std:assert "10" * 4 == 40;
std:assert (`*` 10 4) == 40;
std:assert (float "10.1") * 4 == 40.4;
8.1.4 - / op-a op-b
Returns the division of the two operands.
std:assert 10 / 4 == 2;
std:assert 10.0 / 4 == 2.5;
std:assert "10" / 2 == 5;
std:assert (`/` 10 4) == 2;
std:assert (float "10.1") * 4 == 40.4;
8.1.5 - % op-a op-b
Returns the remainder of the division of op-a by op-b.
std:assert 5 % 4 == 1;
std:assert (`%` 5 4) == 1;
8.1.6 - ^ op-a op-b
Returns op-a raised by the power of op-b. Supports float and integers.
std:assert_eq 2 ^ 4 16;
std:assert_eq std:num:round[(2.0 ^ 2.1) * 1000] 4287.0;
std:assert_eq 2 ^ 2.1 4; # first arg type matters!
8.2 - Comparison
8.2.1 - == op-a op-b
Checks whether the two operands are equal to each other. Data types like booleans, integers, floats, symbols and strings are compared by their contents. Other types like vectors, maps, functions, errors or references are compared by referential equality.
std:assert $none == $none;
std:assert 1 == 2 - 1;
std:assert "aa" == ("a" "a");
std:assert :xxy == :xxy;
std:assert not ~ $[1,2] == $[1,2];
std:assert ~ `==` 1 (2 - 1); # prefix form
8.2.2 - != op-a op-b
Checks whether the two operands are distinct from each other. Data types like booleans, integers, floats, symbols and strings are compared by their contents. Other types like vectors, maps, functions, errors or references are compared by referential equality.
It's generally the opposite of ==
.
std:assert 1 != 2;
std:assert not[2 != 2];
std:assert "foo" != "bar";
std:assert not["foo" != "foo"];
std:assert ~ `!=` 1 2;
!r1 = $[1,2];
!r2 = $[1,2];
std:assert r1 != r2;
8.2.3 - < op-a op-b
Numerical comparison operator that checks whether op-a is less than op-b
std:assert 10 < 11;
std:assert 10.1 < 10.2;
std:assert not[10 < 10.1]; # the type of the first argument decides return type!
8.2.4 - <= op-a op-b
Numerical comparison operator that checks whether op-a is less or equal to op-b
std:assert 10 <= 11;
std:assert 10.1 <= 10.2;
std:assert 10 <= 10.1; # integer <=, the type of the first argument decides return type!
8.2.5 - > op-a op-b
Numerical comparison operator that checks whether op-a is greater than op-b
std:assert 11.1 > 11;
std:assert 11.1 > 11.0;
std:assert not[10 > 10.1]; # the type of the first argument decides return type!
8.2.6 - >= op-a op-b
Numerical comparison operator that checks whether op-a is greater or equal to op-b
std:assert 11 >= 11;
std:assert 10.2 >= 10.1;
std:assert 10 >= 10.1; # integer >=, the type of the first argument decides return type!
8.3 - Bit Operations
8.3.1 - & op-a op-b
Binary and
operation between two integers.
std:assert (0b0011 & 0b1011) == 0b011;
std:assert (3 & 11) == 3;
8.3.2 - &^ op-a op-b
Binary xor
operation between two integers.
std:assert (0b0011 &^ 0b1011) == 0b1000;
std:assert (3 &^ 11) == 8;
8.3.3 - &| op-a op-b
Binary or
operation between two integers.
std:assert (0b0011 &| 0b1000) == 0b1011;
std:assert (3 &| 8) == 11;
8.3.4 - << op-a op-b
Binary left shift
operation of op-a by op-b bits.
std:assert (0b0011 << 3) == 0b11000;
std:assert (`<<` 0b1011 2) == 0b101100
8.3.5 - >> op-a op-b
Binary right shift
operation of op-a by op-b bits.
std:assert (0b0011 >> 2) == 0b0;
std:assert (0b1100 >> 2) == 0b11;
std:assert (`>>` 0b1011000 3) == 0b1011
9 - Modules
9.1 - export
!expr = { _ + 30 };
!@export symbol = expr; # exports symbol with value of expr (a function)
9.2 - 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]";
10 - Core Library
This library contains all the core functions which belong to the core of the WLambda Programming Language. These functions can be seen as keywords of WLambda. Some functions are also available as operators.
11 - Standard Library
11.0.1 - std:shuffle rand_func vec
Shuffles the vec in place. The function rand_func needs to return a random 64 bit integer on each call. Here is an example:
!sm = std:rand:split_mix64_new_from 1234;
!vec = $[1,2,3,4,5,6,7,8];
std:shuffle { std:rand:split_mix64_next sm } vec;
std:assert_eq (str vec) "$[2,1,7,4,8,5,3,6]";
11.0.2 - std:copy vec_or_map
Makes a shallow copy of the given vector or map.
!a = $[1,2,3];
!b = std:copy a;
b.0 = 10;
std:assert_eq a.0 1;
std:assert_eq b.0 10;
11.0.3 - std:sort [compare_fun] vec
Sorts the given vec in place. The comparison function compare_fun gets the two values a and b and needs to return -1 if a < b, 0 if a = b and 1 if a > b.
There are four functions that implement numeric and lexicographic ordering:
std:cmp:num:asc
std:cmp:num:desc
std:cmp:str:asc
std:cmp:str:desc
If no compare_fun is given, the ordering will be ascending and lexicographic
vs. numeric will be chosen by the type of the a
value (if it is an integer or
float it will be numeric, otherwise lexicographic).
!v = $[$[1], $[-1], $[3]];
std:sort { std:cmp:num:desc _.0 _1.0 } v;
std:assert_eq v.0.0 3;
std:assert_eq v.1.0 1;
std:assert_eq v.2.0 -1;
11.0.4 - std:cmp:num:asc a b
Compares a and b numerically and returns:
Cases | Return Value |
---|---|
a > b | -1 |
a == b | 0 |
a < b | 1 |
std:assert_eq (std:cmp:num:asc 20 2) -1;
std:assert_eq (std:cmp:num:asc "20" "20") 0;
std:assert_eq (std:cmp:num:asc 20 21) 1;
11.0.5 - std:cmp:num:desc a b
Compares a and b numerically descending and returns:
Cases | Return Value |
---|---|
a > b | 1 |
a == b | 0 |
a < b | -1 |
std:assert_eq (std:cmp:num:desc "20" "2") 1;
std:assert_eq (std:cmp:num:desc "20" "20") 0;
std:assert_eq (std:cmp:num:desc 20 21) -1;
11.0.6 - std:displayln arg1 ...
This function writes a humand readable version of all the arguments (with a space inbetween) to the standard output. This means that:
std:displayln "foo"
Will just print foo
and a newline.
If you need a less ambigous form, use std:writeln
, which
handles it's argument like written via std:str:write
instead of str
.
11.0.7 - std:writeln arg1 ...
This function writes the WLambda representation of it's arguments (with a space inbetween) to standard output. This means that:
std:displayln "foo"
Will print "foo"
and a newline.
See also the description of std:str:write
.
If you need a more human readable form use std:displayln
.
11.0.8 - std:str:write arg
Returns the WLambda representation of the value arg as string.
Most values have the same represenation like a WLambda literal, but there are other values that don't have a literal representation.
Warning: Consider all values that don't have a fixed literal representation in the WLambda syntax as debug output that might change in future versions.
std:assert_eq (std:str:write "foo") $q|"foo"|;
std:assert_eq (std:str:write $none) $q|$n|;
std:assert_eq (std:str:write $[1,:a]) $q|$[1,:"a"]|;
11.0.9 - 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;
11.0.10 - 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
11.0.11 - 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";
11.1 - I/O
11.1.1 - 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";
11.1.2 - 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";
11.1.3 - 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.
11.1.4 - 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.
12 - Optional Standard Library
12.1 - serialization
12.1.1 - std: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}]";
12.1.2 - std: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;
12.1.3 - std:ser:csv field_delim row_separator escape_all table
This serializes the table as CSV with the given field_delim
and row_separator. If escape_all is $true
all fields will be
put into '"'.
!csv_str =
std:ser:csv
";" "|" $f
$[ $[1,2,3,4,$q/foo"bar/],
$[44,55],
$[]]
| std:displayln;
std:assert_eq csv_str $q/1;2;3;4;"foo""bar"|44;55||/;
std:assert_eq
(std:ser:csv ";" "|" $f $[$[:a,$q/;/, $q/|/, $q/ /]])
"a;\";\";\"|\";\" \"|";
12.1.4 - std:deser:csv field_delim row_separator data
Parses the string data as CSV. With the field delimiter field_delim and the row_separator for the data rows.
!table = std:deser:csv ";" "\r\n" "foo;bar\r\nx;y\r\n";
std:assert_eq table.0.0 "foo";
std:assert_eq table.0.1 "bar";
std:assert_eq table.1.1 "y";
12.1.5 - std:ser:msgpack data
Serializes the data and returns a msgpack bytes value.
std:assert_eq (std:ser:msgpack $b"abc") $b"\xC4\x03abc";
12.1.6 - std:deser:msgpack bytes
Deserializes the msgpack bytes value into a data structure.
std:assert_eq (std:deser:msgpack $b"\xC4\x03abc") $b"abc";
12.2 - regex
12.3 - chrono
12.3.1 - std: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) == 2020;
std:assert ~ (year_str | int) == 2020;
!now_str = std:chrono:timestamp[];
12.4 - hash
12.4.1 - std:hash:fnv1a arg1 ...
Hashes all the arguments as FNV1a and returns an integer.
12.5 - rand
12.5.1 - std: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
.
12.5.2 - std: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
.
12.5.3 - std:rand:split_mix64_next sm_state [count]
Returns the count next integer values generated from the given sm_state.
12.5.4 - std: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. |