# WLambda Language 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](#1-variable-definition-and-assignment) Variable Definition and Assignment - [1.1](#11-destructuring-to-variables) Destructuring to Variables - [1.2](#12-global-variables) Global Variables - [1.3](#13-constants) Constants - [2](#2-functions-part-12) Functions (part 1/2) - [2.1](#21-closures) Closures - [2.1.1](#211-object-oriented-programming-with-closures) Object Oriented Programming with Closures - [2.2](#22-function-calling) Function calling - [2.3](#23-function-arity-checks) Function arity checks - [2.3.1](#231-stdtonoarity-function) std:to\_no\_arity _function_ - [2.4](#24-calling-fields--method-calling) Calling fields / Method calling - [2.4.1](#241-the-self-and-data-special-variables) The $self and $data special variables - [2.4.2](#242-object-oriented-programming-with-prototypes) Object Oriented Programming with Prototypes - [2.4.3](#243-object-oriented-with-prototypes-and-inheritance) Object Oriented with Prototypes and Inheritance - [2.4.4](#244-object-oriented-with-prototypes-and-self-references-and-data-references) Object Oriented with Prototypes and $self References and $data References - [2.5](#25-function-call-composition) Function call composition - [2.5.1](#251--tail-argument-function-chaninig) '|' Tail Argument Function Chaninig - [2.5.2](#252--left-hand-function-chaining) '|>' Left Hand Function Chaining - [2.5.3](#253-forward-argument-pipe-arg--fun) Forward Argument Pipe `arg &> fun` - [2.5.4](#254-forward-argument-apply-pipe-list--fun) Forward Argument Apply Pipe `list &@> fun` - [2.5.5](#255-reverse-argument-pipe-fun--arg) Reverse Argument Pipe `fun <& arg` - [2.5.6](#256-reverse-argument-apply-pipe-list--fun) Reverse Argument Apply Pipe `list &@> fun` - [2.6](#26-control-flow---returning) Control Flow - Returning - [2.6.1](#261-return-label-value) return [_label_] _value_ - [2.6.2](#262-block-label-function) block [label] _function_ - [2.6.3](#263-stdtodrop-function-or-raii-destructors-or-drop-functions) std:to\_drop _function_ (or RAII, Destructors or Drop Functions) - [2.6.4](#264-stdtimenow-unit) std:time:now [_unit_] - [2.6.5](#265-stdsrand-seed) std:srand [_seed_] - [2.6.6](#266-stdrand-max-or-mode) std:rand [_max-or-mode_] - [2.7](#27-function-utilities) Function utilities - [2.7.1](#271-isfun-value) is\_fun _value_ - [3](#3-data-types) Data Types - [3.1](#31-none-sentinel-value-n-or-none) None sentinel value: `$n` or `$none` - [3.1.1](#311-isnone-value) is\_none _value_ - [3.1.2](#312-issome-value) is\_some _value_ - [3.2](#32-optional-values-o-and-o) Optional values `$o()` and `$o(...)` - [3.2.1](#321-isoptional-value) is\_optional _value_ - [3.2.2](#322-unwrapping-optionals) Unwrapping optionals - [3.3](#33-error-values-e-expr-or-error-expr) Error values: `$e expr` or `$error expr` - [3.3.1](#331--label-value) _? [_label_] _value_ - [3.3.2](#332-unwrap-value) unwrap _value_ - [3.3.3](#333-unwraperr-error-value) unwrap\_err _error-value_ - [3.3.4](#334-onerror-handler-maybe-error-value) on\_error _handler_ _maybe-error-value_ - [3.3.5](#335-iserr-value) is\_err _value_ - [3.3.6](#336-stderrortostr-value) std:error\_to\_str _value_ - [3.4](#34-booleans) Booleans - [3.4.1](#341-isbool-any-value) is\_bool _any-value_ - [3.4.2](#342-bool-any-value) bool _any-value_ - [3.4.3](#343-not-value) not _value_ - [3.4.4](#344-boolean-list-indexing) Boolean List Indexing - [3.5](#35-64-bit-integers) 64-Bit Integers - [3.5.1](#351-int-value) int _value_ - [3.5.2](#352-isint-value) is\_int _value_ - [3.5.3](#353-stdnegi64-integer) std:neg\_i64 _integer_ - [3.5.4](#354-stdnoti64-integer) std:not\_i64 _integer_ - [3.5.5](#355-stdnegu32-integer) std:neg\_u32 _integer_ - [3.5.6](#356-stdnotu32-integer) std:not\_u32 _integer_ - [3.6](#36-64-bit-floats) 64-Bit Floats - [3.6.1](#361-float-value) float _value_ - [3.6.2](#362-isfloat-value) is\_float _value_ - [3.6.3](#363-stdnumacos-float) std:num:acos _float_ - [3.6.4](#364-stdnumacosh-float) std:num:acosh _float_ - [3.6.5](#365-stdnumasin-float) std:num:asin _float_ - [3.6.6](#366-stdnumasinh-float) std:num:asinh _float_ - [3.6.7](#367-stdnumatan-float) std:num:atan _float_ - [3.6.8](#368-stdnumatan2-y-x) std:num:atan2 _y_ _x_ - [3.6.9](#369-stdnumatanh-float) std:num:atanh _float_ - [3.6.10](#3610-stdnumcbrt-float) std:num:cbrt _float_ - [3.6.11](#3611-stdnumceil-float) std:num:ceil _float_ - [3.6.12](#3612-stdnumcos-float) std:num:cos _float_ - [3.6.13](#3613-stdnumcosh-float) std:num:cosh _float_ - [3.6.14](#3614-stdnumexp-float) std:num:exp _float_ - [3.6.15](#3615-stdnumexp2-float) std:num:exp2 _float_ - [3.6.16](#3616-stdnumexpm1-float) std:num:exp\_m1 _float_ - [3.6.17](#3617-stdnumfloor-float) std:num:floor _float_ - [3.6.18](#3618-stdnumhypot-y-x) std:num:hypot _y_ _x_ - [3.6.19](#3619-stdnumln-float) std:num:ln _float_ - [3.6.20](#3620-stdnumlog-float) std:num:log _float_ - [3.6.21](#3621-stdnumlog10-float) std:num:log10 _float_ - [3.6.22](#3622-stdnumlog2-float) std:num:log2 _float_ - [3.6.23](#3623-stdnumpow-float) std:num:pow _float_ - [3.6.24](#3624-stdnumrecip-float) std:num:recip _float_ - [3.6.25](#3625-stdnumround-float) std:num:round _float_ - [3.6.26](#3626-stdnumsin-float) std:num:sin _float_ - [3.6.27](#3627-stdnumsinh-float) std:num:sinh _float_ - [3.6.28](#3628-stdnumsqrt-float) std:num:sqrt _float_ - [3.6.29](#3629-stdnumtan-float) std:num:tan _float_ - [3.6.30](#3630-stdnumtanh-float) std:num:tanh _float_ - [3.6.31](#3631-stdnumtodegrees-float) std:num:to\_degrees _float_ - [3.6.32](#3632-stdnumtoradians-float) std:num:to\_radians _float_ - [3.6.33](#3633-stdnumtrunc-float) std:num:trunc _float_ - [3.6.34](#3634-stdnumlerp-a-b-x) std:num:lerp _a_ _b_ _x_ - [3.6.35](#3635-stdnumsmoothstep-a-b-x) std:num:smoothstep _a_ _b_ _x_ - [3.6.36](#3636-stdnumfract-float) std:num:fract _float_ - [3.7](#37-numeric-functions) Numeric Functions - [3.7.1](#371-stdnumabs-number) std:num:abs _number_ - [3.7.2](#372-stdnumsignum-number) std:num:signum _number_ - [3.7.3](#373-stdnuminttoclosedopen01-integer) std:num:int\_to\_closed\_open01 _integer_ - [3.7.4](#374-stdnuminttoopen01-integer) std:num:int\_to\_open01 _integer_ - [3.7.5](#375-stdnuminttoopenclosed01-integer) std:num:int\_to\_open\_closed01 _integer_ - [3.8](#38-numerical-mathematical-vectors) Numerical Mathematical Vectors - [3.8.1](#381-vector-conversions) Vector Conversions - [3.8.2](#382-vector-component-access) Vector Component Access - [3.8.3](#383-named-field-access-and-swizzling) Named Field Access and Swizzling - [3.8.4](#384-euler-additionsubtraction) Euler Addition/Subtraction - [3.8.5](#385-scalar-multiplicationdivision) Scalar Multiplication/Division - [3.8.6](#386-unary-vector-operations) Unary Vector Operations - [3.8.7](#387-isfvec-value) is\_fvec _value_ - [3.8.8](#388-isivec-value) is\_ivec _value_ - [3.8.9](#389-isnvec-value) is\_nvec _value_ - [3.8.10](#3810-nveclen-value) nvec\_len _value_ - [3.8.11](#3811-fvec-value) fvec _value_ - [3.8.12](#3812-fvec2-value) fvec2 _value_ - [3.8.13](#3813-fvec3-value) fvec3 _value_ - [3.8.14](#3814-fvec4-value) fvec4 _value_ - [3.8.15](#3815-ivec-value) ivec _value_ - [3.8.16](#3816-ivec2-value) ivec2 _value_ - [3.8.17](#3817-ivec3-value) ivec3 _value_ - [3.8.18](#3818-ivec4-value) ivec4 _value_ - [3.8.19](#3819-stdvdims-vec) std:v:dims _vec_ - [3.8.20](#3820-stdvmag2-vec) std:v:mag2 _vec_ - [3.8.21](#3821-stdvmag-vec) std:v:mag _vec_ - [3.8.22](#3822-stdvnorm-vec) std:v:norm _vec_ - [3.8.23](#3823-stdvdot-vec1-vec2) std:v:dot _vec1_ _vec2_ - [3.8.24](#3824-stdvcross-vec1-vec2) std:v:cross _vec1_ _vec2_ - [3.8.25](#3825-stdvlerp-vec1-vec2-t) std:v:lerp _vec1_ _vec2_ _t_ - [3.8.26](#3826-stdvslerp-vec1-vec2-t) std:v:slerp _vec1_ _vec2_ _t_ - [3.8.27](#3827-stdvvec2rad-vec) std:v:vec2rad _vec_ - [3.8.28](#3828-stdvrad2vec-radians) std:v:rad2vec _radians_ - [3.9](#39-characters-and-bytes) Characters and Bytes - [3.9.1](#391-byte-value) byte _value_ - [3.9.2](#392-char-value) char _value_ - [3.9.3](#393-isbyte-value) is\_byte _value_ - [3.9.4](#394-ischar-value) is\_char _value_ - [3.9.5](#395-stdchartolowercase-value) std:char:to\_lowercase _value_ - [3.9.6](#396-stdchartouppercase-value) std:char:to\_uppercase _value_ - [3.10](#310-strings) Strings - [3.10.1](#3101-string-literal-syntaxes) String Literal Syntaxes - [3.10.2](#3102-str-value) str _value_ - [3.10.3](#3103-stdwritestr-value) std:write\_str _value_ - [3.10.4](#3104-isstr-value) is\_str _value_ - [3.10.5](#3105-stdstrcat-a-b-) std:str:cat _a_ _b_ ... - [3.10.6](#3106-stdstrjoin-sep-vector) std:str:join _sep_ _vector_ - [3.10.7](#3107-stdstrlen-value) std:str:len _value_ - [3.10.8](#3108-stdstrfind-pattern-string-offset) std:str:find _pattern_ _string_ [_offset_] - [3.10.9](#3109-stdstrreplace-pattern-replacement-string) std:str:replace _pattern_ _replacement_ _string_ - [3.10.10](#31010-stdstrreplacen-pattern-replacement-count-string) std:str:replace\_n _pattern_ _replacement_ _count_ _string_ - [3.10.11](#31011-stdstrtrim-value) std:str:trim _value_ - [3.10.12](#31012-stdstrtrimstart-value) std:str:trim\_start _value_ - [3.10.13](#31013-stdstrtrimend-value) std:str:trim\_end _value_ - [3.10.14](#31014-stdstrpadstart-len-pad-str-value) std:str:pad\_start _len_ _pad-str_ _value_ - [3.10.15](#31015-stdstrpadend-len-pad-str-value) std:str:pad\_end _len_ _pad-str_ _value_ - [3.10.16](#31016-stdstrtobytes-string) std:str:to\_bytes _string_ - [3.10.17](#31017-stdstrtobyteslatin1-string) std:str:to\_bytes\_latin1 _string_ - [3.10.18](#31018-stdstrfromlatin1-byte-vector) std:str:from\_latin1 _byte-vector_ - [3.10.19](#31019-stdstrfromutf8-byte-vector) std:str:from\_utf8 _byte-vector_ - [3.10.20](#31020-stdstrfromutf8lossy-byte-vector) std:str:from\_utf8\_lossy _byte-vector_ - [3.10.21](#31021-stdstrtocharvec-string) std:str:to\_char\_vec _string_ - [3.10.22](#31022-stdstrfromcharvec-vector) std:str:from\_char\_vec _vector_ - [3.10.23](#31023-stdstrtolowercase-string) std:str:to\_lowercase _string_ - [3.10.24](#31024-stdstrtouppercase-string) std:str:to\_uppercase _string_ - [3.10.25](#31025-stdstreditdistance-str-a-strb) std:str:edit\_distance _str-a_ _str\_b - [3.11](#311-byte-vectors) Byte Vectors - [3.11.1](#3111-call-properties-of-bytes) Call Properties of Bytes - [3.11.2](#3112-byte-conversion-functions) Byte Conversion Functions - [3.11.3](#3113-isbytes-value) is\_bytes _value_ - [3.11.4](#3114-stdbytesfind-pattern-string-offset) std:bytes:find _pattern_ _string_ [_offset_] - [3.11.5](#3115-stdbytesreplace-byte-vector-pattern-replacement) std:bytes:replace _byte-vector_ _pattern_ _replacement_ - [3.11.6](#3116-stdbytesfromhex-string-with-hex-chars) std:bytes:from\_hex _string-with-hex-chars_ - [3.11.7](#3117-stdbytesfromvec-vector-of-ints) std:bytes:from\_vec _vector-of-ints_ - [3.11.8](#3118-stdbytestohex-byte-vector) std:bytes:to\_hex _byte-vector_ - [3.11.9](#3119-stdbytestobase64-byte-vector-config) std:bytes:to\_base64 _byte-vector_ [_config_] - [3.11.10](#31110-stdbytesfrombase64-byte-vector-config) std:bytes:from\_base64 _byte-vector_ [_config_] - [3.11.11](#31111-stdbytestovec-byte-vector) std:bytes:to\_vec _byte-vector_ - [3.11.12](#31112-stdbytespack-pack-format-string-list-of-values) std:bytes:pack _pack-format-string_ _list-of-values_ - [3.11.13](#31113-stdbytesunpack-pack-format-string-byte-vector) std:bytes:unpack _pack-format-string_ _byte-vector_ - [3.12](#312-symbols) Symbols - [3.12.1](#3121-sym-value) sym _value_ - [3.12.2](#3122-issym-value) is\_sym _value_ - [3.12.3](#3123-stdsymbolscollect) std:symbols:collect - [3.13](#313-syntax-block) Syntax `$%:Block` - [3.13.1](#3131-stdsynpos-syntax) std:syn:pos _syntax_ - [3.13.2](#3132-stdsyntype-syntax) std:syn:type _syntax_ - [3.14](#314-pairs-pa-b) Pairs `$p(a, b)` - [3.14.1](#3141-pair-operator-a--b) Pair Operator `a => b` - [3.14.2](#3142-cons-a-b) cons _a_ _b_ - [3.14.3](#3143-pair-stringbyte-vector-operations) Pair string/byte vector operations - [3.14.3.1](#31431-p-from--count--string-or-byte-vec) $p( _from_ , _count_ ) _string-or-byte-vec_ - [3.14.3.2](#31432-p-pattern--replacement--string-or-byte-vec) $p( _pattern_ , _replacement_ ) _string-or-byte-vec_ - [3.14.3.3](#31433-p-split-pattern--max--string-or-byte-vec) $p( _split-pattern_ , _max_ ) _string-or-byte-vec_ - [3.14.4](#3144-pair-to-iterator) Pair to Iterator - [3.14.4.1](#31441-iter---range) Iter - Range - [3.14.4.2](#31442-iter---enumerate) Iter - Enumerate - [3.14.4.3](#31443-iter---values) Iter - Values - [3.14.4.4](#31444-iter---keys) Iter - Keys - [3.14.5](#3145-ispair-value) is\_pair _value_ - [3.15](#315-vectors-or-lists) Vectors (or Lists) - [3.15.1](#3151-stdpush-vector-item) std:push _vector_ _item_ - [3.15.2](#3152-stdpop-vector) std:pop _vector_ - [3.15.3](#3153-stdunshift-vector-item) std:unshift _vector_ _item_ - [3.15.4](#3154-isvec-value) is\_vec _value_ - [3.15.5](#3155-vector-splicing) Vector Splicing - [3.15.6](#3156-stdappend-vec-a-value-or-vec-) std:append _vec-a_ _value-or-vec_ ... - [3.15.7](#3157-stdprepend-vec-a-value-or-vec-) std:prepend _vec-a_ _value-or-vec_ ... - [3.15.8](#3158-stdtake-count-vector) std:take _count_ _vector_ - [3.15.9](#3159-stddrop-count-vector) std:drop _count_ _vector_ - [3.16](#316-associative-maps-or-string-to-value-mappings) Associative Maps (or String to Value mappings) - [3.16.1](#3161-map-splicing) Map Splicing - [3.16.2](#3162-ismap-value) is\_map _value_ - [3.17](#317-references) References - [3.17.1](#3171-stdtoref-value) std:to\_ref _value_ - [3.17.2](#3172-stdrefweaken-ref) std:ref:weaken _ref_ - [3.17.3](#3173-stdrefhide-value) std:ref:hide _value_ - [3.17.4](#3174-isref-value) is\_ref _value_ - [3.17.5](#3175-iswref-value) is\_wref _value_ - [3.17.6](#3176-stdrefstrengthen-ref) std:ref:strengthen _ref_ - [3.17.7](#3177-stdrefset-ref-value) std:ref:set _ref_ _value_ - [3.18](#318-iterators-iter-expression) Iterators $iter _expression_ - [3.18.1](#3181-iterator-kinds) Iterator Kinds - [3.18.2](#3182-iterators-on-mutated-data) Iterators on mutated data - [3.18.3](#3183-splicing-an-iterator) Splicing an Iterator - [3.18.4](#3184-calling-an-iterator-with-a-function) Calling an Iterator with a Function - [3.18.5](#3185-zip-iterators) Zip Iterators - [3.18.6](#3186-isiter-value) is\_iter _value_ - [3.19](#319-calling-semantics-of-data-types) Calling Semantics of Data Types - [4](#4-conditional-execution---if--then--else) Conditional Execution - if / then / else - [4.1](#41-if-condition-then-expr-else-expr) if/? _condition_ _then-expr_ [_else-expr_] - [4.2](#42-using-booleans-for-conditional-execution) Using Booleans for Conditional Execution - [4.2.1](#421-pick-bool-a--b-) pick _bool_ _a_ -b- - [4.2.2](#422-indexing-by-booleans) Indexing by Booleans - [4.3](#43-value-matching-with---match-value-expr-) Value matching with - match _value-expr_ ... - [5](#5-loops-and-iteration) Loops And Iteration - [5.1](#51-control-flow) Control Flow - [5.1.1](#511-while-predicate-body) while _predicate_ _body_ - [5.1.2](#512-iter-var-iterable-body) iter _var_ _iterable_ _body_ - [5.1.2.1](#5121-counting-loop-with-iter) Counting loop with _iter_ - [5.1.2.2](#5122-vector-iteration-with-iter) Vector iteration with _iter_ - [5.1.2.3](#5123-map-iteration-with-iter) Map iteration with _iter_ - [5.1.2.4](#5124-closures-and-iter-iter-i-) Closures and _iter_ `iter i ...` - [5.1.3](#513-range-start-end-step-fun) range _start_ _end_ _step_ _fun_ - [5.1.4](#514-break-value) break _value_ - [5.1.5](#515-next) next - [5.1.6](#516-jump-index-val-branch1--last-branch) jump _index-val_ _branch1_ ... _last-branch_ - [5.2](#52-collection-iteration) Collection Iteration - [5.2.1](#521-iteration-over-vectors) Iteration over vectors - [5.2.2](#522-iteration-over-maps) Iteration over maps - [5.2.3](#523-for-iteratable-value-function) for _iteratable-value_ _function_ - [5.2.4](#524-map-function-iterable) map _function_ _iterable_ - [5.2.5](#525-filter-function-iterable) filter _function_ _iterable_ - [5.3](#53-accumulation-and-collection) Accumulation and Collection - [5.3.1](#531-transforming-a-vector) Transforming a vector - [5.3.2](#532-example-of-) Example of `$@@` - [5.3.3](#533-transforming-a-vector-to-a-map) Transforming a vector to a map - [5.3.4](#534-iteratively-concatenating-strings) Iteratively concatenating strings - [5.3.5](#535-accumulating-sums) Accumulating sums - [5.4](#54-utilities) Utilities - [5.4.1](#541-stdaccum-collection-a-b-) std:accum _collection_ _a_ _b_ ... - [5.4.2](#542-stdzip-vector-map-fn) std:zip _vector_ _map-fn_ - [5.4.3](#543-stdfold-accumulator-func-iteratable) std:fold _accumulator_ _func_ _iteratable_ - [5.4.4](#544-stdenumerate-map-fn) std:enumerate _map-fn_ - [6](#6-operators) Operators - [6.1](#61-operator-assignment) Operator Assignment - [6.2](#62-arithmetic) Arithmetic - [6.2.1](#621--operand-1-operand-2-) + _operand-1_ _operand-2_ ... - [6.2.2](#622---operand-1-operand-2-) - _operand-1_ _operand-2_ ... - [6.2.3](#623--op-a-op-b) * _op-a_ _op-b_ - [6.2.4](#624--op-a-op-b) / _op-a_ _op-b_ - [6.2.5](#625--op-a-op-b) % _op-a_ _op-b_ - [6.2.6](#626--op-a-op-b) ^ _op-a_ _op-b_ - [6.3](#63-comparison) Comparison - [6.3.1](#631--op-a-op-b) == _op-a_ _op-b_ - [6.3.2](#632--op-a-op-b) != _op-a_ _op-b_ - [6.3.3](#633--op-a-op-b) < _op-a_ _op-b_ - [6.3.4](#634--op-a-op-b) <= _op-a_ _op-b_ - [6.3.5](#635--op-a-op-b) > _op-a_ _op-b_ - [6.3.6](#636--op-a-op-b) >= _op-a_ _op-b_ - [6.4](#64-bit-operations) Bit Operations - [6.4.1](#641--op-a-op-b) & _op-a_ _op-b_ - [6.4.2](#642--op-a-op-b) &^ _op-a_ _op-b_ - [6.4.3](#643--op-a-op-b) &| _op-a_ _op-b_ - [6.4.4](#644--op-a-op-b) << _op-a_ _op-b_ - [6.4.5](#645--op-a-op-b) >> _op-a_ _op-b_ - [6.5](#65-collection-addition-operators--and-) Collection Addition Operators +> and <+ - [6.5.1](#651--collection-a-) +> _collection_ _a_ ... - [6.5.2](#652--collection-a-) <+ _collection_ _a_ ... - [7](#7-string-and-byte-vector-formatting) String and Byte Vector Formatting - [7.0.1](#701-stdformatter-format-string) std:formatter _format-string_ - [7.1](#71-formatting-numbers) Formatting Numbers - [8](#8-data-structure-matchers-selectors-and-string-patternsregex) Data Structure Matchers, Selectors and String Patterns/Regex - [8.1](#81-data-structure-matcher) Data Structure Matcher - [8.1.1](#811-match-value-expr-match-pair1--default-expr) match _value-expr_ _match-pair1_ ... [_default-expr_] - [8.1.2](#812-m-expr) $M _expr_ - [8.1.3](#813-data-structure-matcher-syntax) Data Structure Matcher Syntax - [8.2](#82-data-structure-selectors-s) Data Structure Selectors `$S(...)` - [8.2.1](#821-selector-and-wlambda-regex-syntax) Selector and WLambda Regex Syntax: - [8.2.2](#822-stdselector-string) std:selector _string_ - [8.3](#83-string-patterns-regex-r) String Patterns (Regex) `$r/.../` - [8.3.1](#831-global-patterns-rg) Global Patterns `$rg/.../` - [8.3.2](#832-pattern-substitutions-rs) Pattern Substitutions `$rs/.../` - [8.3.3](#833-pattern-syntax-overview) Pattern Syntax Overview - [8.3.4](#834-standard-regular-expressions) Standard Regular Expressions - [8.3.5](#835-stdpattern-string-mode) std:pattern _string_ [_mode_] - [9](#9-modules) Modules - [9.1](#91-export) export - [9.2](#92-import) import - [10](#10-core-library) Core Library - [10.0.1](#1001-type-value) type _value_ - [10.0.2](#1002-len-value) len _value_ - [10.0.3](#1003-panic-message) panic _message_ - [11](#11-standard-library) Standard Library - [11.0.1](#1101-stdshuffle-randfunc-vec) std:shuffle _rand\_func_ _vec_ - [11.0.2](#1102-stddelete-vector-or-map-index-or-key) std:delete _vector-or-map_ _index-or-key_ - [11.0.3](#1103-stdrefid-value) std:ref\_id _value_ - [11.0.4](#1104-stdcopy-vecormap) std:copy _vec\_or\_map_ - [11.0.5](#1105-stdvalues-collection-or-iter) std:values _collection-or-iter_ - [11.0.6](#1106-stdkeys-collection-or-iter) std:keys _collection-or-iter_ - [11.0.7](#1107-stdsort-comparefun-vec) std:sort [_compare\_fun_] _vec_ - [11.0.8](#1108-stdcmpnumasc-a-b) std:cmp:num:asc _a_ _b_ - [11.0.9](#1109-stdcmpnumdesc-a-b) std:cmp:num:desc _a_ _b_ - [11.0.10](#11010-stdcmpstrasc-a-b) std:cmp:str:asc _a_ _b_ - [11.0.11](#11011-stdcmpstrdesc-a-b) std:cmp:str:desc _a_ _b_ - [11.0.12](#11012-stdreverse-value) std:reverse _value_ - [11.0.13](#11013-stddisplayln-arg1-) std:displayln _arg1_ ... - [11.0.14](#11014-debug-arg1-) $DEBUG _arg1_ ... - [11.0.15](#11015-stdwriteln-arg1-) std:writeln _arg1_ ... - [11.0.16](#11016-stdeval-code-string) std:eval _code-string_ - [11.0.17](#11017-stdassert-bool-message) std:assert _bool_ \[_message_] - [11.0.18](#11018-stdasserteq-actual-expected-message) std:assert\_eq _actual_ _expected_ \[_message_] - [11.0.19](#11019-stdassertstreq-actual-expected) std:assert\_str\_eq _actual_ _expected_ - [11.0.20](#11020-stdassertreleq-l-r-epsilon-message) std:assert\_rel\_eq _l_ _r_ _epsilon_ \[_message_] - [11.0.21](#11021-stdmeasuretime-unit-function) std:measure\_time _unit_ _function_ - [11.1](#111-io) I/O - [11.1.1](#1111-stdioline) std:io:line - [11.1.2](#1112-stdiolines-value) std:io:lines [_value_] - [11.1.3](#1113-stdiofilereadtext-filename) std:io:file:read\_text _filename_ - [11.1.4](#1114-stdiofileread-filename) std:io:file:read _filename_ - [11.1.5](#1115-stdiofilewritesafe-filename-bytes-or-string) std:io:file:write\_safe _filename_ _bytes-or-string_ - [11.1.6](#1116-stdiofileappend-filename-bytes-or-string) std:io:file:append _filename_ _bytes-or-string_ - [11.1.7](#1117-stdiostdoutnewline) std:io:stdout:newline - [11.1.8](#1118-stdiostdoutflush) std:io:stdout:flush - [11.1.9](#1119-stdiostdoutprint-value) std:io:stdout:print _value_ - [11.1.10](#11110-stdiostdoutwrite-value) std:io:stdout:write _value_ - [11.1.11](#11111-stdioflush-handle) std:io:flush _handle_ - [11.1.12](#11112-stdioreadsome-handle) std:io:read\_some _handle_ - [11.1.13](#11113-stdiowrite-handle-data-offs) std:io:write _handle_ _data_ [_offs_] - [11.1.14](#11114-stdiowritesome-handle-data) std:io:write\_some _handle_ _data_ - [11.2](#112-networking) Networking - [11.2.1](#1121-stdnettcpconnect-socket-addr-connect-timeout) std:net:tcp:connect _socket-addr_ [_connect-timeout_] - [11.2.2](#1122-stdnettcplisten-socket-addr-function) std:net:tcp:listen _socket-addr_ _function_ - [11.2.3](#1123-stdnetudpnew-socket-addr-connect-addr) std:net:udp:new _socket-addr_ [_connect-addr_] - [11.2.4](#1124-stdnetudpsend-socket-data-socket-addr) std:net:udp:send _socket_ _data_ [_socket-addr_] - [11.2.5](#1125-stdnetudprecv-socket-byte-count) std:net:udp:recv _socket_ [_byte-count_] - [11.3](#113-processes) Processes - [11.3.1](#1131-stdprocessrun-executable-path-arguments) std:process:run _executable-path_ [_arguments_] - [11.3.2](#1132-stdprocessspawn-executable-path-arg-vector-inheritout--inheritall) std:process:spawn _executable-path_ _arg-vector_ [:inherit\_out | :inherit\_all] - [11.3.3](#1133-stdprocesstrywait-child-handle) std:process:try\_wait _child-handle_ - [11.3.4](#1134-stdprocesskillwait-child-handle) std:process:kill\_wait _child-handle_ - [11.3.5](#1135-stdprocesswait-child-handle) std:process:wait _child-handle_ - [11.4](#114-file-system) File System - [11.4.1](#1141-stdfsrename-file-path-new-file-name) std:fs:rename _file-path_ _new-file-name_ - [11.4.2](#1142-stdfscopy-src-file-path-dst-file-path) std:fs:copy _src-file-path_ _dst-file-path_ - [11.4.3](#1143-stdfsreaddir-path-function) std:fs:read\_dir _path_ _function_ - [11.4.4](#1144-stdfsremovefile-file-path) std:fs:remove\_file _file-path_ - [11.4.5](#1145-stdfsremovedir-dir-path) std:fs:remove\_dir _dir-path_ - [11.4.6](#1146-stdfsremovedirall-dir-path) std:fs:remove\_dir\_all _dir-path_ - [11.5](#115-system) System - [11.5.1](#1151-stdsysos) std:sys:os - [11.6](#116-threading) Threading - [11.6.1](#1161-stdthreadspawn-string-globals-map) std:thread:spawn _string_ [_globals-map_] - [11.6.2](#1162-stdthreadsleep-duration) std:thread:sleep _duration_ - [11.6.3](#1163-thread-handle-api) Thread Handle API - [11.6.3.1](#11631-thdljoin) thdl.join - [11.6.3.2](#11632-thdlrecvready) thdl.recv\_ready - [11.6.4](#1164-atom-api) Atom API - [11.6.4.1](#11641-stdsyncatomnew-value) std:sync:atom:new _value_ - [11.6.4.2](#11642-atomread) atom.read - [11.6.4.3](#11643-atomwrite-value) atom.write _value_ - [11.6.4.4](#11644-atomswap-value) atom.swap _value_ - [11.6.5](#1165-atom-value-slot-api) Atom Value Slot API - [11.6.5.1](#11651-stdsyncslotnew) std:sync:slot:new - [11.6.5.2](#11652-atomslotsend-value) atom\_slot.send _value_ - [11.6.5.3](#11653-atomslotrecv) atom\_slot.recv - [11.6.5.4](#11654-atomslottryrecv) atom\_slot.try\_recv - [11.6.5.5](#11655-atomslotrecvtimeout-duration) atom\_slot.recv\_timeout _duration_ - [11.6.5.6](#11656-atomslotcheckempty) atom\_slot.check\_empty - [11.6.5.7](#11657-atomslotwaitempty) atom\_slot.wait\_empty - [11.6.5.8](#11658-atomslotwaitemptytimeout-duration) atom\_slot.wait\_empty\_timeout _duration_ - [11.6.6](#1166-channel-api) Channel API - [11.6.6.1](#11661-stdsyncmpscnew) std:sync:mpsc:new - [11.6.6.2](#11662-channelsend-value) channel.send _value_ - [11.6.6.3](#11663-channelrecv) channel.recv - [11.6.6.4](#11664-channeltryrecv) channel.try\_recv - [11.6.6.5](#11665-channelrecvtimeout-duration) channel.recv\_timeout _duration_ - [12](#12-optional-standard-library) Optional Standard Library - [12.1](#121-serialization) serialization - [12.1.1](#1211-stdserwlambda-arg) std:ser:wlambda _arg_ - [12.1.2](#1212-stdserjson-data-nopretty) std:ser:json _data_ \[_no\_pretty_] - [12.1.3](#1213-stddeserjson-string) std:deser:json _string_ - [12.1.4](#1214-stdsercsv-fielddelim-rowseparator-escapeall-table) std:ser:csv _field\_delim_ _row\_separator_ _escape\_all_ _table_ - [12.1.5](#1215-stddesercsv-fielddelim-rowseparator-data) std:deser:csv _field\_delim_ _row\_separator_ _data_ - [12.1.6](#1216-stdsermsgpack-data) std:ser:msgpack _data_ - [12.1.7](#1217-stddesermsgpack-bytes) std:deser:msgpack _bytes_ - [12.2](#122-regular-expressions-more-classic-syntax) Regular Expressions (more classic syntax) - [12.2.1](#1221-stdrematch-regex-string-input-string-function) std:re:match _regex-string_ _input-string_ _function_ - [12.2.2](#1222-stdrematchcompile-regex-string) std:re:match\_compile _regex-string_ - [12.2.3](#1223-stdremap-regex-string-function-input-string) std:re:map _regex-string_ _function_ _input-string_ - [12.2.4](#1224-stdrereplaceall-regex-string-replace-function-input-string) std:re:replace\_all _regex-string_ _replace-function_ _input-string_ - [12.3](#123-xml) xml - [12.3.1](#1231-stdxmlreadsax-xml-string-event-callback-function-do-not-trim-text) std:xml:read\_sax _xml-string_ _event-callback-function_ [_do-not-trim-text_] - [12.3.2](#1232-stdxmlcreatesaxwriter-indent) std:xml:create\_sax\_writer [_indent_] - [12.3.3](#1233-stdxmlcreatetreebuilder) std:xml:create\_tree\_builder - [12.4](#124-chrono) chrono - [12.4.1](#1241-stdchronotimestamp-format) std:chrono:timestamp \[_format_] - [12.4.2](#1242-stdchronoformatutc-utc-timestamp-format) std:chrono:format\_utc _utc-timestamp_ [_format_] - [12.4.3](#1243-stdchronoformatlocal-utc-timestamp-format) std:chrono:format\_local _utc-timestamp_ [_format_] - [12.5](#125-color-conversion) color conversion - [12.5.1](#1251-stdvrgb2hsv-color-vector) std:v:rgb2hsv _color-vector_ - [12.5.2](#1252-stdvhsv2rgb-color-vector) std:v:hsv2rgb _color-vector_ - [12.5.3](#1253-stdvrgba2hex-color-vector) std:v:rgba2hex _color-vector_ - [12.5.4](#1254-stdvhex2rgbaf-string) std:v:hex2rgba\_f _string_ - [12.5.5](#1255-stdvhex2rgbai-string) std:v:hex2rgba\_i _string_ - [12.5.6](#1256-stdvhex2hsvai-string) std:v:hex2hsva\_i _string_ - [12.5.7](#1257-stdvhex2hsvaf-string) std:v:hex2hsva\_f _string_ - [12.6](#126-hash) hash - [12.6.1](#1261-stdhashfnv1a-arg1-) std:hash:fnv1a _arg1_ ... - [12.7](#127-rand) rand - [12.7.1](#1271-stdrandsplitmix64new) std:rand:split\_mix64\_new - [12.7.2](#1272-stdrandsplitmix64newfrom-seed) std:rand:split\_mix64\_new\_from _seed_ - [12.7.3](#1273-stdrandsplitmix64next-smstate-count) std:rand:split\_mix64\_next _sm\_state_ \[_count_] - [12.7.4](#1274-stdrandsplitmix64nextopen01-smstate-count) std:rand:split\_mix64\_next\_open01 _sm\_state_ \[_count_] - [12.7.5](#1275-stdrandsplitmix64nextopenclosed01-smstate-count) std:rand:split\_mix64\_next\_open\_closed01 _sm\_state_ \[_count_] - [12.7.6](#1276-stdrandsplitmix64nextclosedopen01-smstate-count) std:rand:split\_mix64\_next\_closed\_open01 _sm\_state_ \[_count_] - [12.8](#128-utility-functions) Utility Functions - [12.8.1](#1281-stddumpupvals-function) std:dump\_upvals _function_ - [12.8.2](#1282-stdwlambdaversion) std:wlambda:version - [12.8.3](#1283-stdwlambdasizes) std:wlambda:sizes - [12.8.4](#1284-stdwlambdaparse-string) std:wlambda:parse _string_ - [12.9](#129-http-client) HTTP Client - [12.9.1](#1291-stdhttpclientnew) std:http:client:new - [12.9.2](#1292-stdhttpget-http-client-url-string-headers-and-options-map) std:http:get _http-client_ _url-string_ [_headers-and-options-map_] - [12.9.3](#1293-stdhttppost-http-client-url-string-body-bytes-headers-and-options-map) std:http:post _http-client_ _url-string_ _body-bytes_ [_headers-and-options-map_] - [12.9.4](#1294-stdhttprequest-http-client-method-string-url-string-body-bytes-headers-and-options-map) std:http:request _http-client_ _method-string_ _url-string_ [_body-bytes_ [_headers-and-options-map_]] - [12.10](#1210-mqtt-messaging) MQTT Messaging - [12.10.1](#12101-stdmqttbrokernew-config) std:mqtt:broker:new _config_ - [12.10.1.1](#121011-brokerpublish-topic-string-payload-bytes) broker.publish _topic-string_ _payload-bytes_ - [12.10.2](#12102-stdmqttclientnew-channel-client-id-broker-host-broker-port) std:mqtt:client:new _channel_ _client-id_ _broker-host_ _broker-port_ - [12.10.2.1](#121021-mqttclientpublish-topic-string-payload-bytes) mqtt\_client.publish _topic-string_ _payload-bytes_ - [12.10.2.2](#121022-mqttclientsubscribe-topic-string) mqtt\_client.subscribe _topic-string_ - [13](#13-wlambda-lexical-syntax-and-grammar) WLambda Lexical Syntax and Grammar - [13.1](#131-special-forms) Special Forms - [13.2](#132-string-formatting-syntax) String Formatting Syntax - [13.3](#133-format-string-syntax-for-stdbytespack-and-stdbytesunpack) Format String Syntax for std:bytes:pack and std:bytes:unpack ----- ## 1 - 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: ```wlambda !a = 10; # variable definition & initialization .a = 20; # assignment of a new value to a variable ``` WLambda also supports destructuring assignment of vectors: ```wlambda !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: ```wlambda !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; ``` And also with pairs: ```wlambda !p = $p(10, 20); !(a, b) = p; .(a, b) = p; std:assert_eq a 10; std:assert_eq b 20; ``` ### 1.1 - Destructuring to Variables Like highlighted in the previous section you can define and assign to multiple variables at once. Following data types support destructuring: - Vectors: ```wlambda !(a, b, c) = $[1, 2, 3]; std:assert_eq a 1; std:assert_eq b 2; std:assert_eq c 3; ``` - Maps: ```wlambda !(x, foo, lol) = ${foo = 33, lol = 42, x = 2}; std:assert_eq x 2; std:assert_eq foo 33; std:assert_eq lol 42; ``` - Pairs: ```wlambda !(x, y) = $p("ex", "uepsilon"); std:assert_eq x "ex"; std:assert_eq y "uepsilon"; ``` - Numerical Vectors: ```wlambda !(x, y, z) = $i(3, 44, 4); std:assert_eq x 3; std:assert_eq y 44; std:assert_eq z 4; !(r, g, b, a) = $f(0.3, 1.0, 0.4, 1.0); std:assert_eq r 0.3; std:assert_eq g 1.0; std:assert_eq b 0.4; std:assert_eq a 1.0; ``` ### 1.2 - Global Variables You can define global variables that are not bound to a lexical scope as follows: ```wlambda { !:global a = 13; }[]; std:assert_eq a 13; ``` Global variables however do not live beyond file or module boundaries. ### 1.3 - Constants WLambda supports constant _variables_. These are global variables you can't assign to. They are resolved and inserted at compile time and offer a slight performance advantage (roughly 3-4%) over (global or local) variables. ```wlambda !:const X = 11; std:assert_eq X 11; # Destructuring works too, but only with compile time literal values # in the vectors / maps: !:const (ON, OFF) = $[$true, $false]; !:const (RED, BLUE) = ${ BLUE = 0x0000FF, RED = 0xFF0000, }; std:assert_eq ON $true; std:assert_eq OFF $false; std:assert_eq RED 0xFF0000; std:assert_eq BLUE 0x0000FF; ``` However, be aware that these _constants_ are not really constant. Due to performance reasons referential values like Lists or Maps are not copied (neither shallow, nor deep) if you access them through a constant. ```wlambda !:const V = $[1,2,3]; std:assert_eq (str V) (str $[1,2,3]); std:push V 43; # Mutation of a 'constant' std:assert_eq V.3 43; ``` Constants also work across module borders: ```wlambda !:const X = 10; # When imported the X will remain constant: !@export X = X; ``` ## 2 - 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. ### 2.1 - Closures Functions take values from the outer scope by promoting the variable at runtime to a hidden reference to their previous value: ```wlambda !a = 10; !b = 20; # function transforms a and b to hidden references !add_a_and_b = { a + b }; std:assert_eq add_a_and_b[] 30; # The assignment assigns to the hidden reference, so the closure add_a_and_b # also receives the new value: .a = 33; std:assert_eq add_a_and_b[] 53; # a and b are dereferenced on local variable access. std:assert_eq a + b 53; ``` #### 2.1.1 - Object Oriented Programming with Closures This section explains how to create objects and hide state using closures. Keep in mind, that there are also `$self` and `$data` available, which allow a different approach for referring to the object state/data than to capture the object as reference in a closure. Keep in mind, that care must be taken (references need to be captures weakly) with the references as shown below, because otherwise you will get reference cycles and memory leaks. ```wlambda !new_Cat = {!(name) = @; # Captures by closures upgrade the outer `self` variable to a _hidden_ # reference, which is then captured. As the closure is stored in # `self`, this would create a ref cycle. This is why we needed # to make a weak reference to self. # Make an explicit hidden reference: !self_ = $& ${ name = name, }; # Create a weak reference form the hidden reference: !self = $weak& $:self_; self.meow = { std:displayln self.name " meows!"; }; self.get_name = { self.name }; # To keep the object alive, we retrieve a strong reference # from the hidden reference: $: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: ```wlambda !new_Cat = {!(name) = @; # This does not make cycles, because `name` does not contain # the closures in the end. !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"; ``` ### 2.2 - Function calling To call functions, you have at least 4 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). The fourth alternative is the `&>` and `<&` (and the apply variants `&@>` and `<@&`) argument pipe operators which can be conveniently used in conjunction with the first variant to prevent some parenthesis. Also belonging into the category of function calling operators there is the collection addition operators `+>` and `<+` which are described in their own section. Here are examples: ```wlambda # 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 fourth variant: std:assert_eq str <& $[1, 2, 3] "$[1,2,3]"; std:assert_eq $[1, 2, 3] &> str "$[1,2,3]"; ``` 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. ```wlambda !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: ```wlamdba !add = {!(a, b) = @; a + b }; std:assert_eq add[1, 2] 3; ``` ### 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: ```wlambda !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; ``` #### 2.3.1 - std:to\_no\_arity _function_ This function disables all arity checks of a function. Use this with care and diligence. ```wlambda !f = { _ }; # accepts exactly 1 param # f keeps its 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; ``` ### 2.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'. ```wlambda !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`. #### 2.4.1 - The $self and $data special variables If you call a method using the dot `.`, and the value on the left side is a map or vector, you will get the map or vector by `$self`. However, if you define a `_data` key on the map, or put something in the second element of the vector, you can refer to it using `$data`. You can use this to refer to the members and other functions of a structure: ```wlambda !new_ab_struct = { ${ _data = ${ a = 1, b = 2 }, inc_a = { $data.a += 1; }, inc_b = { $data.b += 1; }, a = { $data.a }, b = { $data.b }, inc_both = { $self.inc_a[]; $self.inc_b[]; }, } }; !ab = new_ab_struct[]; ab.inc_a[]; std:assert_eq ab.a[] 2; ab.inc_b[]; std:assert_eq ab.b[] 3; ab.inc_both[]; std:assert_eq ab.a[] 3; std:assert_eq ab.b[] 4; ``` The next seconds show how this can be used to do prototyped object oriented programming. #### 2.4.2 - 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: ```wlambda !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: ```wlambda !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. ```wlambda !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; ``` #### 2.4.3 - Object Oriented with Prototypes and Inheritance You can inherit functionality from a different class by assigning it to the prototype of the class itself. ```wlambda !SuperClass = ${ init_super_class = { $data.inc = 0; }, inc = { $data.inc += 1; $data.inc }, }; ``` Please notice, that _SuperClass_ does not have it's own constructor, instead you should define a custom init function like `init_super_class`, to define the used fields. The _SuperClass_ will refer to the `$data` of the object that is going to be created by _MyClass_ in the next step. ```wlambda !SuperClass = ${ init_super_class = { $data.inc = 0; }, inc = { $data.inc += 1; $data.inc }, }; !MyClass = ${ _proto = SuperClass, new = { !self = ${ _proto = $self, _data = ${ other = 10 }, }; self.init_super_class[]; self }, get_other = { $data.other }, get_inc = { $data.inc }, }; !my_obj = MyClass.new[]; std:assert_eq my_obj.get_other[] 10; std:assert_eq my_obj.inc[] 1; std:assert_eq my_obj.inc[] 2; std:assert_eq my_obj.inc[] 3; std:assert_eq my_obj._data.other 10; std:assert_eq my_obj._data.inc 3; ``` #### 2.4.4 - Object Oriented with Prototypes and $self References and $data References There might come a time, when you want to pass a reference of your object around, but you want to prevent cyclic references. For this you will need to return a strong reference `$&&` from your constructor as `$self` and if you want to refer to `$data` from callback functions, you are advised to also wrap it into a strong reference. ```wlambda !destroyed = $false; !MyClass = ${ new = { $&& ${ _proto = $self, _data = $&& ${ x = 1 }, dropper = std:to_drop { .destroyed = $t; }, } }, inc_x = { $data.x += 1 }, install_on = {!(callchain) = @; !self = $w& $self; std:push callchain { self.inc_x[]; }; }, install_getter = {!(callchain) = @; !data = $w& $data; std:push callchain { data.x }; }, }; # Create instance: !my_obj = MyClass.new[]; my_obj.inc_x[]; !chain = $[]; my_obj.install_on chain; my_obj.install_getter chain; # There are now 3 references to 'my_obj': # - my_obj variable # - first callback in chain # - second callback in chain std:assert_eq my_obj._data.x 2; chain.0[]; # calls my_ocj.inc_x[]; std:assert_eq my_obj._data.x 3; # Second callback gets x: std:assert_eq chain.1[] 3; !my_obj = $n; # destroy only strong reference std:assert destroyed; ``` Of course the callbacks now refer to `$none` to call `inc_x`, a more sophisticated way of cleanup is of course necessary. But this is just an example. ### 2.5 - 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 #### 2.5.1 - '|' Tail Argument Function Chaninig This syntax is useful if you have following function call composition: ```text (fn arg1 arg2 (fn2 arg_b1 arg_b2 (fn3 arg_c1 arg_c2 ...))) ``` These can be written more comfortably like this: ```text fn3 arg1 arg2 | fn2 arg_b1 arg_b2 | fn arg1 arg2 ``` An example with actual values: ```wlambda !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: ```text fn1 a1 a2 | fn2 b1 b2 ( ) => fn2 b1 b2 (fn1 a1 a2) """"""""" ^ v | --------------------| ``` #### 2.5.2 - '|>' Left Hand Function Chaining This syntax is useful if you want to make deep call chains like these: ```text (((fn arg1 arg2 ...) arg_b1 arg_b2 ...) arg_c1 arg_c2 ...) ``` These can be written more comfortably like this: ```text fn arg1 arg2 |> arg_b1 arg_b2 |> arg_c1 arg_c2 ``` or nicer formatted: ```text fn arg1 arg2 |> arg_b1 arg_b2 |> arg_c1 arg_c2 ``` Here an actual example: ```wlambda !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: ```text fn1 a1 a2 |> b1 b2 => (( ) ) """"""""" """"" ^ ^ v v | | -----------|--------------| | -------------------| ``` #### 2.5.3 - Forward Argument Pipe `arg &> fun` This operator has the highest precedence over all other operators and is used to be able to write this: ```wlambda if "foob" &> $r/f(^*)b/ { std:assert_eq $\.1 "oo"; } { std:assert $false; } ``` That means `f a &> b` is equivalent to writing `f[b[a]]` or `(f (b a))`. Chaining multiple is also possible and left associative: `a &> b &> c` is `(c (b a))`. You can see it as piping operation: ```wlambda !r = "ABC" &> std:str:to_lowercase &> \std:str:pad_start 10 "0" _; std:assert_eq r "0000000abc"; ``` #### 2.5.4 - Forward Argument Apply Pipe `list &@> fun` This operator is like `&>`. But it will call the function with the elements in the given _list_ as arguments. That means: `list &@> fun` is equivalent to `fun[[list]]`. ```wlambda std:assert_eq $[2, 5] &@> `+` 7; ``` #### 2.5.5 - Reverse Argument Pipe `fun <& arg` Like the `&>` operator this operator, but it has a lower precedence (does not bind as strongly as `&>`) and is right associative. That means you can write this: ```wlambda !r = (\std:str:pad_start 10 "0" _) <& std:str:to_lowercase <& "ABC"; std:assert_eq r "0000000abc"; ``` That means, writing `f <& a <& x` becomes `f[a[x]]` or `(f (a x))`. #### 2.5.6 - Reverse Argument Apply Pipe `list &@> fun` This operator is like `<&`. But it will call the function with the elements in the given _list_ as arguments. That means: `fun <@& list` is equivalent to `fun[[list]]`. ```wlambda std:assert_eq `+` <@& $[2, 5] 7; ``` ### 2.6 - 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 using the boolean calling semantics. ```wlambda !some_func = \:outer { !x = 10; # does stuff... (x == 10) { return :outer 20 }; # more stuff that is not executed if x == 10. } ``` #### 2.6.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. ```wlambda !f = { 10; return 20; 30 }; std:assert_eq f[] 20; ``` Here an example for unwinding two call frames: ```wlambda !f = \:x { 10; { return :x 20 }[]; 30; }; std:assert_eq f[] 20; ``` The labels do not adhere to lexical scoping and are dynamically scoped: ```wlambda !g = { return :x 30 }; !f = \:x { 20; g[]; 40 }; std:assert_eq f[] 30; ``` #### 2.6.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: ```wlambda !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: ```wlambda !y = 1; !res = \:x { .y = y + 1; (y >= 2) \return :x 20; .y = y + 1; .y = y + 1; }[]; std:assert_eq res 20; ``` #### 2.6.3 - std:to\_drop _function_ (or RAII, Destructors or Drop Functions) You can create a function that is called when it is dropped/its reference count goes to 0. ```wlambda !dropped = $false; !x = std:to_drop { .dropped = $true; }; std:assert not[dropped]; .x = $none; std:assert dropped; ``` Please note, that the drop function will be executed in a newly constructed default EvalContext, this means there is some overhead and that the EvalContext dependent results of `std:eval` might be different. #### 2.6.4 - std:time:now [_unit_] Returns the current system time since the UNIX epoch (1970-01-01 00:00:00 UTC). If no _unit_ is specified, the default is `:ms`. Following units are available: - seconds: `"s"` - milliseconds: `"ms"` - microseconds: `"us"` - nanoseconds: `"ns"` ```wlambda std:assert (std:time:now :s) > 1000; std:assert (std:time:now :ms) > 1000; std:assert len[str[std:time:now :ns]] > 18; ``` #### 2.6.5 - std:srand [_seed_] With this function you can seed the internal pseudo random number generator based on an unspecified PRNG algorithm, that might or might not change in the next WLambda version. If no _seed_ is provided, the current system time (in `ns` resolution) is used. If _seed_ is provided, it is set to the integer value of that. ```wlambda std:srand[]; std:srand 1000; ``` #### 2.6.6 - std:rand [_max-or-mode_] Returns a random number between 0 and _max_. The interval 0 to _max-or-mode_ is closed/open, that means 0 is included but _max-or-mode_ is not included. If _max-or-mode_ is a string `"i64"` or symbol `:i64`, then std:rand will return a random signed 64 bit integer. If _max-or-mode_ is not provided, a float number between 0.0 and 1.0 (including 0.0 but not including 1.0) is returned. ```wlambda std:srand 1234567890; !zeros = $@i iter i 0 => 1000 { if std:rand[100] == 0 \$+ 1; }; !count_100 = $@i iter i 0 => 1000 { if std:rand[100] == 100 \$+ 1; }; std:assert zeros > 0; std:assert count_100 == 0; std:assert std:rand[] < 1.0; std:assert std:rand[] >= 0.0; ``` Please note: The PRNG algorithm used for `std:rand` may change without further notice. If you require your project to have consistent PRNG results across all WLambda versions use `std:rand:split_mix64_*`. ### 2.7 - Function utilities #### 2.7.1 - is\_fun _value_ Returns `$true` if _value_ is a function. ```wlambda std:assert ~ is_fun {}; std:assert ~ is_fun is_fun; std:assert ~ not ~ is_fun ${a=10}; ``` ## 3 - Data Types ### 3.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:ser:wlambda` 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. ```wlambda std:assert ~ $n == $none; std:assert ~ int[$n] == 0; std:assert ~ float[$n] == 0.0; std:assert ~ str[$n] == ""; std:assert ~ std:ser:wlambda[$n] == "$n"; std:assert ~ is_none[$n]; ``` #### 3.1.1 - is\_none _value_ Returns `$true` if _value_ is `$none` or `$o()`. ```wlambda std:assert ~ is_none $none; std:assert ~ is_none $o(); std:assert ~ not ~ is_none $false; std:assert ~ not ~ is_none $o(10); ``` #### 3.1.2 - is\_some _value_ Returns `$true` if _value_ is anything except `$none` or `$o()`. ```wlambda std:assert ~ not ~ is_some $none; std:assert ~ not ~ is_some $o(); std:assert ~ is_some $false; std:assert ~ is_some 30; std:assert ~ is_some $o(30); ``` ### 3.2 - Optional values `$o()` and `$o(...)` An optional value can either contain another value, or contain no value at all. An empty optional value is not much different from `$none`, but it is sometimes desirabel to make a difference between an optional value and a `$none` value if the `$none` value is used as sentinel value. Optional values were introduced for functions that lookup stuff and either return _something_ that might be `$none` (eg. if some element in a vector is searched for), or return that nothing was found. Note that there are two operators that interact with this type in a convenient way. The optional or operator `/$o` and the default value operator `//` can be used to provide default values and unwrap optionals. These operators also support short circuit, that means you can write `x // return[$none]`. The functions `is_none` and `is_some` like stated above work for these optional values too: ```wlambda std:assert ~ is_none $o(); std:assert ~ is_some $o(10); std:assert ~ is_some $o($none); std:assert ~ is_some $o($o()); ``` Here is a demonstration of the very convenient optionals or operator and the default value operator: ```wlambda std:assert_eq $o() /$o 11 11; std:assert_eq $o(22) /$o 11 22; std:assert_eq $none /$o 11 $none; std:assert (is_err ($e 10) /$o 20); std:assert_eq $o() // 11 11; std:assert_eq $o(22) // 11 22; std:assert_eq $none // 11 11; std:assert_eq 123 // 11 123; std:assert (is_err ($e 10) // 20); std:assert_eq $o() /? 11 11; std:assert_eq $o(22) /? 11 22; std:assert_eq $none /? 11 11; std:assert_eq 123 /? 11 123; std:assert_eq ($e 10) /? 11 11; ``` Calling an optional value will return it's contents or `$none`: ```wlambda std:assert_eq $o()[] $none; std:assert_eq $o(10)[] 10; !do_something = { if _ == 0 { $o() } { $o(_ + 10) } }; !result = do_something 11; std:assert_eq result[] 21; !result = do_something 0; std:assert_eq result[] $none; ``` In a boolean context an optional becomes `$true` if it contains something and `$false` if it has nothing. ```wlambda std:assert ~ not ~ bool $o(); std:assert ~ bool $o(10); !x = $o(); !res1 = if x "something" "nothing"; std:assert_eq res1 "nothing"; .x = $o(30); !res2 = if x "something" "nothing"; std:assert_eq res2 "something"; ``` Many other operations are just forwarded to the contents of the optional value: ```wlambda std:assert_eq $o(33) + 44 77; !x = $o($[1,2,3]); std:push x 4; std:assert_eq (str x) (str $[1,2,3,4]); std:assert_eq (float $o(4.4)) 4.4; std:assert_eq (int $o(4.4)) 4; ``` An optional value can also be dereferenced: ```wlambda std:assert_eq $*$o() $none; std:assert_eq $*$o(10) 10; ``` Calls with more than zero arguments are forwarded to the contents: ```wlambda std:assert_eq ($o("xx") "yy") "xxyy"; !x = { _ * 20 }; std:assert_eq ($o(x) 30) 600; ``` #### 3.2.1 - is\_optional _value_ Returns `$true` if _value_ is an optional value. That means either `$o()` or `$o(...)`. ```wlambda std:assert ~ is_optional $o(); std:assert ~ is_optional $o($none); std:assert ~ is_optional $o(10); std:assert ~ not ~ is_optional $true; std:assert ~ not ~ is_optional $none; std:assert ~ not ~ is_optional $false; std:assert ~ not ~ is_optional 303; ``` #### 3.2.2 - Unwrapping optionals You can unwrap an optional with `unwrap`. It will panic if there is no value provided. Otherwise it will return the contents. Alternatively you can use the optional or operator `/$o` or even the default value operator `//` for accessing the potentially wrapped value while providing a default value. ```wlambda std:assert_eq unwrap[$o(10)] 10; std:assert_eq $o(10) /$o 20 10; std:assert_eq $o() /$o 20 20; std:assert_eq $none /$o 20 $none; std:assert_eq $o(10) // 20 10; std:assert_eq $o() // 20 20; std:assert_eq $none // 20 20; ``` ### 3.3 - 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. ```wlambda 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. You can also use the `match` statement to check return types of functions: ```wlambda !res = match (std:str:join "," "foo") ($e err) => { $\.err } r => { $\.r }; std:assert_eq (res 0 8) "str:join"; # As comparison, the positive case for this construct: !res = match 100 ($e err) => { $\.err } r => { $\.r }; std:assert_eq res 100; ``` There is also the error or operator `/$e` and the extended default value operator `/?` that helps to shortcut any errors. These operators also support short circuit, that means you can do `func_might_return_err[] // return[$n]`. But use this with care, it hides errors all to easily! ```wlambda std:assert_eq ($e 30) /$e 20 20; std:assert_eq $n /$e 10 $n; # The extended default value operator also hides errors: std:assert_eq ($e 30) /? 20 20; std:assert_eq $n /? 10 10; std:assert_eq $o() /? 10 10; std:assert_eq $o(22) /? 10 22; std:assert_eq 22 /? 10 22; std:assert_eq $f /? 10 $false; # You can combine this operator with the some or operator `//` or even `/?`: std:assert_eq ($e 30) /$e 10 // 20 10; std:assert_eq $n /$e 10 // 20 20; # To demonstrate short circuit: !res = 0; !x = ($e 10) /$e { .res = 10 }[]; std:assert_eq res 10; std:assert_eq x $none; ``` 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 - std: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_optional - is_int - == - != - std:to_ref - std:ref_id - std:ser:wlambda - `//`, `/?`, `/$n`, `/$e`, `/$o` All other functions don't accept errors as their argument. #### 3.3.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. ```wlambda !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: ```wlambda !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: ```wlambda !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); ``` #### 3.3.2 - unwrap _value_ Unwraps the given _value_. If the _value_ is an error object it will panic. Otherwise it will just return the given value. If the _value_ is an optional value, it will return the value that is wrapped in the optional value. If it is an empty optional, it will also panic. Here an demonstration of the unwrap panic: ```wlambda match (std:eval $code { unwrap $e XXX }) ($e err) => { std:assert ~ std:str:find "Variable 'XXX' undefined" $\.err; } { std:assert $false }; ``` And here how to unwrap optionals: ```wlambda std:assert_eq (unwrap $o(123)) 123; match (std:eval $code { unwrap $o() }) ($e err) => { std:assert ~ std:str:find "unwrap empty option" $\.err; } { std:assert $false }; ``` #### 3.3.3 - unwrap\_err _error-value_ Unwraps an error value. Does panic if _error-value_ is not an error value. If it is an error value, the inner wrapped value is returned. ```wlambda !v = unwrap_err $e "Some Error"; std:assert_eq v "Some Error"; ``` #### 3.3.4 - 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: ```wlambda on_error {!(func, line, col, filename) = @; # ... } ($e "test"); ``` A usage example: ```wlambda !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!"; ``` #### 3.3.5 - is\_err _value_ Returns `$true` if _value_ is an error value. ```wlambda std:assert ~ is_err $e "foo"; std:assert ~ not ~ is_err $none; std:assert ~ not ~ is_err 10; ``` #### 3.3.6 - std:error\_to\_str _value_ This function accepts an error value in contrast to `str`, but does not panic but transform the error value into its string representation. ```wlambda !r = std:error_to_str $e "TEST"; std:assert_eq r "$e \"TEST\" [@ :1:26 Err]"; ``` 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. ### 3.4 - 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: ```wlambda !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"; ``` #### 3.4.1 - is\_bool _any-value_ You can check if something is a boolean with `is_bool`: ```wlambda 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]; ``` #### 3.4.2 - bool _any-value_ You can cast _any-value_ into a boolean with the `bool` function: ```wlambda 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; ``` #### 3.4.3 - not _value_ This function negates the boolean _value_. If it is not a boolean, it will be casted into one before negating. ```wlambda std:assert ~ not $false; std:assert ~ not 0; std:assert ~ not $none; ``` #### 3.4.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: ```wlambda std:assert_eq ($true $[:a, :b]) :b; std:assert_eq ($false $[:a, :b]) :a; ``` ### 3.5 - 64-Bit Integers WLambda's most basic numeric data type is the 64-Bit integer, aka _i64_ in Rust. Like with other numbers multiple radix literal forms are supported: ```wlambda # Decimal: std:assert_eq 10r99 99; # Hexadecimal: std:assert_eq 0xFF01 65281; # Binary: std:assert_eq 0b1011 11; std:assert_eq -0b1011 -11; # Radix 4: std:assert_eq 4r31 13; ``` #### 3.5.1 - int _value_ Returns the integer casted version of _value_. Mostly interesting for converting a string to an integer (in radix 10) or for getting the truncated value of a float. ```wlambda std:assert_eq (int 4.2) 4; std:assert_eq (int "402") 402; std:assert_eq (int "a3") 0; std:assert_eq (int $b"@") 0x40; # Returns the byte value of the first char std:assert_eq (int $[4,4,4]) 3; # Same as `len` std:assert_eq (int ${a=4,b=4}) 2; # Same as `len` ``` #### 3.5.2 - is\_int _value_ Returns `$true` if _value_ is of data type integer. Otherwise it returns `$false`. #### 3.5.3 - std:neg\_i64 _integer_ Negates the _integer_, which makes a negative from a positive and positive from a negative number. ```wlambda std:assert_eq (std:neg_i64 -1) 1; std:assert_eq (std:neg_i64 1) -1; std:assert_eq (std:neg_i64 0xFF) -255; ``` #### 3.5.4 - std:not\_i64 _integer_ Flips the bits of the signed 64-Bit _integer_. ```wlambda std:assert_eq (std:not_i64 -1) 0; std:assert_eq (std:not_i64 1) -2; std:assert_eq (std:not_i64 0xFF) -256; ``` #### 3.5.5 - std:neg\_u32 _integer_ Negates the _integer_ as if it was an unsigned 32-Bit integer. ```wlambda std:assert_eq (std:neg_u32 0xFF) 4294967041; std:assert_eq (std:neg_u32 0x1) 4294967295; std:assert_eq (std:neg_u32 0x0) 0; ``` #### 3.5.6 - std:not\_u32 _integer_ Flips the bits of the _integer_ as if it was an unsigned 32-Bit integer. ```wlambda std:assert_eq (std:not_u32 0xFF) 4294967040; std:assert_eq (std:not_u32 0x1) 4294967294; std:assert_eq (std:not_u32 0x0) 4294967295; ``` ### 3.6 - 64-Bit Floats WLambda supports 64-Bit floating point numbers, aka _f64_ in Rust. Like with other numbers multiple radix literal forms are supported: ```wlambda # 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; ``` #### 3.6.1 - float _value_ This function casts _value_ into a float: ```wlambda 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; ``` #### 3.6.2 - is\_float _value_ Returns `$true` if _value_ is a float, otherwise `$false` is returned. ```wlambda 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; ``` #### 3.6.3 - std:num:acos _float_ Computes the arccosine of a number. Return value is in radians in the range [0, pi] or NaN if the number is outside the range [-1, 1]. #### 3.6.4 - std:num:acosh _float_ Inverse hyperbolic cosine function. #### 3.6.5 - std:num:asin _float_ Computes the arcsine of a number. Return value is in radians in the range [-pi/2, pi/2] or NaN if the number is outside the range [-1, 1]. #### 3.6.6 - std:num:asinh _float_ Inverse hyperbolic sine function. #### 3.6.7 - std:num:atan _float_ Computes the arctangent of a number. Return value is in radians in the range [-pi/2, pi/2]. #### 3.6.8 - std:num:atan2 _y_ _x_ Computes the four quadrant arctangent of _y_ and other _x_ in radians. - x = 0, y = 0: 0 - x >= 0: arctan(y/x) -> [-pi/2, pi/2] - y >= 0: arctan(y/x) + pi -> (pi/2, pi] - y < 0: arctan(y/x) - pi -> (-pi, -pi/2) #### 3.6.9 - std:num:atanh _float_ Inverse hyperbolic tangent function. #### 3.6.10 - std:num:cbrt _float_ Takes the cubic root of a number. #### 3.6.11 - std:num:ceil _float_ Returns the smallest integer (still a float) greater than or equal to a number. #### 3.6.12 - std:num:cos _float_ Computes the cosine of a number (in radians). #### 3.6.13 - std:num:cosh _float_ Hyperbolic cosine function. #### 3.6.14 - std:num:exp _float_ Returns e ^ _float_, (the exponential function). #### 3.6.15 - std:num:exp2 _float_ Returns 2 ^ _float_. #### 3.6.16 - std:num:exp\_m1 _float_ Returns (e ^ _float_ - 1) in a way that is accurate even if the number is close to zero. #### 3.6.17 - std:num:floor _float_ Returns the largest integer (still as float) less than or equal to a number. #### 3.6.18 - std:num:hypot _y_ _x_ Calculates the length of the hypotenuse of a right-angle triangle given legs of length _x_ and _y_. #### 3.6.19 - std:num:ln _float_ Returns the natural logarithm of the number. #### 3.6.20 - std:num:log _float_ Returns the logarithm of the number with respect to an arbitrary base. The result may not be correctly rounded owing to implementation details; `std:log2` can produce more accurate results for base 2, and `std:log10` can produce more accurate results for base 10. #### 3.6.21 - std:num:log10 _float_ Returns the base 10 logarithm of the number. #### 3.6.22 - std:num:log2 _float_ Returns the base 2 logarithm of the number. #### 3.6.23 - std:num:pow _float_ Raises a number to a floating point power. You may also use the `^` operator, which also works for integers. #### 3.6.24 - std:num:recip _float_ Takes the reciprocal (inverse) of a number, 1/x. #### 3.6.25 - std:num:round _float_ Returns the nearest integer (still a float) to a number. Round half-way cases away from 0.0. #### 3.6.26 - std:num:sin _float_ Computes the sine of a number (in radians). #### 3.6.27 - std:num:sinh _float_ Hyperbolic sine function. #### 3.6.28 - std:num:sqrt _float_ Takes the square root of a number. #### 3.6.29 - std:num:tan _float_ Computes the tangent of a number (in radians). #### 3.6.30 - std:num:tanh _float_ Hyperbolic tangent function. #### 3.6.31 - std:num:to\_degrees _float_ Converts radians to degrees. #### 3.6.32 - std:num:to\_radians _float_ Converts degrees to radians. #### 3.6.33 - std:num:trunc _float_ Returns the integer part of a number. #### 3.6.34 - std:num:lerp _a_ _b_ _x_ Linear interpolation between _a_ and _b_ by _x_. Where _x_ is in the range of `[0.0, 1.0]`. ```wlambda !res = int ~ std:num:lerp 0.0 100.0 0.5; std:assert_eq res 50; ``` #### 3.6.35 - std:num:smoothstep _a_ _b_ _x_ Interpolates smoothly from 0.0 to 1.0 where _x_ is in the range of `[a, b]`. ```wlambda !res = int ~ 1000.0 * (std:num:smoothstep 0.0 100.0 10.0); std:assert_eq res 28; ``` #### 3.6.36 - std:num:fract _float_ Returns the fractional part of the floating point number _float_. ```wlambda std:assert ((std:num:fract 4.25) - 0.25) < 0.00001 ``` ### 3.7 - Numeric Functions These functions work for all types of numbers. #### 3.7.1 - std:num:abs _number_ Takes the absolute value of _number_. If _number_ is not a number it will be converted into an integer. ```wlambda std:assert_eq (std:num:abs -10) 10; std:assert_eq (std:num:abs -13.3) 13.3; ``` #### 3.7.2 - std:num:signum _number_ Returns either 1 or -1, depending on the sign of the given _number_. ```wlambda std:assert_eq (std:num:signum -4) -1; std:assert_eq (std:num:signum 4) 1; std:assert_eq (std:num:signum -4.0) -1.0; std:assert_eq (std:num:signum 4.0) 1.0; ``` #### 3.7.3 - std:num:int\_to\_closed\_open01 _integer_ Transforms the given 64-Bit _integer_ into a number in the range `0.0` to `1.0`. Inclusive `0.0`, exclusive `1.0`. This function is mainly useful if you generated the integer from a random number generator. ```wlambda std:assert_rel_eq (std:num:int_to_closed_open01 0) 0.0 0.00000001; std:assert_rel_eq (std:num:int_to_closed_open01 -1) 0.999999999 0.00000001; ``` #### 3.7.4 - std:num:int\_to\_open01 _integer_ Transforms the given 64-Bit _integer_ into a number in the range `0.0` to `1.0`. Exclusive `0.0`, exclusive `1.0`. This function is mainly useful if you generated the integer from a random number generator. ```wlambda std:assert (std:num:int_to_open01 0) > 0.0; std:assert (std:num:int_to_open01 -1) < 1.0; ``` #### 3.7.5 - std:num:int\_to\_open\_closed01 _integer_ Transforms the given 64-Bit _integer_ into a number in the range `0.0` to `1.0`. Inclusive `0.0`, inclusive `1.0`. This function is mainly useful if you generated the integer from a random number generator. ```wlambda std:assert (std:num:int_to_open_closed01 0) > 0.0; std:assert (std:num:int_to_open_closed01 -1) == 1.0; ``` ### 3.8 - Numerical Mathematical Vectors In order to aid in the development of GUIs, games, and other physics/geometry adjacent software, WLambda comes with a built in datatype for mathematical vectors, which can contain floats and integers and have between two and four dimensions. ```wlambda # integer vectors std:assert ~ $i(-1, 2).y == 2; std:assert ~ (ivec ${z=3}) == $i(0,0,3); std:assert ~ (ivec4 $[]) == $i(0,0,0,0); std:assert ~ $i(1.49, -2.72) == $i(1,-2); # float vectors std:assert ~ $f(1.00, -33).x == $f(1, 200).first; std:assert ~ $f(-0, 2.4).y == $f(1.6, 2.4).second; std:assert ~ (fvec3 ${w=0.1}) == $f(0,0,0); # conversion std:assert ~ (fvec3 $i(1, 2))/10 == $f(0.1, 0.2, 0); std:assert ~ (ivec2 $f(1.3, 2.7, -5.8)) == $i(1, 2); std:assert ~ (ivec $f(1.3, 2.7, -5.8)) == $i(1, 2, -5); ``` #### 3.8.1 - Vector Conversions There are eight functions for converting other values into vectors and vectors of integers into vectors of floats: - `ivec` - `ivec2` - `ivec3` - `ivec4` - `fvec` - `fvec2` - `fvec3` - `fvec4` The functions without a dimension suffix fill in as many dimensions as are present in the object being converted. The functions with dimension suffixes fill in any missing dimensions with `0`s and ignore dimensions as necessary. NOTE: `ivec` will always truncate (i.e. round down) floats into integers when converting, just like when converting floats into integers implicitly elsewhere in WLambda. #### 3.8.2 - Vector Component Access There are 12 functions for accessing the components of vectors, but only four have unique behavior (the rest are aliases). - `x`/`r`/`h`/`0`/`first`, - `y`/`g`/`s`/`1`/`second`, - `z`/`b`/`v`/`2`/`third`, - `w`/`3`/`fourth` ```wlambda !my_vec = $f(39.3, 404.504, 333.8); std:assert_eq my_vec.x my_vec.0; std:assert_eq my_vec.x my_vec.first; std:assert_eq my_vec.y my_vec.1; std:assert_eq my_vec.y my_vec.second; std:assert_eq my_vec.z my_vec.2; std:assert_eq my_vec.z my_vec.third; std:assert_eq my_vec.w my_vec.3; std:assert_eq my_vec.w my_vec.fourth; ``` #### 3.8.3 - Named Field Access and Swizzling You can access the fields of numeric vectors with different keys: ```wlambda std:assert_eq $i(2,3,4,5).x 2; std:assert_eq $i(2,3,4,5).y 3; std:assert_eq $i(2,3,4,5).z 4; std:assert_eq $i(2,3,4,5).w 5; std:assert_eq $i(5,6,7,8).r 5; std:assert_eq $i(5,6,7,8).g 6; std:assert_eq $i(5,6,7,8).b 7; std:assert_eq $i(5,6,7,8).a 8; std:assert_eq $i(5,6,7,8).h 5; std:assert_eq $i(5,6,7,8).s 6; std:assert_eq $i(5,6,7,8).v 7; std:assert_eq $i(5,6,7,8).a 8; std:assert_eq $i(5,6,7,8).0 5; std:assert_eq $i(5,6,7,8).1 6; std:assert_eq $i(5,6,7,8).2 7; std:assert_eq $i(5,6,7,8).3 8; ``` You can also use **swizzling** to quickly make a new vector: ```wlambda std:assert_eq $i(2,3,4).xx $i(2,2); std:assert_eq $i(2,3,4).xyxz $i(2,3,2,4); std:assert_eq $i(2,3,4).bgr $i(4,3,2); std:assert_eq $i(2,3).xyrg $i(2,3,2,3); std:assert_eq $i(2,3,4,5).zw $i(4,5); ``` #### 3.8.4 - Euler Addition/Subtraction You can add vectors to each other and subtract them from each other. The type of the resulting vector will be the same as the vector on the left. The number of dimensions in the resulting vector will be the same as the vector with the highest number of dimensions that was involved in the operation. If the value on the right isn't a vector, it will be converted into one, just as if it were passed through `ivec` or `fvec`, meaning that as many dimensions are kept as are present. ```wlambda std:assert_eq[ $i(0.1, 0.9) + $i(1, 0) , $i(1, 0) ]; std:assert_eq[ $f(0.1, 0.9) + $i(1, 0) , $f(1.1, 0.9) ]; std:assert_eq[ $f(0.1, 0.9) + ${ w=7 } , $f(0.1, 0.9, 0, 7) ]; std:assert_eq[ std:v:mag2 $i(-1, 5) + $i(1, -5) , 0.0 ]; ``` #### 3.8.5 - Scalar Multiplication/Division You can multiply and divide integer and float vectors by single numbers. This copies the vector, multiplies or divides each component of the vector by the single number, and returns the result. NOTE: Dividing `ivec`s will always truncate (i.e. round down) floats into integers. ```wlambda std:assert ~ $i(3, 6)/2 == $i(1, 3); std:assert ~ $f(3, 6)/2 == $f(1.5, 3); std:assert ~ $f(0.5, 0) * 1.3 == $f(0.65,0); std:assert ~ (std:v:mag (std:v:norm $[40.19, 0.399]) * 10) == 10.0; ``` #### 3.8.6 - Unary Vector Operations Calling `-` on a vector returns a new vector with all of its fields negated. This is equivalent to multiplying the vector by `-1`. Calling `+` on a vector returns a copy of the exact same vector. This is equivalent to multiplying the vector by `1`. ```wlambda !my_vec = $f(1.2, 2.3, 3.4); std:assert_eq (ivec (-my_vec)) $i(-1, -2, -3); std:assert_eq (+my_vec) my_vec; # adding something to its inverse yields all 0s std:assert_eq[ my_vec + (-my_vec), my_vec * 0 ]; ``` #### 3.8.7 - is\_fvec _value_ Returns `$true` if _value_ is a float vector. ```wlambda std:assert_eq (is_fvec $f(1,2)) $true; std:assert_eq (is_fvec $f(1,2,3)) $true; std:assert_eq (is_fvec $f(1,2,3,4)) $true; std:assert_eq (is_fvec $none) $false; std:assert_eq (is_fvec $i(1,2)) $false; std:assert_eq (is_fvec $[3.4, 4.5]) $false; std:assert_eq (is_fvec fvec <& $[3.4, 4.5]) $true; # References are not dereferenced: std:assert_eq (is_fvec $&&$f(3,4)) $false; std:assert_eq (is_fvec $*$&&$f(3,4)) $true; ``` #### 3.8.8 - is\_ivec _value_ Returns `$true` if _value_ is an integer vector. ```wlambda std:assert_eq (is_ivec $i(1,2)) $true; std:assert_eq (is_ivec $i(1,2,3)) $true; std:assert_eq (is_ivec $i(1,2,3,4)) $true; std:assert_eq (is_ivec $none) $false; std:assert_eq (is_ivec $[3, 4]) $false; std:assert_eq (is_ivec ivec <& $[3.4, 4.5]) $true; # References are not dereferenced: std:assert_eq (is_ivec $&& $i(3,4)) $false; std:assert_eq (is_ivec $* $&& $i(3,4)) $true; ``` #### 3.8.9 - is\_nvec _value_ Returns `$true` if _value_ is either a numerical float or integer vector. ```wlambda std:assert_eq (is_nvec $i(1,2)) $true; std:assert_eq (is_nvec $i(1,2,3)) $true; std:assert_eq (is_nvec $i(1,2,3,4)) $true; std:assert_eq (is_nvec $f(1,2)) $true; std:assert_eq (is_nvec $f(1,2,3)) $true; std:assert_eq (is_nvec $f(1,2,3,4)) $true; std:assert_eq (is_nvec $none) $false; std:assert_eq (is_nvec $[3, 4]) $false; std:assert_eq (is_nvec fvec <& $[3.4, 4.5]) $true; std:assert_eq (is_nvec ivec <& $[3.4, 4.5]) $true; ``` #### 3.8.10 - nvec\_len _value_ Returns the length of a numerical vector, commonly known as the dimension. Either 2, 3 or 4. ```wlambda std:assert_eq (nvec_len $i(1, 2)) 2; std:assert_eq (nvec_len $i(1, 2, 3)) 3; std:assert_eq (nvec_len $i(1, 2, 3, 4)) 4; std:assert_eq (nvec_len $f(1.1, 2.2)) 2; std:assert_eq (nvec_len $f(1.1, 2.2, 3.3)) 3; std:assert_eq (nvec_len $f(1.1, 2.2, 3.3, 4.4)) 4; ``` #### 3.8.11 - fvec _value_ Will cast _value_ into a float vector. You can cast a multitude of data types into a float vector: ```wlambda std:assert_eq (fvec $[1,2,3,4]) $f(1,2,3,4); std:assert_eq (fvec $[1,2,3]) $f(1,2,3); std:assert_eq (fvec $[1,2]) $f(1,2); std:assert_eq (fvec $i(1,2)) $f(1,2); std:assert_eq (fvec $i(1,2,3)) $f(1,2,3); std:assert_eq (fvec $i(1,2,3,4)) $f(1,2,3,4); std:assert_eq (fvec $p("2", "3.4")) $f(2,3.4); !i = $iter $[] +> $p(3,4) +> $[5,6]; std:assert_eq (fvec i) $f(3,4); std:assert_eq (fvec i) $f(5,6); std:assert_eq (fvec ${x = 1, y = 2}) $f(1,2); std:assert_eq (fvec ${x = 1, y = 2, z = 3}) $f(1,2,3); std:assert_eq (fvec ${x = 1, y = 2, z = 3, w = 4}) $f(1,2,3,4); ``` #### 3.8.12 - fvec2 _value_ Like `fvec` but always returns a 2 dimensional vector. ```wlambda std:assert_eq (fvec2 $i(3,4,5)) $f(3,4); std:assert_eq (fvec2 $[4,5,6,7,8]) $f(4,5); std:assert_eq (fvec2 ${x = 1, y = 2, z = 3, w = 4}) $f(1,2); ``` #### 3.8.13 - fvec3 _value_ Like `fvec` but always returns a 3 dimensional vector. ```wlambda std:assert_eq (fvec3 $i(3,4,5)) $f(3,4,5); std:assert_eq (fvec3 $[4,5,6,7,8]) $f(4,5,6); std:assert_eq (fvec3 ${x = 1, y = 2, z = 3, w = 4}) $f(1,2,3); ``` #### 3.8.14 - fvec4 _value_ Like `fvec` but always returns a 4 dimensional vector. ```wlambda std:assert_eq (fvec4 $i(3,4,5)) $f(3,4,5,0); std:assert_eq (fvec4 $[4,5,6,7,8]) $f(4,5,6,7); std:assert_eq (fvec4 ${x = 1, y = 2, z = 3, w = 4}) $f(1,2,3,4); ``` #### 3.8.15 - ivec _value_ Will cast _value_ into a float vector. You can cast a multitude of data types into a float vector: ```wlambda std:assert_eq (ivec $[1,2,3,4]) $i(1,2,3,4); std:assert_eq (ivec $[1,2,3]) $i(1,2,3); std:assert_eq (ivec $[1,2]) $i(1,2); std:assert_eq (ivec $f(1.1,2.1)) $i(1,2); std:assert_eq (ivec $f(1.1,2.1,3.2)) $i(1,2,3); std:assert_eq (ivec $f(1.1,2.1,3.2,4)) $i(1,2,3,4); std:assert_eq (ivec $p("2", "3")) $i(2,3); !i = $iter $[] +> $p(3,4) +> $[5,6]; std:assert_eq (ivec i) $i(3,4); std:assert_eq (ivec i) $i(5,6); std:assert_eq (ivec ${x = 1, y = 2}) $i(1,2); std:assert_eq (ivec ${x = 1, y = 2, z = 3}) $i(1,2,3); std:assert_eq (ivec ${x = 1, y = 2, z = 3, w = 4}) $i(1,2,3,4); ``` #### 3.8.16 - ivec2 _value_ Like `ivec` but always returns a 2 dimensional vector. ```wlambda std:assert_eq (ivec2 $f(3,4,5)) $i(3,4); std:assert_eq (ivec2 $[4,5,6,7,8]) $i(4,5); std:assert_eq (ivec2 ${x = 1, y = 2, z = 3, w = 4}) $i(1,2); ``` #### 3.8.17 - ivec3 _value_ Like `ivec` but always returns a 3 dimensional vector. ```wlambda std:assert_eq (ivec3 $f(3,4,5)) $i(3,4,5); std:assert_eq (ivec3 $[4,5,6,7,8]) $i(4,5,6); std:assert_eq (ivec3 ${x = 1, y = 2, z = 3, w = 4}) $i(1,2,3); ``` #### 3.8.18 - ivec4 _value_ Like `ivec` but always returns a 4 dimensional vector. ```wlambda std:assert_eq (ivec4 $f(3,4,5)) $i(3,4,5,0); std:assert_eq (ivec4 $[4,5,6,7,8]) $i(4,5,6,7); std:assert_eq (ivec4 ${x = 1, y = 2, z = 3, w = 4}) $i(1,2,3,4); ``` #### 3.8.19 - std:v:dims _vec_ You can use this function to retrieve the number of dimensions in _vec_. Like most other std:v functions, it will coerce whatever value is passed into it into a `ivec`, if that value is not a `fvec`. This function always returns an integer, regardless of whether an `ivec` or `fvec` is passed in. ```wlambda # the least number of dimensions a vector can have is 2. std:assert_eq (std:v:dims $[]) 2; # while the most is 4. std:assert_eq (std:v:dims ${w=0}) 4; std:assert_eq (std:v:dims $f(1,2)) (std:v:dims $i(1,2)); ``` #### 3.8.20 - std:v:mag2 _vec_ Returns the magnitude of _vec_, squared. Calculating the squared magnitude is a little bit faster, so you should prefer this method where performance is paramount. The magnitude is always a float, regardless of whether the parameter is an `ivec` or `fvec`. ```wlambda std:assert_eq (std:v:mag2 ${w=4}) 16.0; ``` #### 3.8.21 - std:v:mag _vec_ Returns the magnitude (also known as the length) of _vec_. The magnitude is always a float, regardless of whether the parameter is an `ivec` or `fvec`. ```wlambda std:assert_eq (std:v:mag ${w=4}) 4.0; ``` #### 3.8.22 - std:v:norm _vec_ Returns a new vector which has a magnitude of `1`, but points in the same direction as _vec_. Vectors with a length of one are also known as unit vectors. Note that this still returns an `ivec` when used on `ivec`s, meaning that when used on an `ivec2` only four values are possible: - `$i(1, 0)` - `$i(-1, 0)` - `$i(0, 1)` - `$i(0, -1)` These are the only `ivec2`s that have a length of `1`. ```wlambda !p1 = fvec ${ x = 20, y = 30.5 }; !p2 = fvec ${ x = -10, y = 0.5 }; # get the delta representing how far you'd have to travel to get from p1 to p2 !delta = p2 - p1; # the normalized delta represents a single 1 sized step you could take to get to p2 from p1. !n = std:v:norm delta; # the length of this step is reflected in the magnitude of the vectors std:assert_eq[ (std:v:mag delta) - 1, std:v:mag (p1 + n) - p2 ]; ``` #### 3.8.23 - std:v:dot _vec1_ _vec2_ Returns the sum of all components after multiplying each component in _vec1_ with the corresponding component of _vec2_. This can be used to represent the "sameness" of two vectors (especially unit vectors): the degree to which they are pointing in the same direction. Returns an integer when used on an `ivec`, and a float when used on an `fvec`. If _vec1_ is an `fvec`, then _vec2_ will also be coerced into one. If _vec1_ isn't an `fvec`, then it's coerced into an `ivec`, just like the other `std:v` functions. ```wlambda !at = fvec ${ x = 20 , y = 30.5 }; # where you're at !goal = fvec ${ x = -10, y = 0.5 }; # where you want to look !looking = std:v:rad2vec (std:num:to_radians 90); # direction you're looking in # do you need to turn left or right to look at `goal`, # if you're standing at `at` looking in `looking`? # find the unit vector representing the space between where you want to look and where you're at. !delta = std:v:norm goal - at; # the direction you need to turn in can be found by checking the sign of # the dot product of where you're currently looking and where you're at. !dir = std:v:dot delta looking; std:assert_eq[ (dir < 0) "left" "right", "left" ]; ``` #### 3.8.24 - std:v:cross _vec1_ _vec2_ Returns a vector perpendicular to _vec1_ and _vec2_. Similar to the dot product, but instead of returning a single value it returns another vector, and is only useful in three (and seven, but WLambda's vectors don't support so many) dimensions. Regardless of the number of dimensions in the input vectors, this function will return a 3d vector. ```wlambda !x = fvec ${x=1}; !y = fvec ${y=1}; # the cross product of these two values will represent the third axis, z, and will be # perpendicular to both other vectors. !z = std:v:cross x y; std:assert_eq z (fvec ${z=1}); # because all three vectors are perpindicular, they'll all have the same dot product from each other. std:assert_eq[(std:v:dot x y), (std:v:dot y z)]; std:assert_eq[(std:v:dot y z), (std:v:dot z x)]; ``` #### 3.8.25 - std:v:lerp _vec1_ _vec2_ _t_ `lerp` stands for linear interpolation. This function is useful when animating positions, whereas slerp is useful for animating rotations. Creates a new vector in a new position relative to _vec1_ and _vec2_. Aside from the two reference vectors, this function also takes a variable, _t_, which represents how far relative to the first and second vector the new vector should be. If _t_ is `0`, _vec1_ is returned. If _t_ is `1`, then _vec2_ is returned. If _t_ is `0.5`, the resulting vector will be halfway in between the first and second vector. ```wlambda std:assert_eq[ std:v:lerp $f(1,0) $f(0,1) 0.5 , $f(0.5, 0.5) ]; std:assert_eq[ std:v:lerp $f(5,10) ${y=10} 0.75 , $f(1.25, 10) ]; std:assert_eq[ std:v:lerp $[-2,5] $[2,-5] 0.5 , $i(0, 0) ]; !a = $f(83, -49.5); std:assert_eq[ (std:v:mag a) / 2 , std:v:mag (std:v:lerp a $[] 0.5) ]; std:assert_eq[ (std:v:mag a) * 2 , std:v:mag (std:v:lerp $f(0,0) a 2.0) ]; !b = $f(-484.58, -19); std:assert_eq[ std:v:lerp b a 1.5 , std:v:lerp a b -0.5 ]; ``` #### 3.8.26 - std:v:slerp _vec1_ _vec2_ _t_ `slerp` stands for spherical linear interpolation. This function is useful when animating rotations, whereas lerp is useful for animating positions. In most cases, you'll want to pass in unit vectors representing rotations to slerp. You should get back unit vectors in the vast majority of cases, but if perfect accuracy is required normalizing the output of this function is suggested. Creates a new vector in a new position relative to _vec1_ and _vec2_. Aside from the two reference vectors, this function also takes a variable, _t_, which represents how far relative to the first and second vector the new vector should be. If _t_ is `0`, _vec1_ is returned. If _t_ is `1`, then _vec2_ is returned. If _t_ is `0.5`, the resulting vector will be halfway in between _vec1_ and _vec2_. ```wlambda # compare this to the one for std:v:lerp! note that the length of this one is almost 1. # this is definitely not the case for std:v:lerp's output with the same input. !v = std:v:slerp $f(1,0) $f(0,1) 0.5; # the values may not be exact because of floating point rounding errors, # but they should be pretty close. std:assert_rel_eq v.x 0.7071067811865476 0.000001; std:assert_rel_eq v.y 0.7071067811865476 0.000001; # The values are interpolated around a circle, so if you raise t high enough you'll start # getting the same values as you get with a lower t, although not quite because of float rounding. !half = (std:v:slerp $f(1,0) $f(0,1) 0.5); !four = (std:v:slerp $f(1,0) $f(0,1) 4.5); std:assert_rel_eq half.x four.x 0.000001; std:assert_rel_eq half.y four.y 0.000001; ``` #### 3.8.27 - std:v:vec2rad _vec_ Creates a rotation in radians from the x and y components of _vec_. Always returns a float. Coerces the argument into an `ivec` unless it's a `fvec`. ```wlambda std:assert_eq[ std:num:to_degrees (std:v:vec2rad ${x=1}) , 0.0 ]; std:assert_eq[ std:num:to_degrees (std:v:vec2rad ${y=1}) , 90.0 ]; # halfway in between 0.0 and 90.0 should be 45. # note that lerp would work here as well !h = std:v:slerp $f(1, 0) $f(0, 1) 0.5; std:assert_eq[ std:num:to_degrees (std:v:vec2rad h) , 45.0 ]; ``` #### 3.8.28 - std:v:rad2vec _radians_ Creates a unit vector from _radians_. Always returns an `fvec`. ```wlambda std:assert_eq[ std:v:rad2vec (std:num:to_radians 0.0) , $f(1, 0)]; std:assert_eq[ ivec (std:v:rad2vec (std:num:to_radians 90.0)), $i(0, 1)]; # halfway in between 0.0 and 90.0 should be 45. # note that lerp would NOT work here as well, rad2vec returns a unit vector. !h = std:v:slerp $f(1, 0) $f(0, 1) 0.5; # slerp because rotations !r = std:v:rad2vec (std:num:to_radians 45.0); std:assert_rel_eq r.x h.x 0.0001; std:assert_rel_eq r.y h.y 0.0001; ``` ### 3.9 - Characters and Bytes WLambda has a data type for single characters and bytes. The lexical syntax is a character or escape sequence delimited by `'`: ```wlambda std:assert_eq (type 'a') "char"; std:assert_eq (type $b'a') "byte"; std:assert_eq (type '\u{40}') "char"; std:assert_eq (type $b'\x40') "byte"; # You can use the unicode escapes up to the first 256 code points in bytes too: std:assert_eq (char $b'\u{40}') '\u{40}'; # Beyond that, you will get the byte '?': std:assert_eq (char $b'\u{3131}') '?'; ``` The can be used interchangeably almost everywhere. They can often also be used instead of a string, because they are handled like a single character long string. They are useful because they do not require an extra allocation in the background. They are not boxed like strings: ```wlambda std:assert_eq ("foo" $p(0, 1)) "f"; # requires allocation std:assert_eq ("foo".0) 'f'; # requires NO allocation ``` #### 3.9.1 - byte _value_ Converts the _value_ to a byte. If _value_ is a number, it must be below or equal to 255, otherwise it will result in the byte `'?'`. ```wlambda std:assert_eq (byte 64) $b'@'; std:assert_eq (byte 300) $b'?'; std:assert_eq (byte "ABC") $b'A'; std:assert_eq (byte $b"\xFF\xF0") $b'\xFF'; std:assert_eq (byte "\xFF\xF0") $b'\xC3'; # first byte of an utf-8 sequence! ``` #### 3.9.2 - char _value_ Converts the _value_ to a Unicode character. ```wlambda std:assert_eq (char $b'\xFF') 'ÿ'; std:assert_eq (char 0x262F) '☯'; std:assert_eq (char "☯xyz") '☯'; ``` #### 3.9.3 - is\_byte _value_ Checks if _value_ is of the byte data type. ```wlambda std:assert (is_byte $b'X'); std:assert not[is_byte 'X']; std:assert not[is_byte 123]; std:assert (is_byte $b"abc".0); std:assert not[is_byte "abc".0]; std:assert not[is_byte $b"abc"]; ``` #### 3.9.4 - is\_char _value_ Check if _value_ is of the Unicode character data type. ```wlambda std:assert (is_char 'X'); std:assert not[is_char $b'X']; std:assert not[is_char 123]; std:assert (is_char "abc".0); std:assert not[is_char $b"abc".0]; std:assert not[is_char $b"abc"]; std:assert not[is_char "abc"]; ``` #### 3.9.5 - std:char:to\_lowercase _value_ Turns the _value_ into a lower case Unicode character. ```wlambda std:assert_eq (std:char:to_lowercase 'A') 'a'; std:assert_eq (std:char:to_lowercase 65) 'a'; ``` #### 3.9.6 - std:char:to\_uppercase _value_ Turns the _value_ into an upper case Unicode character. ```wlambda std:assert_eq (std:char:to_uppercase 'a') 'A'; std:assert_eq (std:char:to_uppercase 97) 'A'; ``` ### 3.10 - Strings Strings in WLambda are like Rust UTF-8 encoded immutable Unicode strings. There are two types of literal forms for strings: ```wlambda "abc def \"foo\""; std:assert_eq $q/any delimiter may be used instead of/ "any delimiter may be used instead of"; # Unicode escapes are also working: std:assert_eq "\u{2211}" "∑"; ``` #### 3.10.1 - String Literal Syntaxes There are multiple kinds of syntax constructs you can use to notate string (and byte vector) literals: - Regular strings ```wlambda !s = "a b c"; std:assert_eq s "a b c"; ``` - Byte vectors `$b"\x02FOO\x03"` - Quoted strings `$q(123433)` - Quoted byte vectors `$Q(XZY)` - WLambda code strings ```wlambda # Short form $c works too. !code = $code { !this = is a block; It just needs to be in valid WLambda[:Syntax]; .x = But it does not need to pass the compiler phase.x; }; # Primary use case is `eval` and `std:thread:spawn`: !v = (std:thread:spawn $code { !@import std std; !res = "x" "y" "z"; std:str:cat res 33; }).join[]; std:assert_eq v "xyz33"; ``` #### 3.10.2 - str _value_ Casts _value_ to a string and returns it. Also dereferences a value. ```wlambda std:assert_eq (str "\xFF") "ÿ"; std:assert_eq (str "\x0A") "\n"; std:assert_eq (str 1) "1"; std:assert_eq (str $n) ""; std:assert_eq (str $t) "$true"; std:assert_eq (str $f) "$false"; std:assert_eq (str $&10) "10"; std:assert_eq (str $&&10) "10"; std:assert_eq (str ${a=10}) "${a=10}"; std:assert_eq (str $[1,2,3]) "$[1,2,3]"; std:assert_eq (str $o(42)) "42"; std:assert_eq (str $o()) ""; !x = $&&10; std:assert_eq (str ~ std:ref:weaken x) "10"; ``` #### 3.10.3 - std:write\_str _value_ Writes a WLambda syntax representation of the given _value_ to a string. This is useful for debugging purposes or in combination with `std:eval`. ```wlambda std:assert_eq (std:write_str "foo") "\"foo\""; std:assert_eq (std:write_str $&&10) "$&&10"; std:assert_eq (std:write_str $[1, 2, 3]) "$[1,2,3]" ``` Here an example in combination with `std:eval`: ```wlambda !code = std:str:cat "$@i iter i " (std:write_str $[1, 2, 3, 4]) " { $+ i }"; std:assert_eq (std:eval code) 10; ``` #### 3.10.4 - is\_str _value_ Returns `$true` if _value_ is a string. ```wlambda std:assert ~ is_str "foo"; std:assert ~ not ~ is_str $b"foo"; std:assert ~ not ~ is_str :foo; std:assert ~ not ~ is_str 324; std:assert ~ not ~ is_str $&&"foo"; std:assert ~ is_str $*$&&"foo"; ``` #### 3.10.5 - std:str:cat _a_ _b_ ... Stringifies (like with `str`) and concatenates all its arguments. If an argument is a vector, it's elements will be stringified and concatenated. ```wlambda std:assert_eq (std:str:cat :a 10 23.2 "ab" "cd" $[1, 2, 3]) "a1023.2abcd123"; ``` If a vector argument is given, it's elements are stringified, thats useful if you prepare substrings to be concatenated in one single action: ```wlambda !out = $[]; std:push out "abc"; std:push out "123"; std:push out "XXX"; !s = std:str:cat out; std:assert_eq s "abc123XXX"; ``` #### 3.10.6 - std:str:join _sep_ _vector_ Join's the stringified elements of _vector_ with the _sep_ string. Will return an error if _vector_ is not a vector. ```wlambda std:assert_eq (std:str:join "::" $[1,2,3]) "1::2::3"; ``` #### 3.10.7 - std:str:len _value_ Returns the length of the stringified _value_ in unicode characters. The core function `len` does return the number of bytes in the string instead. ```wlambda std:assert_eq (len "∑") 3; std:assert_eq (std:str:len "∑") 1; std:assert_eq (len "∑ÄÄ") 7; std:assert_eq (std:str:len "∑ÄÄ") 3; std:assert_eq (len "abcd") 4; std:assert_eq (std:str:len "abcd") 4; ``` #### 3.10.8 - std:str:find _pattern_ _string_ [_offset_] Searches for the string _pattern_ in the _string_ and returns the 0 based position in the string the given _pattern_ starts. If no pattern was found `$none` is returned. ```wlambda std:assert_eq (std:str:find "xyz" "abcxyz") 3; std:assert_eq (std:str:find "xyz" "abcxyzxyz" 6) 6; std:assert_eq (std:str:find "xyz" "abcxyzfooxyz" 6) 9; ``` #### 3.10.9 - std:str:replace _pattern_ _replacement_ _string_ Replaces every occurence of _pattern_ in _string_ with _replacement_ and returns a new string. All values will be casted to a string if they aren't. ```wlambda !s = std:str:replace "dog" "cat" "I really like my dog, because when you dog, you can put dog in the dog!"; std:assert_eq s "I really like my cat, because when you cat, you can put cat in the cat!"; !s = std:str:replace "9" "1" "9999"; std:assert_eq s "1111"; ``` #### 3.10.10 - std:str:replace\_n _pattern_ _replacement_ _count_ _string_ Replaces _count_ occurences of _pattern_ in _string_ with _replacement_ and returns a new string. All values will be casted to a string if they aren't. ```wlambda !s = std:str:replace_n "dog" "cat" 2 "I really like my dog, because when you dog, you can put dog in the dog!"; std:assert_eq s "I really like my cat, because when you cat, you can put dog in the dog!"; !s = std:str:replace_n "9" "1" 3 "9999"; std:assert_eq s "1119"; ``` #### 3.10.11 - std:str:trim _value_ Trims off any (unicode) white space from the start and end of the stringified _value_. ```wlambda std:assert_eq (std:str:trim "\nfooo bar ") "fooo bar"; ``` #### 3.10.12 - std:str:trim\_start _value_ Trims off any (unicode) white space from the start of the stringified _value_. ```wlambda std:assert_eq (std:str:trim_start " \nfooo bar \n") "fooo bar \n"; ``` #### 3.10.13 - std:str:trim\_end _value_ Trims off any (unicode) white space from the end of the stringified _value_. ```wlambda std:assert_eq (std:str:trim_end " \nfooo bar \n") " \nfooo bar"; ``` #### 3.10.14 - std:str:pad\_start _len_ _pad-str_ _value_ Pads the stringified _value_ by _pad-str_ up to _len_ characters, inserting at the start of the string. The output string is guaranteed to be exactly _len_ unicode characters long and not longer. If _pad-str_ is empty, nothing is done. ```wlambda std:assert_eq (std:str:pad_start 2 "Ä" "0") "Ä0"; std:assert_eq (std:str:pad_start 5 "∑∑∑" "∑∑") "∑∑∑∑∑"; std:assert_eq (std:str:pad_start 8 "Ä∑ßs" "∑∑") "ßsÄ∑ßs∑∑"; # Empty _pad-str_ is not an error but a nop: std:assert_eq (std:str:pad_start 8 "" "∑∑") "∑∑"; # also works with characters std:assert_eq (std:str:pad_start 3 'x' "0") "xx0"; # also works with bytes std:assert_eq (std:str:pad_start 3 $b'x' "0") "xx0"; ``` #### 3.10.15 - std:str:pad\_end _len_ _pad-str_ _value_ Pads the stringified _value_ by _pad-str_ up to _len_ characters, appending at the end. The output string is guaranteed to be exactly _len_ unicode characters long and not longer. If _pad-str_ is empty, nothing is done. ```wlambda std:assert_eq (std:str:pad_end 2 "Ä" "0") "0Ä"; std:assert_eq (std:str:pad_end 5 "∑∑∑" "∑∑") "∑∑∑∑∑"; std:assert_eq (std:str:pad_end 8 "Ä∑ßs" "∑∑") "∑∑Ä∑ßsÄ∑"; # Empty _pad-str_ is not an error but a nop: std:assert_eq (std:str:pad_end 8 "" "∑∑") "∑∑"; # also works with characters std:assert_eq (std:str:pad_end 3 'x' "0") "0xx"; # also works with bytes std:assert_eq (std:str:pad_end 3 $b'x' "0") "0xx"; ``` #### 3.10.16 - std:str:to\_bytes _string_ Encodes _string_ in UTF-8 and returns a byte vector containing all it's bytes. ```wlambda !b = std:str:to_bytes "1234"; std:assert_eq b $b"1234"; !b = std:str:to_bytes "Äß日本人"; std:assert_eq b $b"\xC3\x84\xC3\x9F\xE6\x97\xA5\xE6\x9C\xAC\xE4\xBA\xBA"; ``` #### 3.10.17 - std:str:to\_bytes\_latin1 _string_ Encodes _string_ as bytes in Latin1 (ISO-8859-1) encoding and returns a byte vector containing all it's bytes. If a character is outside the Latin1 Unicode range, it will be replaced by a "?". ```wlambda !b = std:str:to_bytes_latin1 "\u{FF}\u{F0}"; std:assert_eq b $b"\xFF\xF0"; !b = std:str:to_bytes_latin1 "\u{FE00}\u{FF}\u{3232}"; std:assert_eq b $b"?\xFF?"; ``` #### 3.10.18 - std:str:from\_latin1 _byte-vector_ Converts the _byte-vector_ to a Unicode string, assuming Latin 1 (ISO-8859-1) encoding and returns it. ```wlambda !s = std:str:from_latin1 $b"Ä"; std:assert_eq s "\u{C3}\u{84}"; !s = std:str:from_latin1 $b"\xFF\xF0"; std:assert_eq s "\u{FF}\u{F0}"; ``` #### 3.10.19 - std:str:from\_utf8 _byte-vector_ Converts the _byte-vector_ to a Unicode string and returns it. If the _byte-vector_ contains invalid UTF-8 sequences an error value is returned. ```wlambda !s = _? ~ std:str:from_utf8 $b"\xC3\x84\xC3\x9F\xE6\x97\xA5\xE6\x9C\xAC\xE4\xBA\xBA"; std:assert_eq s "Äß日本人"; !r = on_error {|| _ } ~ std:str:from_utf8 $b"\xFF\xFF"; std:assert_eq r "str:from_utf8 decoding error: invalid utf-8 sequence of 1 bytes from index 0"; ``` #### 3.10.20 - std:str:from\_utf8\_lossy _byte-vector_ Converts the _byte-vector_ to a Unicode string and returns it. If the _byte-vector_ contains invalid UTF-8 sequences a `"�"` will be inserted. ```wlambda !s = _? ~ std:str:from_utf8_lossy $b"\xC3\x84\xFF\xC3\x9F\xE6\x97\xA5\xE6\x9C\xAC\xE4\xBA\xBA\xFF\xFF\x00"; std:assert_eq s "Ä�ß日本人��\0"; ``` #### 3.10.21 - std:str:to\_char\_vec _string_ Converts the _string_ into a vector of integers which represent the Unicode character number. ```wlambda !v = std:str:to_char_vec "1234"; std:assert_eq (str v) ~ str $[49,50,51,52]; !v = std:str:to_char_vec "Äß日本人"; std:assert_eq (str v) ~ str $[196,223,0x65E5,0x672C,0x4EBA]; ``` #### 3.10.22 - std:str:from\_char\_vec _vector_ The reverse operation of `std:str:to_char_vec`. It converts a vector of integers to a unicode string. Any integer that has no associated Unicode character will be converted to `"?"`. ```wlambda std:assert_eq (std:str:from_char_vec $[9999999999]) "?"; std:assert_eq (std:str:from_char_vec $[49,50,196,223,0x65E5,0x672C,0x4EBA]) "12Äß日本人"; ``` #### 3.10.23 - std:str:to\_lowercase _string_ Swaps all (Unicode) characters in _string_ to their lowercase version. ```wlambda std:assert_eq (std:str:to_lowercase "ZABzabÄßÜÖ") "zabzabäßüö"; ``` #### 3.10.24 - std:str:to\_uppercase _string_ Swaps all (Unicode) characters in _string_ to their lowercase version. ```wlambda std:assert_eq (std:str:to_uppercase "ZABzabäßüö") "ZABZABÄSSÜÖ"; ``` #### 3.10.25 - std:str:edit\_distance _str-a_ _str\_b Calculates the Levenshtein distance between two (Unicode) strings. ```wlambda std:assert_eq (std:str:edit_distance "aaa" "aba") 1; ``` ### 3.11 - Byte Vectors Bytes (plural of Byte) are a vector of bytes. Unlike strings they don't have any encoding. Literal syntax however supports inserting unicode characters: ```wlambda $b"abc"; $b"\xFF\xFD\x00"; $Q/ABCDEF\xFD/; # \xFD is not an escape sequence here! ``` #### 3.11.1 - Call Properties of Bytes You can index inside a byte array by calling it with an integer: ```wlambda std:assert_eq ($b"ABC" 1) $b'B'; ``` You can extract a whole range when calling with 2 integers: ```wlambda 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: ```wlambda !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 ``` If you call bytes with a pair as argument, you can do a multitude of operations, from replacement to finding a byte: ```wlambda # replacing substrings: std:assert_eq ($b"a,b,c,d" $p($b',', $b';')) $b"a;b;c;d"; std:assert_eq ($b"a,b,c,d" $p($b"a,", $b"XXX")) $b"XXXb,c,d"; # also works with strings and chars: std:assert_eq ($b"a,b,c,d" $p("a,", "XXX")) $b"XXXb,c,d"; std:assert_eq ($b"a,b,c,d" $p("a,", 'O')) $b"Ob,c,d"; # finding a character/byte: std:assert_eq ($b"a,b,c,d" $p(0, $b'c')) 4; std:assert_eq ($b"a,b,c,d" $p(0, 'c')) 4; # splitting: std:assert_str_eq ($b"A\B\C" $p($b'\', 0)) $[$b"A", $b"B", $b"C"]; ``` See also the section [Calling Semantics of Data Types](#319-calling-semantics-of-data-types). #### 3.11.2 - Byte Conversion Functions You can convert bytes to strings in a multitude of ways: - str _bytes_ ```wlambda 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_]] ```wlambda 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\_latin1 _bytes_ ```wlambda std:assert_eq (std:str:from_latin1 $b"\xFF\xF0") "\u{FF}\u{F0}"; ``` - std:str:from\_utf8 _bytes_ ```wlambda 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_ ```wlambda 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: ```wlambda !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`: ```wlambda std:assert_eq (std:bytes:from_hex ~ std:bytes:to_hex $b"ABC") $b"ABC"; ``` #### 3.11.3 - is\_bytes _value_ Returns `$true` if _value_ is a byte vector. ```wlambda std:assert ~ is_bytes $b"ABC"; std:assert ~ not ~ is_bytes "ABC"; ``` #### 3.11.4 - std:bytes:find _pattern_ _string_ [_offset_] Searches for the string _pattern_ in the _string_ and returns the 0 based position in the string the given _pattern_ starts. If no pattern was found `$none` is returned. ```wlambda std:assert_eq (std:bytes:find $b"xyz" $b"abcxyz") 3; std:assert_eq (std:bytes:find $b"xyz" $b"abcxyzxyz" 6) 6; std:assert_eq (std:bytes:find $b"xyz" $b"abcxyzfooxyz" 6) 9; ``` #### 3.11.5 - std:bytes:replace _byte-vector_ _pattern_ _replacement_ Replaces all occurences of _pattern_ in _byte-vector_ with _replacement_. ```wlambda std:assert_eq (std:bytes:replace $b"XXX\x01\x02\x03OOO" $b"\x01\x02\x03" $b"---") $b"XXX---OOO"; std:assert_eq (std:bytes:replace $b"XXX\x01\x02\x03OOO" $b"\x01\x02\x03" $b"") $b"XXXOOO"; std:assert_eq (std:bytes:replace $b"XXX\x01\x02\x03OOO" $b"\x01\x02\x03" $b"\xFF\xFF\xFF\xFF") $b"XXX\xFF\xFF\xFF\xFFOOO"; ``` #### 3.11.6 - std:bytes:from\_hex _string-with-hex-chars_ This function decodes a string of hex characters into a byte vector. ```wlambda !bv = std:bytes:from_hex "62797465"; std:assert_eq bv $b"byte"; ``` #### 3.11.7 - std:bytes:from\_vec _vector-of-ints_ Decodes a vector of integers in the range 0-255 into a byte vector. If an integer is larger than 255 don't expect a sensible result. But it will most likely just wrap around. ```wlambda std:assert_eq (std:bytes:from_vec $[1,2,3,0x62,0x79,0x74,0x65]) $b"\x01\x02\x03byte"; ``` #### 3.11.8 - std:bytes:to\_hex _byte-vector_ Converts the given byte vector to a string of hex encoded bytes. ```wlambda std:assert_eq (std:bytes:to_hex $b"byte") "62797465"; ``` #### 3.11.9 - std:bytes:to\_base64 _byte-vector_ [_config_] Converts the given byte vector to a Base64 encoded string. With _config_ you can define the encoding style: - `:std` - `:std_no_pad` - `:url` - `:url_no_pad` ```wlambda std:assert_eq (std:bytes:to_base64 $b"\x00\xFF") "AP8="; std:assert_eq (std:bytes:to_base64 "test") "dGVzdA=="; std:assert_eq (std:bytes:to_base64 "test" :std_no_pad) "dGVzdA"; std:assert_eq (std:bytes:from_base64 (std:bytes:to_base64 "test")) $b"test"; std:assert_eq (std:bytes:from_base64 (std:bytes:to_base64 "test" :url) :url) $b"test"; ``` #### 3.11.10 - std:bytes:from\_base64 _byte-vector_ [_config_] Converts the given byte vector to a Base64 encoded string. With _config_ you can define the encoding style, see also `to_base64` for a list of possible values. ```wlambda std:assert_eq (std:bytes:from_base64 "AP8=") $b"\x00\xFF"; std:assert_eq (std:bytes:from_base64 "dGVzdA") $b"test"; ``` #### 3.11.11 - std:bytes:to\_vec _byte-vector_ Converts the given byte vector to a vector of integers in the range 0-255. ```wlambda std:assert_str_eq (std:bytes:to_vec $b"byte") $[98, 121, 116, 101]; ``` #### 3.11.12 - std:bytes:pack _pack-format-string_ _list-of-values_ Returns a byte vector containing the values of _list-of-values_ serialized in binary form (packed) according to the given _pack-format-string_. If the syntax of the _pack-format-string_ has errors, an error value is returned. See also the section [Format String Syntax](#133-format-string-syntax-for-stdbytespack-and-stdbytesunpack) for a description of the syntax for _pack-format-string_. This function is very useful for constructing binary data for file formats and network protocols. ```wlambda std:assert_eq (std:bytes:pack "> i16 x s8 x f" $[1, "test", 0.5]) $b"\0\x01\0\x04test\0?\0\0\0"; ``` #### 3.11.13 - std:bytes:unpack _pack-format-string_ _byte-vector_ Decodes the given _byte-vector_ according to the _pack-format-string_ and returns it as list of values. If the syntax of the _pack-format-string_ has errors or the given _byte-vector_ is too short, an error value is returned. See also the section [Format String Syntax](#133-format-string-syntax-for-stdbytespack-and-stdbytesunpack) for a description of the syntax for _pack-format-string_. ```wlambda std:assert_str_eq (std:bytes:unpack "< i16 x c3 s16 x y" $b"\x10\x00\x00ABC\x02\x00XY\x00This is the rest") $[16, $b"ABC", $b"XY", $b"This is the rest"]; ``` ### 3.12 - Symbols Symbols are a special kind of strings that are interned by the runtime. That means, comparing two symbols is an O(1) operation and not an O(n) operation on the length of the string. Symbols are also used as keys for maps. Use them however you see fit. They will do a key lookup (on maps, vectors (as indices) and user values) if they are called with an argument. ```wlambda std:assert_eq (:1 $[1,2,3]) 2; std:assert_eq (:a ${a=30}) 30; ``` They are basically the same as string, but strings have slightly different calling semantics and a different literal syntax. Often you can use them as shortform literal in places where a string is expected: ```wlambda std:assert_eq (std:str:replace :A :a "All AbabA") "all ababa"; ``` They can be very useful as sentinel values or custom enums: ```wlambda !x = :ON; !y = :OFF; std:assert_eq ((x == :ON) { 10 }) 10; # They don't match with strings: std:assert_eq ((x == "ON") { 10 } { 20 }) 20; # Work together nicely with `match`: !state = ""; match x :ON => { .state = "is on" } :OFF => { .state = "is off" }; std:assert_eq state "is on"; match y :ON => { .state = "is on" } :OFF => { .state = "is off" }; std:assert_eq state "is off"; ``` Keep in mind, that all symbols are interned strings. And if you create many symbols that are not used anymore, you might need to trigger a cleanup with `std:symbols::collect`. The collection of dead symbols is also run automatically for every 100th newly allocated symbol. #### 3.12.1 - sym _value_ Casts the given _value_ into a symbol. ```wlambda std:assert_eq (sym "a") :a; std:assert_eq (sym $b"a") :a; std:assert_eq (sym $[]) :"$[]"; std:assert_eq (sym 10) :10; ``` #### 3.12.2 - is\_sym _value_ Returns `$true` if the _value_ is symbol. ```wlambda std:assert ~ is_sym :a; std:assert ~ is_sym ~ sym "a"; std:assert ~ is_sym ~ sym "a"; std:assert ~ is_sym ~ sym $b"a"; std:assert ~ not ~ is_sym "a"; std:assert ~ not ~ is_sym $b"a"; std:assert ~ not ~ is_sym $&&:a; std:assert ~ not ~ is_sym ${}; std:assert ~ not ~ is_sym $none; std:assert ~ not ~ is_sym $true; ``` #### 3.12.3 - std:symbols:collect Collect and remove all interned symbols in the current thread that are no longer used. Returns the number of freed symbols. Please keep in mind, that the `std:ref_id` of any collected symbol will be different from a symbol that is created later with the same characters. The collection of dead symbols is also run automatically for every 100th newly allocated symbol. If you rely on the reference ID of a symbol, you should make sure to keep it around. Literal symbols are always kept around as long as the code is running or referenced somewhere (eg. by a function). ```wlambda std:symbols:collect[]; !probably_unique_sym = sym "onceonly_used"; std:assert_eq (std:ref_id ~ sym "onceonly_used") (std:ref_id probably_unique_sym); std:assert_eq std:symbols:collect[] 0; .probably_unique_sym = $none; std:assert_eq std:symbols:collect[] 1; ``` ### 3.13 - Syntax `$%:Block` A syntax element is an element of an abstract syntax tree as it is returned by `std:wlambda:parse` for instance. They carry the type of syntax node and debug information with them. #### 3.13.1 - std:syn:pos _syntax_ Returns the position of the syntax element in the source code. ```wlambda !ast = std:wlambda:parse "1 + 2"; !add_syntax = ast.1.0; std:assert_str_eq (std:syn:pos add_syntax) $["", 1, 3]; ``` #### 3.13.2 - std:syn:type _syntax_ Converts the syntax element to a symbol, so you can determine it's type: ```wlambda !ast = std:wlambda:parse "1 + 2"; !add_syntax = ast.1.0; std:assert_str_eq (std:syn:type add_syntax) :BinOpAdd; ``` ### 3.14 - Pairs `$p(a, b)` A pair is an immutable tuple of 2 values. You can use it for returning two values from a function as it is a slight bit slimmer than a vector with two values. Unlike a vector, pairs are compared by `==` according to their contents and not by referencial equality. There are two ways to form a pair: - Pair value syntax: `$p(a, b)` - Pair right associative operator: `a => b` You can access the pair values by the following keys: ```wlambda !v = $p(11, 12); std:assert_eq v.0 11; std:assert_eq v.1 12; std:assert_eq (0 v) 11; std:assert_eq (1 v) 12; std:assert_eq v.car 11; std:assert_eq v.cdr 12; std:assert_eq v.head 11; std:assert_eq v.tail 12; std:assert_eq v.first 11; std:assert_eq v.second 12; # Pairs are often used to represent map entries, # so you can use `key` and `value` # and the short forms `k` and `v` too: std:assert_eq v.value 11; std:assert_eq v.key 12; std:assert_eq v.v 11; std:assert_eq v.k 12; ``` Comparison does happen by their contents: ```wlambda std:assert $p(1, 2) == $p(1, 2); std:assert $p(2, 2) != $p(1, 2); # In contrast to vectors: std:assert not ~ $[1, 2] == $[1, 2]; ``` The index is wrapping around, that means `$p(a, b).2` is the first element again: ```wlambda !v = $p(33, 44); !l = $[]; iter i $i(0, 4) ~ std:push l v.(i); std:assert_eq (str l) (str $[33, 44, 33, 44]); ``` A pair is a referencial data type, that means you can use `std:ref_id` on it: ```wlambda !a = $p(1, 2); !b = $p(2, 3); !id_a = std:ref_id a; !id_b = std:ref_id b; std:assert (id_a != id_b); std:assert std:ref_id[a] == id_a; !v = $[a]; std:assert std:ref_id[v.0] == id_a; ``` #### 3.14.1 - Pair Operator `a => b` Writing `a => b` operator is the same as writing `$p(a, b)`. However, the precedence of the `=>` operator is the lowest and right associative, so writing this is possible: ```wlambda !p = 1 + 2 => 3 + 4; std:assert_eq p $p(3, 7); ``` The following example shows off the associativity of the operator: ```wlambda !a = 1 => 2; !b = 2 => 3 => 4; std:assert_eq a $p(1, 2); std:assert_eq b $p(2, $p(3, 4)); std:assert_eq b 2 => 3 => 4; ``` #### 3.14.2 - cons _a_ _b_ Creates a new pair from the values _a_ and _b_. ```wlambda !p = cons 3 4; std:assert_eq p $p(3, 4); ``` #### 3.14.3 - Pair string/byte vector operations If you call a pair with a string or byte vector as argument, there are some operations that can be done: ##### 3.14.3.1 - $p( _from_ , _count_ ) _string-or-byte-vec_ Returns a substring starting at _from_ with the length _count_. ```wlambda std:assert_eq ($p(2, 4) "abcdefgh") "cdef"; ``` The same works for byte vectors: ```wlambda std:assert_eq ($p(2, 4) $b"abcdefgh") $b"cdef"; ``` ##### 3.14.3.2 - $p( _pattern_ , _replacement_ ) _string-or-byte-vec_ Replaces all _pattern_ occurences in _string_ by _replacement_. ```wlambda std:assert_eq ($p(";", "_") "A;B;D;EFG;HI") "A_B_D_EFG_HI"; ``` The same works for byte vectors: ```wlambda std:assert_eq ($p($b";", $b"_") $b"A;B;D;EFG;HI") $b"A_B_D_EFG_HI"; ``` ##### 3.14.3.3 - $p( _split-pattern_ , _max_ ) _string-or-byte-vec_ Splits _string_ at _split-pattern_ a _max_ number of times. If _max_ is 0, it is split completely. ```wlambda std:assert_eq str[$p(";", 3) "A;B;D;EFG;HI"] ~ str $["A", "B", "D;EFG;HI"]; std:assert_eq str[$p(";", 0) "A;B;D;EFG;HI"] ~ str $["A", "B", "D", "EFG", "HI"]; ``` The same works for byte vectors: ```wlambda std:assert_eq str[$p($b";", 0) $b"A;B;D;EFG;HI"] ~ str $[$b"A", $b"B", $b"D", $b"EFG", $b"HI"]; ``` #### 3.14.4 - Pair to Iterator Pairs play a special role if you make an iterator from it. It can be used to create a specialized iterator that only iterates over keys or values of a map. Or that enumerates a vector or map. ##### 3.14.4.1 - Iter - Range `$iter $p(0, 10)` is the same as `$iter $i(0, 10)` and will construct an iterator that iterates from `0` to `9` (inclusive). Because of the pair operator `a => b` we can nicely write a counting loop like this: ```wlambda !sum = $@i iter i 0 => 10 { $+ i; }; std:assert_eq sum 45; ``` ##### 3.14.4.2 - Iter - Enumerate If the first value of the pair is `:enumerate` it will enumerate entries in a map or values in a vector. ```wlambda !v = $[]; # $iter is only explicit here for demonstration # purposes! `iter` will make an iter from the pair # if you don't pass one! iter i ($iter $p(:enumerate, $[:a, :b, :c])) ~ std:push v i; std:assert_eq (str v) (str $[0, 1, 2]); ``` For maps: ```wlambda !v = $[]; iter i $p(:enumerate, ${a = 10, b = 20}) ~ std:push v i; std:assert_eq (str v) (str $[0, 1]); ``` ##### 3.14.4.3 - Iter - Values This is useful for iterating over the values in a map in an undefined order: ```wlambda !m = ${ a = 10, b = 20, c = 33 }; !sum = $@i iter v $p(:values, m) ~ $+ v; std:assert_eq sum 63; ``` ##### 3.14.4.4 - Iter - Keys You can also iterate over map keys in an undefined order: ```wlambda !m = ${ :10 = :c, :20 = :b, :30 = :a }; !sum = $@i iter v $p(:keys, m) ~ $+ v; std:assert_eq sum 60; ``` #### 3.14.5 - is\_pair _value_ Checks if _value_ is a pair. ```wlambda std:assert ~ is_pair $p(1, 2); std:assert not ~ is_pair $[1, 2]; std:assert not ~ is_pair $i(1, 2); ``` ### 3.15 - 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 [5.2 Collection Iteration](#52-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: ```wlambda !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; ``` To add elements to a vector, you can use the prepend and append operators `+>` and `<+` too: ```wlambda !v = $[1]; 0 <+ v; v +> 2; std:assert_str_eq v $[0,1,2]; ``` #### 3.15.1 - std:push _vector_ _item_ Pushes _item_ to the end of _vector_. Returns _item_. Be aware, that there is also the `+>` operator, that will append elements to a vector. ```wlambda !v = $[1,2]; std:push v 3; std:assert_eq (str v) (str $[1,2,3]); ``` #### 3.15.2 - std:pop _vector_ Pops off the last element of _vector_. Returns `$none` if the vector is empty or if _vector_ is not a vector. ```wlambda !v = $[1,2,3]; std:assert_eq (std:pop v) 3; std:assert_eq (str v) (str $[1,2]); ``` #### 3.15.3 - std:unshift _vector_ _item_ Inserts _item_ at the front of _vector_. Returns _item_ and mutates _vector_ inplace. Be aware that this operation is of O(n) complexity. Be aware, that there is also the `<+` operator, that will prepend elements to a vector (with O(n) complexity however). ```wlambda !v = $[1,2]; std:unshift v 3; std:assert_eq (str v) (str $[3,1,2]); ``` #### 3.15.4 - is\_vec _value_ Returns `$true` if _value_ is a vector. ```wlambda std:assert ~ is_vec $[]; std:assert ~ is_vec $[1,2,3]; std:assert ~ not ~ is_vec 0; std:assert ~ not ~ is_vec $none; std:assert ~ not ~ is_vec $true; std:assert ~ not ~ is_vec $p(1,2); std:assert ~ not ~ is_vec $i(1,2); std:assert ~ not ~ is_vec $f(1,2); std:assert ~ not ~ is_vec ${a = 10}; ``` #### 3.15.5 - Vector Splicing You can splice vectors directly into their literal form with the `$[..., * vec_expr, ...]` syntax. Here is an example: ```wlambda !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]"; ``` #### 3.15.6 - 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 its items will be appended to _vec-a_. ```wlambda !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: ```wlambda !v = std:append 1 :a :b $[:c, :d]; std:assert_eq (str v) "$[1,:a,:b,:c,:d]"; ``` #### 3.15.7 - std:prepend _vec-a_ _value-or-vec_ ... Prepends _value-or-vec_ and all following items to the front of _vec-a_. If _value-or-vec_ is a vector, all its items will be prepended to _vec-a_. ```wlambda !v = std:prepend $[1,2,3] :a :b $[:c, :d]; std:assert_eq (str v) (str $[:d, :c, :b, :a, 1, 2, 3]); ``` If _vec-a_ is not a vector, a vector containing it will be created: ```wlambda !v = std:prepend 1 :a :b $[:c, :d]; std:assert_eq (str v) (str $[:d, :c, :b, :a, 1]); ``` #### 3.15.8 - std:take _count_ _vector_ Takes and returns the first _count_ elements of _vector_. Does not mutate _vector_. ```wlambda !v = $[1,2,3,4,5,6]; !t = std:take 4 v; std:assert_eq (str v) "$[1,2,3,4,5,6]"; std:assert_eq (str t) "$[1,2,3,4]"; ``` #### 3.15.9 - std:drop _count_ _vector_ Drops _count_ elements from _vector_ and returns them as new vector. Does not mutate _vector_. ```wlambda !v = $[1,2,3,4,5,6]; !t = std:drop 4 v; std:assert_eq (str v) "$[1,2,3,4,5,6]"; std:assert_eq (str t) "$[5,6]"; ``` ### 3.16 - 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 symbols (or strings), the values in the literals can be any expression. Keys for maps are interned strings, so keep that in mind if you fill a map with garbage keys. For iteration over a map please refer to [5.2 Collection Iteration](#52-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: ```wlambda !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: ```wlambda !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. #### 3.16.1 - Map Splicing Like vectors you can splice map values directly into map literals: ```wlambda !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"}/; ``` #### 3.16.2 - is\_map _value_ Returns `$true` if _value_ is a map. ```wlambda std:assert ~ is_map ${}; std:assert ~ is_map ${a = 10}; std:assert ~ not ~ is_map $&&${}; std:assert ~ not ~ is_map $&${}; std:assert ~ not ~ is_map $[:a, 10]; std:assert ~ not ~ is_map $p(:a, 10); std:assert ~ not ~ is_map $none; std:assert ~ not ~ is_map $true; ``` ### 3.17 - References TODO - 3 types: strong, hidden, weak - strong: - `$&&` - mention DWIM'ery - hidden: - `$&` - mention usage for closures - how to "Unhide" a reference using `$:` - weak: - std:ref:weaken and `$w&` - how to break reference cycles - how weak references are also caught weakly by closures and not strongly. - how to get a strong reference using `$:` Some data structures already have reference characteristics, such as strings, vectors and maps. But you can also wrap other values like integers, floats, ... into a reference. There are 3 types of references in WLambda that have different usecases and semantics. These referential types are neccessary to mutate lexical variables from a parent scope. To give a rather natural example: ```wlambda !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 its contents is implicitly changed in to a _hidden_ `$&` reference. The closure then stores this hidden reference too. You have to be aware of this, because in some use cases this can lead to cyclic reference structures, which are not automatically freed. Please use weak references `$w&` for mitigating this. Hidden references and weak references captured by a closure are dereferenced implicitly if you access the variables. Weak references in the local scope are not implicitly dereferenced. However, sometimes it's desirable to have a more explicit reference data types. For this the strong references `$&&` are available. Use `$*` for accessing the value of a strong reference or a weak reference in the local scope. TODO These types of references exist: - `$&` - A _hidden_ reference, that is captured by closures or constructed using `$&`. - `$w&` - A _weak_ reference, can't be constructed literally, only indirectly as upvalue of a closure or by `std:ref:weaken`. - `$&&` - A _strong_ reference, that is captured strongly by closures. Inside closures they are also implicitly dereferenced by assignment and access by variable name. ```wlambda !x = $& 10; { .x = 20; }[]; # Closures implicitly handle weak references std:assert_eq x 20; ``` And the same with strong references: ```wlambda !x = $&& 10; .*x = 11; { .*x = 20; }[]; # Closures need explicit handling of strong references std:assert_eq $*x 20; ``` Strong references can also be created using the `std:to_ref` function and the `$:` operation. #### 3.17.1 - std:to\_ref _value_ Creates a new strong reference that refers to a cell that stores _value_. ```wlambda !x = std:to_ref 10; std:assert_eq (std:ser:wlambda x) "$&&10"; std:assert_eq $*x 10; ``` #### 3.17.2 - std:ref:weaken _ref_ You can weaken any of those two types of references manually using the `std:ref:weaken` function. ```wlambda !drop_check = $& $f; # Set `drop_check` to $true when all (non weak) references to it are gone. !x = $&& (std:to_drop {|| .drop_check = $true }); # Create a weakened reference to the value referred to by x: !y = std:ref:weaken x; # The reference to the drop function 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; ``` Please note that you can use `$w&`/`$weak&` as a shortcut to calling the library function: ```wlambda !x = $&& 10; !x_weak = $w& x; std:assert_eq x &> type "ref_strong"; std:assert_eq x_weak &> type "ref_weak"; ``` #### 3.17.3 - std:ref:hide _value_ Creates a hidden reference from a given value or reference. ```wlambda !r = $&& 10; # strong ref to value 10 # hide the reference for direct access via local variables !h = std:ref:hide r; .h += 11; std:assert_eq $*r 21; std:assert_eq h 21; std:assert_eq (std:write_str $:h) "$&&21"; std:assert_eq (std:write_str $[r, $:h]) "$[$<1=>$&&21,$<1>]"; ``` #### 3.17.4 - is\_ref _value_ Returns `$true` if _value_ is a reference (strong, hidden or weak). ```wlambda !x = $&&10; std:assert ~ is_ref ~ std:ref:weaken x; std:assert ~ is_ref $&10; std:assert ~ is_ref $&&10; std:assert ~ not ~ is_ref $[1,2,3]; std:assert ~ not ~ is_ref ${a=10}; std:assert ~ not ~ is_ref $true; std:assert ~ not ~ is_ref $none; ``` #### 3.17.5 - is\_wref _value_ Returns `$true` if _value_ is a weak reference. ```wlambda !x = $&& 10; !y = std:ref:weaken x; std:assert ~ is_wref y; std:assert ~ not ~ is_wref x; ``` #### 3.17.6 - std:ref:strengthen _ref_ You can convert a weak reference (weakened by `std:ref:weaken`) or a captured weak reference `$&` to strong with `std:ref:strengthen ```wlambda !x = $&&10; !y = std:ref:weaken x; .x = $none; std:assert ~ is_none $*y; .x = $&&10; .y = std:ref:weaken x; !y2 = std:ref:strengthen y; # Here we take a second strong reference from a weak one .x = $none; std:assert ~ is_some $*y; std:assert ~ is_some $*y2; .y2 = $none; std:assert ~ is_none $*y; ``` #### 3.17.7 - std:ref:set _ref_ _value_ Sets the value of the reference _ref_ to _value_. If _ref_ is not a strong, hidden or weak reference nothing happens. Returns _value_ or `$none`. ```wlambda !r1 = $&&1; std:ref:set r1 10; std:assert_eq $*r1 10; # Note that $& references in local variables are # automatically dereferenced. Because of that we need to wrap it into # an extra reference. !r2 = $& $& 1; std:ref:set r2 11; std:assert_eq $*r2 11; !r3 = $& $& 1; !w3 = std:ref:weaken r3; std:ref:set w3 14; # Set reference via the weak reference in w3 to r3. std:assert_eq $*r3 14; ``` ### 3.18 - Iterators $iter _expression_ As a companion to the `iter` operation there are the iterator values. These are a special kind of values that generate a value when they are called. It supports to make an iterator from the same values as the `iter` operation. You can create an iterator from vectors and maps, but also specialized iterators that return a range of numbers or only keys of a map. About this see the section _Iterator Kinds_ below. The `$iter` syntax takes a complete expression as argument, that means you can directly write `$iter function arg1 arg2 ...` without delimiting the function call. Here is an example how to make an iterator over a vector: ```wlambda !it = $iter $[1,2,3,4]; !first = it[]; # returns an optional value $o(1) !second = it[]; # returns an optional value $o(2) std:assert_eq first $o(1); std:assert_eq second $o(2); ``` You can also directly cast an iterator, which will also make it return a value: ```wlambda !it = $iter $[1,2,3,4]; std:assert_eq (int it) 1; std:assert_eq (int it) 2; ``` You can pass an iterator also to the `iter` operation: ```wlambda !it = $iter $[1,2,3]; !sum = 0; iter i it { .sum = sum + i; }; std:assert_eq sum 6; ``` #### 3.18.1 - Iterator Kinds Here is a table of the behaviour of iterators created from WLambda data. | Data | Iterator return values | |-----------|-----------| | vector | Each element of the vector. | | map | Each key/value pair of the map in undefined order. | | `$none` | Returns nothing | | optional | Returns the optional value on first invocation. | | `$o()` | Returns nothing. | | int | Returns the integer value on first invocation. | | float | Returns the integer value on first invocation. | | string | Returns the individual characters as string. | | symbol | Returns the individual characters as string. | | byte vector | Returns the individual bytes as byte vector. | | `$error` | Returns the error value on first invocation. | | `$i(a, b)` | The integers in the range of _a_ to _b_, not including _b_. | | `$i(a, b, step)` | The integers in the range of _a_ to _b_ advanced by _step_, not including _b_. | | `$f(a, b)` | The floats in the range of _a_ to _b_ advanced by `1.0`, not including _b_. | | `$f(a, b, step)` | The floats in the range of _a_ to _b_ advanced by _step_, not including _b_. | | `$p(:enumerate, map)` | Returns integers in the range of `0` to `len map`. | | `$p(:enumerate, vector)` | Returns integers in the range of `0` to `len vector`. | | `$p(:values, map)` | Returns the values of the _map_ in undefined order. | | `$p(:keys, map)` | Returns the keys of the _map_ in undefined order. | | `$p(int_a, int_b)` | The same as `$i(a, b)`. This makes it possible to write `$iter 0 => 10`. | | `$p(iterator_a, iterator_b)` | Returns a zip operation of the elements returned by the iterator_a and iterator_b until one of both returns `$o()`. | | `$p(iterator, x)` | Returns a zip operation of the elements returned by the iterator and the newly created iterator`$iter x`. | #### 3.18.2 - Iterators on mutated data Iterators hold a reference to the collection values. That means, if you mutate a vector while you iterate over it, it will not crash but it might produce weird effects. ```wlambda !v = $[1,2,3]; !it = $iter v; iter i v { if i <= 3 { std:push v i + 10; # This is not recommended however... }; }; std:assert_eq (str v) (str $[1, 2, 3, 11, 12, 13]); ``` This will also work for maps, but as the order of the map entries is undefined it will produce very indeterministic effects and it's really not recommended. #### 3.18.3 - Splicing an Iterator You can directly insert the values produced by an iterator into a vector or map: ```wlambda !it = $iter $[1,2,3,4]; !v = $[10, 20, *it, 99]; std:assert_eq (str v) (str $[10, 20, 1, 2, 3, 4, 99]); ``` Same goes for maps: ```wlambda !it = $iter ${a = 10, b = 20}; !m = ${ x = 99, *it }; std:assert_eq m.a 10; std:assert_eq m.b 20; std:assert_eq m.x 99; ``` #### 3.18.4 - Calling an Iterator with a Function When an iterator gets called with a function as first argument it will repeatedly call that function until no more values are available: ```wlambda !it = $iter $[1,2,3]; !sum = 0; it { .sum = sum + _ }; std:assert_eq sum 6; ``` #### 3.18.5 - Zip Iterators To highlight this feature from the table above: You can zip two iterators if you pass an iterator as first part of a pair `$p(a, b)`: ```wlambda !v = $["a", "b", "c"]; !elems = $@vec iter i $p($iter v, $iter $i(0, 10)) { $+ i; }; std:assert_eq (str elems) (str $[$p("a", 0), $p("b", 1), $p("c", 2)]); ``` #### 3.18.6 - is\_iter _value_ Returns `$true` if _value_ is an iterator. ```wlambda std:assert (is_iter $iter $n); std:assert (is_iter $iter 0 => 30); std:assert not <& (is_iter $[1,2,3]); std:assert (is_iter $iter $[1,2,3]); std:assert (not <& is_iter <& $true); std:assert (not <& is_iter <& $false); std:assert (not <& is_iter <& 4); std:assert (not <& is_iter <& $p(1, 2)); ``` ### 3.19 - 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. | | integer | vector, string, byte_vec, iterator | Will return the element at the given integer index. | | 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. | | string | string, byte_vec, char or byte | Append operation, works with multiple arguments. | | byte_vec | string, byte_vec, char or byte | Append operation, works with multiple arguments. | | string | `$p(int_offs, string/byte_vec/char/byte)` | Find operation, search from int_offs for the given string, byte_vec, char or byte. See also `std:str:find` and `std:bytes:find`. | | byte_vec | `$p(int_offs, string/byte_vec/char/byte)` | Find operation, search from int_offs for the given string, byte_vec, char or byte. See also `std:str:find` and `std:bytes:find`. | | `$p(char or byte, int_count)`| - | Create a string or byte_vec containing int_count of copies. | | `$p(int_offs, string/byte_vec/char/byte)` | string | Find operation, search from int_offs for the given string, byte_vec, char or byte. | | `$p(int_offs, string/byte_vec/char/byte)` | byte_vec | Find operation, search from int_offs for the given string, byte_vec, char or byte. | | `$p(int_from, int_count)` | vector | Vector slice operation. | | `$i(int_from, int_count)` | vector | Vector slice operation. | | `$p(int_from, int_count)` | numeric vector | Creates a vector slice from a numeric vector. | | `$i(int_from, int_count)` | numeric vector | Creates a vector slice from a numeric vector. | | `$p(int_from, int_count)` | iterator | Iterator result list slice operation. | | `$i(int_from, int_count)` | iterator | Iterator result list slice operation. | | `$p(int_from, int_count)` | string | Substring operation. (See also section about pairs) | | `$i(int_from, int_count, ...)`| string | Substring operation. | | `$p(int_from, int_count)` | byte_vec | Substring operation. (See also section about pairs) | | `$i(int_from, int_count, ...)`| byte_vec | Substring operation on the byte vector. | | string |`$i(int_from, int_count, ...)`| Substring operation. | | byte_vec |`$i(int_from, int_count, ...)`| Substring operation on the byte vector. | |`$p(string, int)` | string | Split operation. (See also section about pairs) | |`$p(byte_vec, int)` | byte_vec | Split operation. (See also section about pairs) | | string |`$p(string, int)` | Split operation. | | byte_vec |`$p(byte_vec, int)` | Split operation. | | string |`$p(string, string)` | Replace all operation. | | byte_vec |`$p(byte_vec, byte_vec)` | Replace all operation. | | `$p(string, string)` | string | Replace all operation. (See also section about pairs) | | `$p(byte_vec, byte_vec)` | byte_vec | Replace all operation. (See also section about pairs) | | `$p(pat_char, repl_char)` | string or byte_vec | Replace all pat_char in string or byte_vec with repl_char. | | `$p(pat_byte, repl_byte)` | string or byte_vec | Replace all pat_char in string or byte_vec with repl_char. | | `$p(char, char)` | char or byte | Range check operation, whether char or byte is inside to/from range. | | `$p(byte, byte)` | char or byte | Range check operation, whether char or byte is inside to/from range. | | `$p(int_a, int_b)` | char or byte | Range check operation, whether char or byte is inside to/from range. | | `$i(int_a, int_b)` | char or byte | Range check operation, whether char or byte is inside to/from range. | | char or byte |`$p(char, char)` | Range check operation, whether char or byte is inside to/from range. | | char or byte |`$p(byte, byte)` | Range check operation, whether char or byte is inside to/from range. | | char or byte |`$p(int_a, int_b)` | Range check operation, whether char or byte is inside to/from range. | | char or byte |`$i(int_a, int_b)` | Range check operation, whether char or byte is inside to/from range. | | `$o()` | - | Returns $none. | | `$o(x)` | - | Returns _x_. | | `$o()` | * | Calls $none with arguments, leading to a panic. | | `$o(x)` | * | Calls _x_ with the given arguments. | | | | | ## 4 - Conditional Execution - if / then / else ### 4.1 - if/? _condition_ _then-expr_ [_else-expr_] The keyword for conditional execution is either `if` or just the question mark `?`. It takes 3 arguments: The first is an expression that will be evaluated and cast to a boolean. If the boolean is `$true`, the second argument is evaluated. If the boolean is `$false` the thrid argument is evaluated. The third argument is optional. ```wlambda !x = 10; !msg = "x is "; if x > 4 { .msg = std:str:cat msg "bigger than 4"; } { .msg = std:str:cat msg "smaller than or equal to 4"; }; std:assert_eq msg "x is bigger than 4"; # You may also use `if` in case it suits your coding style better: if x == 10 { std:assert $true; } { std:assert $false; }; ``` The _condition_ can also be a function block, which will be evaluated: ```wlambda !res = if { !x = 2; x > 1 } "x > 1"; std:assert_eq res "x > 1"; ``` ### 4.2 - Using Booleans for Conditional Execution Conditional execution is also 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. ```wlambda (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: ```wlambda (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" ``` #### 4.2.1 - pick _bool_ _a_ -b- Often, you may want to choose one variable (_a_) or another (_b_) based on some predicate (_bool_). 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. ```wlambda !sum = \|| std:fold 0 { _ + _1 } ~ pick (is_vec _) _ @; ``` #### 4.2.2 - Indexing by Booleans 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: ```wlambda !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. ```wlambda !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: ```wlambda !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. ### 4.3 - Value matching with - match _value-expr_ ... See also [8.1.1](#811-match-value-expr-match-pair1--default-expr) for a more comprehensive discussion of `match` and structure matchers. `match` allows for easily select from a set of values: ```wlambda !check_fun = { match _ 20 => "It's 20" 30 => "It's 20" "No idea?" }; std:assert_eq check_fun[20] "It's 20"; std:assert_eq check_fun[34] "No idea?"; ``` Also works for deeper data structures: ```wlambda !val = $[1, ${a = 10}, ${a = 10}, ${a = 10}, ${a = 10}, 2, 2, 2, 10]; !res = match val $[1, _*, 3, 10] => :a $[1, a ~ _* ${ a = y }, b ~ _+ 2, 10] => { ${ a_elems = $\.a, a_value = $\.y, b_vals = $\.b, } }; std:assert_str_eq res.a_elems $[${ a = 10 }, ${ a = 10 }, ${ a = 10 }, ${ a = 10 }]; std:assert_str_eq res.a_value 10; std:assert_str_eq res.b_vals $[2,2,2]; ``` ## 5 - Loops And Iteration WLambda has many ways to loop and iterate: - Counting loop with `range` - While some condition is `$true` with the `while` special form. - Over the items in a vector or map with the `iter` special form. - Calling an `$iter` iterator value with a function as first argument. - 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`. ### 5.1 - Control Flow #### 5.1.1 - while _predicate_ _body_ `while` will evaluate _body_ until the evaluation of _predicate_ function returns `$false`. Or `break` is used to end the loop. The loop can be restarted using `next`. This is the most basic loop for iteration: ```wlambda !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: ```wlambda !i = 0; while $true { (i >= 4) break; .i = i + 1; }; std:assert_eq i 4; ``` The first #### 5.1.2 - iter _var_ _iterable_ _body_ This is the primary syntax of WLambda to iterate over collections, numeric ranges and generally everything you can create an iterator from using the `$iter` syntax. The _var_ will be defined inside the _body_ and be filled with the value that was generated for the current iteration. And _iterable_ is everything that `$iter` can make an iterator from. Please refer to the section `Iterator Kinds` for a listing of this. Like usual, the control flow manipulators `next` and `break` also work for this kind of loop. ##### 5.1.2.1 - Counting loop with _iter_ Here is an example how to iterate over a range from 1 to 9 and collect the sum of those integers using an accumulator: ```wlambda !sum = $@int iter i $i(1,10) ~ $+ i; std:assert_eq sum 45; ``` Because `$iter $p(1, 10)` is the same as `$iter $i(1, 10)` and because there is the pair constructor operator `a => b`, the above can also be written as: ```wlambda !sum = $@int iter i 1 => 10 ~ $+ i; std:assert_eq sum 45; ``` ##### 5.1.2.2 - Vector iteration with _iter_ Here is a simple example of how to iterate over all items of a vector in order: ```wlambda !sum = 0; iter i $[1,2,3,4,5,6] { .sum = sum + i; }; std:assert_eq sum 21; ``` Even if you pass the syntax for constructing a function to `iter` it will create a block of statements from it. So this will work too (also for `while` above): ```wlambda !sum = 0; iter i $[1,2,3,4,5,6] \.sum = sum + i; std:assert_eq sum 21; ``` However _body_ does not have to be a function definition or block, it can also be just a regular call argument: ```wlambda !sum = 0; !inc = { .sum = sum + _; }; iter i $[1,2,3,4] inc[i]; std:assert_eq sum 10; ``` To iterate over a vector by index you can use this: ```wlambda !v = $[1,2,3,4,5]; !sum = 0; iter i $i(0, len v) { .sum = sum + v.(i); }; std:assert_eq sum 15; ``` ##### 5.1.2.3 - Map iteration with _iter_ Iteration over a map is also easy and concise. The map entry will be represented using a pair value `$p(value, key)`. You can access the first and second element of a pair using the `v`/`value` and `k`/`key` keys of a pair (but also all other pair accessors defined in the section for pairs): ```wlambda !sum = 0; iter i ${ a = 10, b = 20 } { .sum = sum + i.v; }; std:assert_eq sum 30; ``` Very useful for iterating just over the keys or values of a map can also be the special iterator values you get from the pair constructors: ```wlambda !m = ${ a = 10, b = 20 }; !sum = 0; iter v $p(:values, m) { .sum = sum + v; }; std:assert_eq sum 30; ``` Or if you need the keys: ```wlambda !m = ${ a = 10, b = 20 }; !sum = 0; iter k $p(:keys, m) { std:displayln "FOO" k; .sum = sum + m.(k); }; std:assert_eq sum 30; ``` ##### 5.1.2.4 - Closures and _iter_ `iter i ...` If you need a new variable for capturing it in a closure on each iteration you need to make a new variable binding for each iteration: ```wlambda !closures = $[]; # Without the rebinding of the variable `i`, `i` would be captured as hidden # reference and each iteration would update the contents of that reference. iter i $i(0, 10) { !i = i; std:push closures { i * 10 }; }; std:assert_eq ($@i closures \$+ _[]) 450; ``` #### 5.1.3 - 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. In contrast to `iter` this is not a special syntax, but just a regular function that calls another function repeatedly. You can control it using the `break` and `next` functions however. ```wlambda !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: ```wlambda !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]"; ``` #### 5.1.4 - 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`, `iter` 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`. Be aware, that returning a value might not be supported by all iterative constructs. ```wlambda !ret = range 0 9 1 {!(i) = @; (i > 4) { break :DONE }; }; std:assert_eq ret :DONE; ``` An example where the list iteration is stopped: ```wlambda !val = $[1,2,3,4] { (_ > 3) { break :XX }; _ }; std:assert_eq val :XX; ``` #### 5.1.5 - next `next` stops execution of the current function or statement block and continues with the next iteration of the inner most iteration. ```wlambda !sum = $@i $[1,2,3,4] { (_ == 3) next; $+ _; }; std:assert_eq sum 7; ``` ```wlambda !sum = $@i range 1 10 1 { (_ % 2 == 0) next; $+ _; }; std:assert_eq sum 25; ``` #### 5.1.6 - jump _index-val_ _branch1_ ... _last-branch_ This is a jump table operation, it's a building block for the more sophisticated `match` operation. The first argument is an index into the table. If the index is outside the table the _last-branch_ is jumped to. The branches are compiled like the bodies of `while`, `iter`, `match` and `if` into a runtime evaluated block. ```wlambda !x = 10; !idx = 2; !res = jump idx { x + 3 } { x + 4 } { x + 5 }; std:assert_eq res 15; ``` The arms don't have to be in `{ ... }` because they are blocks and the above could be written like this: ```wlambda !x = 10; !idx = 2; !res = jump idx x + 3 x + 4 x + 5; std:assert_eq res 15; # or even this: !res = x + (jump idx 3 4 5); std:assert_eq res 15; ``` ### 5.2 - Collection Iteration #### 5.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: ```wlambda !sum = 0; $[1, 2, 3] { .sum = sum + _; }; std:assert_eq sum 6; ``` You can also use `for` if you like. #### 5.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 its key/value pairs. The first argument of the function is the value, the second argument is the key. ```wlambda !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. #### 5.2.3 - for _iteratable-value_ _function_ Calls _function_ for every element of _iteratable-value_. Iteratable values are: - Vectors ```wlambda !product = 1; for $[3,4,5] { .product = product * _; }; std:assert_eq product 60; ``` - Maps ```wlambda !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 ```wlambda !byte_sum = 0; for $b"abc" { .byte_sum = byte_sum + (int _); }; std:assert_eq byte_sum 294; ``` - Strings ```wlambda !str_chars = $[]; for "abc" { std:push str_chars _; }; std:assert_eq (str str_chars) (str $['a', 'b', 'c']); ``` - Symbols ```wlambda !str_chars = $[]; for :abc { std:push str_chars _; }; std:assert_eq (str str_chars) (str $['a', 'b', 'c']); ``` #### 5.2.4 - map _function_ _iterable_ Maps anything that is _iterable_ by calling _function_ with each item as first argument and collecting the return values in a vector. If a map is passed as _iterable_ then _function_ is called with two arguments, the first being the map entry value and the second the key. Note: When iterating over maps, don't assume any order. It is very similar to `$@vec iter i { $+ ... }`. ```wlambda # Lists: std:assert_str_eq (map { float[_] / 2.0 } $[1,2,3,4,5]) $[0.5, 1, 1.5, 2, 2.5]; std:assert_str_eq (map { _ * 10 } $[$b'a', $b'b', $b'c', 10, 20]) ($@vec iter i $[$b'a', $b'b', $b'c', 10, 20] { $+ i * 10 }); # Great for working with strings too: std:assert_str_eq (map std:str:to_uppercase $["abc", "bcbc", "aaad", "afoo", "foo"]) $["ABC", "BCBC", "AAAD", "AFOO", "FOO"]; # Maps: std:assert_str_eq (std:sort ~ map { @ } ${a = 10, b = 20}) $[$[10, "a"], $[20, "b"]]; # Generally anything that you can pass into `$iter`: std:assert_str_eq (map { _ * 2 } 0 => 10) $[0,2,4,6,8,10,12,14,16,18]; ``` #### 5.2.5 - filter _function_ _iterable_ Filters anything that is _iterable_ by the given _function_. The _function_ is called with each item and if it returns a `$true` value, the item will be collected into a vector that is returned later. If a map is passed as _iterable_ then _function_ is called with two arguments, the first being the map entry value and the second the key. It is very similar to `$@vec iter i { if some_function[_] { $+ ... } }`. ```wlambda # Lists: std:assert_str_eq (filter { (_ 0 1) == "a" } $["abc", "bcbc", "aaad", "afoo", "foo"]) $["abc","aaad","afoo"]; # Good in combination with `map` too: std:assert_str_eq (map std:str:to_uppercase ~ filter { (_ 0 1) == "a" } $["abc", "bcbc", "aaad", "afoo", "foo"]) $["ABC","AAAD","AFOO"]; # Also like `map` works fine with maps, but the function # needs to take two arguments and returns a pair: std:assert_str_eq (std:sort ~ filter { @.0 % 2 == 0 } ${a = 2, b = 43, c = 16, d = 13 }) $[16 => "c", 2 => "a"]; # Generally anything that you can pass into `$iter`: std:assert_str_eq (filter { _ % 2 == 0 } 0 => 10) $[0,2,4,6,8]; ``` ### 5.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. | These syntaxes are not lexically scoped. That means `$+` and `$@@` can be used in other functions: ```wlambda !out_mul = { $+ _ * 20 }; !v = $@vec iter i $i(1,5) ~ out_mul i; std:assert_eq (str v) (str $[20, 40, 60, 80]); ``` However, due to issues with coupling your functions to the usage of accumulators this style is recommended: ```wlambda !mul = { _ * 20 }; !v = $@vec iter i $i(1,5) ~ $+ mul[i]; std:assert_eq (str v) (str $[20, 40, 60, 80]); ``` #### 5.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: ```wlambda !result = $@vec $[1,2,3,4] \$+ _ * 2; # multiply each item by 2 std:assert_eq (str result) "$[2,4,6,8]"; ``` #### 5.3.2 - Example of `$@@` Here is an interesting example how $@@ might be used: ```wlambda !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]]"; ``` #### 5.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: ```wlambda !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; ``` #### 5.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`: ```wlambda # 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`: ```wlambda !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: ```wlambda !transform = { ">" _ }; !accum = $@string $["abc", "def", "ghi", "XXX"] { $+[transform _] # appends the string to the accumulation string }; std:assert_eq accum ">abc>def>ghi>XXX"; ``` #### 5.3.5 - Accumulating sums The following examples show how accumulation of values with `$@int` and `$@float` work. ```wlambda !sum = $@int $[1,2,3,4] { $+ _ }; std:assert_eq sum 10; ``` And with floats: ```wlambda !sum = $@float $[1.2,1.3,2.2,3.4] { $+ _ }; std:assert_eq (std:num:round 10.0 * sum) 81.0; ``` ### 5.4 - Utilities #### 5.4.1 - std:accum _collection_ _a_ _b_ ... This function accumulates all its arguments in the _collection_. It does the same form of accumulation as `$+` does. ```wlambda 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"; ``` #### 5.4.2 - std:zip _vector_ _map-fn_ Creates a generator that calls _map_fn_ with the consecutive elements of _vector_ as the last 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. ```wlambda !l = $@v $[13, 42, 97] ~ std:zip $["Foo", "Bar", "Baz"] { $+ @ }; std:assert_eq (str l) (str $[$[13, "Foo"], $[42, "Bar"], $[97, "Baz"]]); ``` #### 5.4.3 - std:fold _accumulator_ _func_ _iteratable_ This function iterates over _iteratable_ while providing the current element from _iteratable_ as first and the _accumulator_ variable to _func_ as second argument. The _accumulator_ for the next iteration is always the return value of the previous execution of _func_. This is a convenience function in cases where the accumulator syntax `$@` does not fit the use-case. Returns the most recently returned value from _func_. Calculate the product of the first 5 integers. ```wlambda !v = std:fold 1 {!(x, acc) = @; x * acc } $[1,2,3,4,5]; std:assert_eq v 120; ``` Another contrived example: ```wlambda !v = std:fold $[] {!(x, acc) = @; std:displayln @; ((std:cmp:str:asc "c" x) > 0) { std:push acc x; }; acc } "abcdef"; std:assert_eq (str v) (str $['d', 'e', 'f']); ``` #### 5.4.4 - std:enumerate _map-fn_ Creates a generator that calls _map-fn_ with a counter that is incremented after each call, starting with 0. The counter is appended to the argument list after the regular arguments. ```wlambda !l = $@v $["lo", "mid", "hi"] ~ std:enumerate { $+ $[_1, _] }; std:assert_eq (str l) (str $[$[0, "lo"], $[1, "mid"], $[2, "hi"]]); ``` ## 6 - Operators ### 6.1 - Operator Assignment Please note, that you can use all these operators, as well as special operators like `=>`, `&>` and `<&` with assignment operations: ```wlambda !x = 10; .x += 3; std:assert_eq x 13; # also comparison operators work !y = 10; .y < = 10; std:assert_eq y $false; # function argument pipelining also works in this context !f = \_ * 10; .f <&= 10; std:assert_eq f 100; !x = 10; .x &>= \_ * 10; std:assert_eq f 100; ``` ### 6.2 - 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. #### 6.2.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. ```wlambda 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; ``` #### 6.2.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. ```wlambda 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; ``` #### 6.2.3 - * _op-a_ _op-b_ Returns the multiplication of the two operands. ```wlambda 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; ``` #### 6.2.4 - / _op-a_ _op-b_ Returns the division of the two operands. ```wlambda 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; ``` #### 6.2.5 - % _op-a_ _op-b_ Returns the remainder of the division of _op-a_ by _op-b_. ```wlambda std:assert 5 % 4 == 1; std:assert (`%` 5 4) == 1; ``` #### 6.2.6 - ^ _op-a_ _op-b_ Returns _op-a_ raised by the power of _op-b_. Supports float and integers. ```wlambda 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! ``` ### 6.3 - Comparison #### 6.3.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. ```wlambda 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 $p(1,2) == $p(1,2); std:assert $i(1,2) == $i(1,2); std:assert $i(1,2,3) == $i(1,2,3); std:assert not ~ $i(1,2,3) == $f(1.0,2.0,3.0); std:assert $f(1.0,2.0,3.0) == $f(1.0,2.0,3.0); std:assert ~ `==` 1 (2 - 1); # prefix form ``` #### 6.3.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 `==`. ```wlambda 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; ``` #### 6.3.3 - < _op-a_ _op-b_ Numerical comparison operator that checks whether _op-a_ is less than _op-b_ ```wlambda 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! ``` #### 6.3.4 - <= _op-a_ _op-b_ Numerical comparison operator that checks whether _op-a_ is less or equal to _op-b_ ```wlambda 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! ``` #### 6.3.5 - > _op-a_ _op-b_ Numerical comparison operator that checks whether _op-a_ is greater than _op-b_ ```wlambda 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! ``` #### 6.3.6 - >= _op-a_ _op-b_ Numerical comparison operator that checks whether _op-a_ is greater or equal to _op-b_ ```wlambda 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! ``` ### 6.4 - Bit Operations #### 6.4.1 - & _op-a_ _op-b_ Binary `and` operation between two integers. ```wlambda std:assert (0b0011 & 0b1011) == 0b011; std:assert (3 & 11) == 3; ``` #### 6.4.2 - &^ _op-a_ _op-b_ Binary `xor` operation between two integers. ```wlambda std:assert (0b0011 &^ 0b1011) == 0b1000; std:assert (3 &^ 11) == 8; ``` #### 6.4.3 - &| _op-a_ _op-b_ Binary `or` operation between two integers. ```wlambda std:assert (0b0011 &| 0b1000) == 0b1011; std:assert (3 &| 8) == 11; ``` #### 6.4.4 - << _op-a_ _op-b_ Binary `left shift` operation of _op-a_ by _op-b_ bits. ```wlambda std:assert (0b0011 << 3) == 0b11000; std:assert (`<<` 0b1011 2) == 0b101100 ``` #### 6.4.5 - >> _op-a_ _op-b_ Binary `right shift` operation of _op-a_ by _op-b_ bits. ```wlambda std:assert (0b0011 >> 2) == 0b0; std:assert (0b1100 >> 2) == 0b11; std:assert (`>>` 0b1011000 3) == 0b1011 ``` ### 6.5 - Collection Addition Operators +> and <+ `+>` and `<+` are special operators for convenient and quick collection creation. You can use them also to call a function with multiple arguments. ```wlambda !vec = $[] +> 1 +> 2 +> 3; std:assert_str_eq vec $[1, 2, 3]; !map = ${} +> (:a => 1) +> (:b => 2); map +> (:c => 3); std:assert_str_eq map ${a=1,b=2,c=3}; ``` Usually these operators just append (`+>`) or prepend (`<+`) the right/left hand side to the collection. But there are some special values which do special things. First and foremost the iterator data type. If you pass an iterator to this operator, the iterator will be iterated and all returned elements are added to the collection in the order of the operator: ```wlambda !v = $[] +> ($iter 0 => 4) +> ($iter "abc"); std:assert_str_eq v $[0,1,2,3,'a','b','c']; ``` As the `<+` operator prepends the individual elements, the same is happening with the iterated elements. Which means that their order is reversed: ```wlambda !v = ($iter 0 => 4) <+ ($iter "abc") <+ $[]; std:assert_str_eq v $[3,2,1,0,'c','b','a']; ``` The following data types can be used as collection for these operators: - Vectors `$[]` - Maps `${}` - Strings `""` - Byte vectors `$b""` - Functions The most special cases are maps and functions, which are described in more detail in the next sections. #### Collection Addition with Maps If you add to a map, there is some special behavior for some data types. Adding a key value pair is done with pairs: ```wlambda !m = ${} +> $p(:a, 10) +> :b => 20; std:assert_str_eq m ${a=10,b=20}; ``` If you add an iterator, the iterator is walked and the given keys are added if present: ```wlambda !m = ${} +> ($iter $[1, 2, 3]) +> ($iter ${ a = 10, b = 20 }); std:assert_str_eq m ${1=1,2=2,3=3,a=10,b=20}; ``` If you add a list to a map, the first element of that list is used as key, and the list as value: ```wlambda !m = ${} +> $[:a, 1, 2] +> $[:b, 3, 4]; std:assert_str_eq m ${a=$[:a,1,2],b=$[:b,3,4]}; ``` #### Collection Addition with Function If you pass a function as collection to either `+>` or `<+` the function is called for each added element. The return value of the expression is the return value of the most recent function call. ```wlambda !v = $[]; !v2 = { std:push v _; v } +> 1 +> 2 +> ${ a = 3, b = 4 }; std:assert_str_eq v $[1,2,${a=3,b=4}]; std:assert_str_eq v2 $[1,2,${a=3,b=4}]; ``` #### 6.5.1 - +> _collection_ _a_ ... Append to collection operator. ```wlambda !v = $[] +> 1 +> "x" +> $b"y" +> ($iter 0 => 3); !v2 = `+>` $[] 1 "x" $b"y" ($iter 0 => 3); std:assert_str_eq v v2; ``` #### 6.5.2 - <+ _collection_ _a_ ... Prepend to collection operator. Please note that the arguments are reversed to the order in an operator expression. ```wlambda !v = ($iter 0 => 3) <+ 1 <+ "x" <+ $b"y" <+ $[]; !v2 = `<+` $[] $b"y" "x" 1 ($iter 0 => 3); std:assert_str_eq v v2; ``` ### - Call Operators &>, \<&, &@> and \<@& See also: - [Forward Argument Pipe `arg &> fun`](#253-forward-argument-pipe-arg--fun) - [Forward Argument Apply Pipe `list &@> fun`](254-forward-argument-apply-pipe-list--fun) - [Reverse Argument Pipe `fun <& arg`](#255-reverse-argument-pipe-fun--arg) - [Reverse Argument Apply Pipe `list &@> fun`](#256-reverse-argument-apply-pipe-list--fun) ### - Default Value Operators //, /?, /$n, /$o and /$e There is a set of convenient default value operators that allow quick unwrapping of optional value, `$none` and even `$error` values. The most safe to use default value operator is the `//` operator, which returns an alternative value in case `$none` or `$o()` is provided and even unwraps optional values like `$o(10)`. It does not do anything if an `$error` value is encountered. ```wlambda !mul = {|1<2| !(a, b) = @; a * b // 1 }; std:assert_eq mul[10] 10; std:assert_eq mul[10, 20] 200; ``` For handling the error value case you can either explicitly combine it with a `/$e` operator, which is the most explicit thing to use. Or use the extended default value operator `/?` which also provides the default value if an error is encountered. Please note that the operator versions (in contrast to the function versions in backticks) is short circuit just like the `&or` operator: ```wlambda !mul = {|1<2| !(a, b) = @; a // return[-1] * b // return[-1] }; std:assert_eq mul[10] -1; std:assert_eq mul[$none, 20] -1; std:assert_eq mul[10, 20] 200; ``` For more details see the following sections. #### - // _a_ _default-b_ The default value operator is the `//` operator, which returns an alternative value in case `$none` or `$o()` is provided on the left hand side and even unwraps optional values like `$o(10)`. It does not do anything if an `$error` value is encountered. For an operator that also defaults `$error` values see the extended default value operator `/?`. ```wlambda std:assert_eq $n // 10 10; std:assert_eq $o() // 10 10; std:assert_eq $o(20) // 10 20; std:assert_eq $false // 10 $false; std:assert_eq (is_err ($e 1) // 10) $true; ``` Please note you can combine and chain these operators: `a_func[] /$e -1 // 10`. #### - /? _a_ _default-b_ The extended default value operator is the `/?` operator, which returns an alternative value in case `$none`, `$error` or `$o()` is provided on the left hand side and even unwraps optional values like `$o(10)`. It's the extended version of the default value operator `//`. ```wlambda std:assert_eq $n /? 10 10; std:assert_eq $o() /? 10 10; std:assert_eq $o(20) /? 10 20; std:assert_eq $false /? 10 $false; std:assert_eq ($e 1) /? 10 10; ``` #### - /$n _a_ _default-b_ The `$none` default value operator returns it's right hand side if the left hand side is a `$none` value. ```wlambda std:assert_eq $n /$n 10 10; std:assert_eq $o() /$n 10 $o(); std:assert_eq $o(20) /$n 10 $o(20); std:assert_eq $false /$n 10 $false; std:assert_eq (is_err ($e 1) /$n 10) $true; ``` Please note you can combine and chain these operators: `a_func[] /$n -1 /$o -2`. #### - /$o _a_ _default-b_ The optionals default value operator returns it's right hand side if the left hand side is a `$o()` value. And it unwraps it's left hand side if it is an non empty optional value like eg. `$o(10)`. ```wlambda std:assert_eq $n /$o 10 $n; std:assert_eq $o() /$o 10 10; std:assert_eq $o(20) /$o 10 20; std:assert_eq $false /$o 10 $false; std:assert_eq (is_err ($e 1) /$o 10) $true; ``` Please note you can combine and chain these operators: `a_func[] /$e -1 /$n -2 /$o -3`. #### - /$e _a_ _default-b_ The error default value operator returns it's right hand side if the left hand side is an `$error` value. It's convenient to provide default values only in case an error is returned. It can also be used to ignore errors more conveniently. ```wlambda std:assert_eq $n /$e 10 $n; std:assert_eq $o() /$e 10 $o(); std:assert_eq $o(20) /$e 10 $o(20); std:assert_eq $false /$e 10 $false; std:assert_eq (is_err ($e 1) /$e 10) $false; ``` Please note you can combine and chain these operators: `a_func[] /$e -1 /$o -2 // 0`. ## 7 - String and Byte Vector Formatting WLambda comes with a built in functionality for string (and byte vector) formatting. It works by creating a specialized formatting function from a given string literal at compile time with the `$F"..."` syntax, or a string at runtime with the `std:formatter _str_` function. The formatter syntax is documented in detail at [12.2 String Formatting Syntax](#132-string-formatting-syntax). It is basically the Rust `std::fmt` Syntax with a few extensions for WLambda data types and the dynamically typed nature of WLambda. The WLambda syntax for `$F` is: `$F string-literal`. This means, you can use any WLambda string literal after `$F`: ```wlambda $F"..."; # normal string $F$b"..."; # byte vector $F$q/.../; # normal string, quote syntax $F$Q"..."; # byte vector quote syntax $F$code{ }; # code block string ``` (Please note, that `$code{ ... }` is not as useful in this context, because the formatter placeholders usually are not valid WLambda syntax.) This is a very simple example: ```wlambda !x = "abc"; !s = $F"x = {}" x; std:assert_eq s "x = abc"; ``` You can also use string formatting to generate byte vectors: ```wlambda !bv = $F$b"x={}" $b"\xFF"; std:assert_eq bv $b"x=\xFF"; ``` If you want to generate the WLambda written representation of a piece of data like `std:write_str` would return it, you have to specify the special formatting syntax `{:...!w}`: ```wlambda std:assert_eq ($F"x={:!w}" $&&$[1, 2, 3, 4]) "x=$&&$[1,2,3,4]"; # Without the `!w` the reference would just # be auto dereferenced like `str` would do it: std:assert_eq ($F"x={}" $&&$[1, 2, 3, 4]) "x=$[1,2,3,4]"; ``` #### 7.0.1 - std:formatter _format-string_ Returns a formatting function that takes exactly the arguments specified in the _format-string_. If the format syntax is wrong, an error is returned. This is useful, if you need to build a format string at runtime, because `$F` only allows string/byte vector literals. ```wlambda !fmt = ">6.2"; !fmt_fun = (std:formatter (std:str:cat "{1} [{0:" fmt "}]")); std:assert_eq (fmt_fun 3.43554 1.2323) "1.2323 [ 3.44]"; ``` ### 7.1 - Formatting Numbers Number formatting, that is integers, float and numerical vectors, require an extension of the formatting syntax. You need to specify whether an integer `"{:...!i}"` or a float `{:...!f}` is formatted. Otherwise WLambda will cast everything to a string for formatting. Here are some examples: ```wlambda std:assert_eq ($F "{:8!i}" 123) " 123"; std:assert_eq ($F "{:08!i}" 123) "00000123"; std:assert_eq ($F "{:<8!i}" 123) "123 "; std:assert_eq ($F "{:^8!i}" 123) " 123 "; std:assert_eq ($F "{:>8!i}" 123) " 123"; std:assert_eq ($F "{:8.2!f}" 123.567) " 123.57"; std:assert_eq ($F "{:08.2!f}" 123.567) "00123.57"; std:assert_eq ($F "{:<8.2!f}" 123.567) "123.57 "; std:assert_eq ($F "{:^8.2!f}" 123.567) " 123.57 "; std:assert_eq ($F "{:>8.2!f}" 123.567) " 123.57"; # Note: For floats, the "!f" is implicit if you specify a precision: std:assert_eq ($F "{:8.2}" 123.567) " 123.57"; std:assert_eq ($F "{:08.2}" 123.567) "00123.57"; std:assert_eq ($F "{:<8.2}" 123.567) "123.57 "; std:assert_eq ($F "{:^8.2}" 123.567) " 123.57 "; std:assert_eq ($F "{:>8.2}" 123.567) " 123.57"; ``` You can even format numbers in numerical vectors, data vector, pairs or maps: ```wlambda std:assert_eq ($F "{:8.2}" $f(1.2, 3.456, 8.232)) "( 1.20, 3.46, 8.23)"; std:assert_eq ($F "{:8.2}" $[1.2, 3.456, 8.232]) "[ 1.20, 3.46, 8.23]"; std:assert_eq ($F "{:8.2}" $p(1.2, 3.456)) "( 1.20, 3.46)"; std:assert_eq ($F "{:8.2}" ${x = 1.2, y = 3.456, z = 8.232}) "{x: 1.20, y: 3.46, z: 8.23}"; std:assert_eq ($F "{:>8!i}" $i(1.2, 3.456, 8.232)) "( 1, 3, 8)"; ``` Also hexadecimal, octal and binary are supported for integers, they come after the `!i`: ```wlambda std:assert_eq ($F "{:5!ix}" 321) " 141"; std:assert_eq ($F "{:5!io}" 321) " 501"; std:assert_eq ($F "{:<11!ib}" 321) "101000001 "; std:assert_eq ($F "{:011!ib}" 321) "00101000001"; ``` ## 8 - Data Structure Matchers, Selectors and String Patterns/Regex WLambda comes with a builtin DSL (domain specific language) for shallow data structure matches and deep data structure selection and regular expression (regex) pattern matching on strings. A _selector_ (structure selection) gives you the ability to search deep into WLambda data structures like [CSS Selectors](https://www.w3.org/TR/selectors-3/) into HTML DOM trees or [XPath](https://www.w3.org/TR/xpath-31/) into XML. While a _structure matcher_, as used by the `match` operation, allows you to directly match a certain WLambda piece of data. A subset of the _selectors_ are the _patterns_, which are able to match strings like regular expressions. The syntax of _patterns_ is a bit different from normal regular expressions like Perl, Python or JavaScript has. This is partly due to the fact that these patterns aim to be easily used to match parts of a specific string like filename globs `photo_???_*.jpg`. For an in depth description of the _selector_ and _pattern_ syntax please refer to the [Pattern and Selector Syntax](#821-selector-and-wlambda-regex-syntax). ### 8.1 - Data Structure Matcher This is probably one of the most convenient matching features of WLambda. While selectors (`$S[a / * / b]`) allow searching deep into data structures, the matches allow to efficient precise shallow selection and matching. The `match` operation allows to match a value against multiple matchers, while the `$M ...` syntax allows to define a matcher function for a single match (commonly used in an if expression). For a reference of the matcher syntax see below. #### 8.1.1 - match _value-expr_ _match-pair1_ ... [_default-expr_] The match operation is a very versatile control flow operation. #### 8.1.2 - $M _expr_ This is a structure matcher expression. It will compile _expr_ into a structure matcher function. The reslting function will match it's first argument agianst the match and return a map containing the capture variables (or just an empty map). It will also bind the result map to `$\`. This makes it possible to easily match a data structure in an if statement: ```wlambda !some_struct = $[:TEST, ${ a = 10, b = 1442 }]; if some_struct &> ($M $[sym, ${ a = 10, b = x }]) { std:assert_eq $\.sym :TEST; std:assert_eq $\.x 1442; } { panic "It should've matched!"; }; ``` #### 8.1.3 - Data Structure Matcher Syntax This the the compiletime syntax that is understood by the structure matchers that are used by `$M ...` and `match`. - `$M`, `$M1`, `$M2`, ... in the following table stands for a structure matcher expression. - All other tokens or values stand for themself. | WLambda Value | Semantics | |-|-| | `x` | Matches any value and assigns it to the variable `x`. | | `?` | Matches any value, but does not assign it. | | `x $M $M1 ... $Mn` | Assign the value that matched $M, $M1 or $Mn to the variable `x`. | | `? $M $M1 ... $Mn` | Matches if $M, $M1 or $Mn matches. | | `_*` | Placeholder for 0 or N items that match any items in the vector. | | `_+` | Placeholder for 1 or N items that match any items in the vector. | | `_?` | Placeholder for 0 or 1 items that match any items in the vector. | | `_* $M` | Placeholder for 0 or N items that match $M in the vector. | | `_+ $M` | Placeholder for 1 or N items that match $M in the vector. | | `_? $M` | Placeholder for 0 or 1 items that match $M in the vector. | | `_type? :integer ...` | Matches an element of one of the given types. Symbol names should have the same name as the type names returned by the `type` function. | | `$r/.../` | Matches any element, where it's string contents matches the given pattern. | | `$rg/.../` | Matches any element, where it's string contents matches the given pattern. Returns a list with all global matches. | | `$M1 &or $M2` | Matches if $M1 or $M2 matches. | | `$M1 &and $M2` | Matches if $M1 and $M2 matches. | | `$[$M1, $M2, ...]` | Matches a vector. | | `${ $Mkey1 = $Mval1, ...}`| Matches a map. $Mkey1 can also be a $M match, but keep in mind that maps can only have symbols as keys. You can however match symbols using regex patterns for instance. If you only use symbols as keys in this match, the map access is optimized a bit, because there is no need to iterate over all keys then. | | `$p($M1, $M2)` | Matches a pair. | | `$i($M1, ...)` | Matches an integer vector. | | `$f($M1, ...)` | Matches a float vector. | | `$o($M)` | Matches an optional where the value matches $M. | | `$e $M` | Matches an error value that matches $M. | | `$n` | Matches $none. | | literal values | Literal values like booleans, strings, symbols and numbers match their value. | ### 8.2 - Data Structure Selectors `$S(...)` This section shows how data structure selectors can be used. TODO #### 8.2.1 - Selector and WLambda Regex Syntax: ```ebnf (* NOTE: Whitespace is not part of a pattern in most places. This means if you want to match whitespace, you will have to escape it either with a '\', with a [ ] character class or match one whitespace char with $s. *) class_char = { ?any character except "]"? } (* special sequence: "\^" => "^" and "\\" => "\" and "\]" => "]" *) ; ident_char_in_selector = (* if regex is used inside a selector: *) { ?any character except whitespace, "!", "?", "/", "\", "|", "^", ",", "'", "&", ":", ";", "$", "(", ")", "{", "}", "[", "]", "*" and "="? } (* allows the usual backslash escaping from strings! *) ; ident_char_in_direct_pattern = | (* if regex is used as pattern directly: *) { ?any character except whitespace, "?", "|", "$", "(", ")", "[", "]" and "*"? } (* allows the usual backslash escaping from strings! *) ; ident_char = ident_char_in_direct_pattern | ident_char_in_selector ; ident = ident_char, { ident_char } ; index = digit, { digit } ; rx_atom = pat_glob | ident_char ; glob_atom = pat_glob | ident ; rx_match_mod = "L" (* transforms the input string from the match position on to lower case. *) | "U" (* transforms the input string from the match position on to upper case. *) ; pat_regex = "*", rx_atom (* matches sub pattern 0 or N times *) | "+", rx_atom (* matches sub pattern 1 or N times *) | "<", [ ("*" | "+" | "?") ], rx_atom (* non greedy version of the above *) | "?", rx_atom (* matches sub pattern 0 or 1 times *) | "!", rx_atom (* matches (zero width) if next pattern does not match *) | "=", rx_atom (* matches (zero width) if next pattern does match *) | "^" (* matches (zero width) start of string *) | "$" (* matches (zero width) end of string *) | "s" (* matches one whitespace character *) | "S" (* matches one non-whitespace character *) | "&", rx_match_mod ; glob_group = "(", "^", pattern, ")" (* capturing sub group *) | "(", pattern, ")" (* sub group *) ; class_range = class_char, "-", class_char (* contains a range of chars, eg. [a-z] *) ; glob_cclass = "[", { class_char | class_range }, "]" (* character class match for 1 char *) | "[^", { class_char | class_range }, "]" (* negated character class match for 1 char *) ; pat_glob = "*" (* 0 or N any characters *) | "?" (* any character *) | "$", pat_regex | glob_cclass | glob_group ; pat_branch = { glob_atom } ; pattern = pat_branch, [ "|", pattern ] ; key = index | pattern ; kv = key, "=", pattern ; kv_item = "{", kv, { ",", kv }, "}" ; node_match = ":", ["!"], "(", selector, ")" | ":", ["!"], kv_item | ":", ["!"], "type", "=", pattern (* pattern is matched against vval type as returned by `type` *) | ":", ["!"], "str", "=", pattern (* pattern is matched against the string contents or stringified representation of the value *) ; node_cond = node_match | node_match, "&", node_cond | node_match, "|", node_cond ; reckey_cond = "!", "key", "=", pattern (* recurse only into values if they are not referred to by a key matching the given pattern. *) ; recval_cond = "=", node_cond (* recurse only into values if they match the given condition *) ; node = key, { node_cond } (* marks it for referencing it in the result set *) | "**", [ reckey_cond ], [ recval_cond ], { node_cond } (* deep expensive recursion *) | "^", node ; selector = node, { "/", node } ; ``` #### 8.2.2 - std:selector _string_ Parses the given _string_ as WLambda data structure selector and returns a function that takes a data structure as first argument. That function will then query the data structure according to the given selector. That function will also set the global variable `$\` to the result. The main usage of this function is, when you want to define the selector at runtime. Otherwise WLambda provides the handy `$S(...)` syntax for generating the structure pattern function at compile time. ```wlambda !runtime_name = "foo"; !sel = std:selector (" * / " runtime_name); if sel <& $[${foo = 1}, ${foo = 2}, ${foo = 3}] { std:assert_str_eq $\ $[1,2,3]; } { std:assert $false }; ``` ### 8.3 - String Patterns (Regex) `$r/.../` This section shows how to use the builtin pattern regex engine in WLambda. You can embed patterns directly in your WLambda source with `$rQ...Q`. Where `Q` stands for the usual string quoting mechanism in WLambda (like `$q/foo/`, `$q(foo bar)`, ...). This has the advantage that the pattern syntax is checked on compile time of your WLambda program. The result of the expression `$r/foo/` is a function, which takes as first arguments a string and returns a vector of substrings of that input string. First element of that vector is always the matched sub string of the input string. All elements after that correspond to a pattern capture `(^...)` like in `$r/foo(^bar)/`. The function returns `$none` if the pattern could not be found in the input string. Lets start off with a simple example: ```wlambda # Please note: Whitespace inside the pattern is allowed and will not be matched! !res = $r/a (^*) b/ "fooaxxxxbber"; std:assert_eq res.0 "axxxxbb"; std:assert_eq res.1 "xxxxb"; ``` To match a whole string you can anchor using `$^` and `$$`: ```wlambda !res = $r/$^ a (^*) b $$/ "axxxxbb"; std:assert_eq res.0 "axxxxbb"; std:assert_eq res.1 "xxxxb"; ``` To match special the characters `$` you can use the backslash escaping `\$`: ```wlambda std:assert_eq ($r/$+ \$/ "FF$$$FF").0 "$$$"; ``` To access captured groups you can either use the return value of the matcher function, or use the global variable `$\` which will contain the results of the latest match that was executed: ```wlambda # Notice the usage of the `<&` function call operator: !res = if "foo//\\/foo" &> $r| $<*? (^$+[\\/]) * | { std:assert_eq $\.0 "foo//\\/foo"; $\.1 }; std:assert_eq res "//\\/"; ``` #### 8.3.1 - Global Patterns `$rg/.../` With the `g` modifier the regex can be modified and will match the input string with the given pattern repeatedly and call a given function for each match. The match function will receive the input string as first argument and a function that will be called for each match as second argument. Inside the match function, you can use the control flow functions `break` and `next` to skip ahead. The match function receives the contents of `$\` as first argument, the offset of the match in the input string as second argument and the length of the match as third argument: ```wlambda !found = $@vec $rg/x(^?)y/ "aax9yaaxcy" {!(match, offs, len) = @; $+ $[match.1, offs, len] }; std:assert_str_eq found $[$["9", 2, 3], $["c", 7, 3]]; ``` #### 8.3.2 - Pattern Substitutions `$rs/.../` The `s` modifier creates a substitution that will substitute each match of the pattern in the given input string with the return value of the match function. The match function is called with the same values as `$rg` does. ```wlambda !digits = $["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]; !ret = $rs/[0-9]/ "on 1 at 0 of 8" {!(match, offs, len) = @; digits.(int match.0) }; std:assert_eq ret "on one at zero of eight"; ``` Inside the match function, you can use the control flow functions `break` and `next`. You can use that to control which occurence within the string to replace: ```wlambda !res = $rs/xxx/ "fxxxfxxxfxxxf" { break "O" }; std:assert_eq res "fOfxxxfxxxf"; ``` #### 8.3.3 - Pattern Syntax Overview While [Selector and WLambda Regex Syntax](#821-selector-and-wlambda-regex-syntax) describes the pattern syntax in detail, here is the WLambda pattern regex syntax in a nutshell: | Pattern Syntax | Semantics | |-|-| | `?|$()[]*` | Many special chars are reserved in WLambda patterns. Be aware that more characters are reserved if you use the patterns in a data structure selector, instead of a single pattern. Please escape then using backslash like `\\/` or `[/]`.| | _whitespace_ | Please note, that whitespace to be matched must be escaped using '\' or inside a character calss `[ ]`. | | `\.` | Backslash escapes work the same as in regular WLambda strings. `\` escapes the following character to have no special syntactic meaning in a pattern except matching itself. While escape sequences like `\x41` match the character `A` or `\u{2211}` matches `∑`. These also work inside of character classes. | | `*` | Match 0 to N occurences of any character. | | `?` | Match 1 occurences of any character. | | `(...)` | A match group (does not capture). | | `(^...)` | A capturing match group. | | `[abcA-Z]` | A character class, matching the listed characters or ranges. | | `[^abcA-Z]` | A negative character class, matching all character except the listed characters or ranges. | | `$^` | String start anchor. Matches only the start of the string. Useful for specifying patterns that match the complete string (in combination with `$$`). | | `$$` | String end anchor. Matches only the end of the string. Useful for specifying patterns that must match the complete string. | | `$*X` | Greedly matches the pattern part `X` 0 or N times. For grouping pattern parts use `(...)` like in `$*(abc)`. | | `$<*X` | Non-greedly matches the pattern part `X` 0 or N times. | | `$+X` | Greedly matches the pattern part `X` 1 or N times. For grouping pattern parts use `(...)` like in `$+(abc)`. | | `$<+X` | Non-greedly matches the pattern part `X` 1 or N times. | | `$?X` | Greedly matches 0 or 1 occurences of the pattern part `X`. Like usual, you can group using `(...)`. | | `$8.3.4 - Standard Regular Expressions Please note that WLambda can optionally be compiled with the `regex` crate, which implements a more common syntax for regular expressions. Please refer to the functions `std:re:match` in the WLambda standard library for this. #### 8.3.5 - std:pattern _string_ [_mode_] Compiles the regex pattern _string_ to a function just like `$r/.../` would do. The _mode_ can either be `:g` (global match like `$rg...`), `:s` (substitution like `$rs...`) or `$none`. Useful for composing WLambda patterns at runtime: ```wlambda !rx = std:pattern ~ std:str:cat "(^" "$+" "[a-z]" ")"; std:assert_eq (rx "foo").1 "foo"; ``` Returns an error if the syntax failes to parse as pattern: ```wlambda !err = unwrap_err ~ std:pattern "($+[a-z]"; std:assert_eq $i(0, 11)[err] "bad pattern"; ``` Here an example of substitution: ```wlambda !subs = std:pattern "$+x" :s; std:assert_eq subs["fooxxxoxx", \"a"] "fooaoa"; ``` ## 9 - Modules ### 9.1 - export ```wlambda !expr = { _ + 30 }; !@export symbol = expr; # exports symbol with value of expr (a function) ``` **Warning:** Do not expect the declared variables in the module to exist beyond execution time. Weak caught values will vanish like usual once the module scope is exited. This means, if you declare helper functions in local variables, do this with the `:global` modifier: ```wlambda !:global helper = { _ * 2 }; !@export doit = { helper 10 }; ``` Alternatively make the helper a strong reference: ```wlambda !helper = $&& { _ * 2 }; !@export doit = { helper 10 }; ``` ### 9.2 - import ```wlambda !@import x = tests:test_mod; # prefixes everything from modixes with x: std:assert ~ (x:symbol 10) == 40; ``` You can also skip the prefix: ```wlambda !@import std; !v = $[]; std:push v 10; std: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. #### 10.0.1 - type _value_ Returns the name of the data type of _value_ as string. ```wlambda std:assert_eq (type 10) "integer"; std:assert_eq (type 10.0) "float"; std:assert_eq (type {}) "function"; !y = $&&std:to_drop { }; std:assert_eq (type y) "drop_function"; std:assert_eq (type :s) "symbol"; std:assert_eq (type "s") "string"; std:assert_eq (type $[]) "vector"; std:assert_eq (type ${}) "map"; std:assert_eq (type $b"") "bytes"; std:assert_eq (type $n) "none"; std:assert_eq (type $t) "bool"; std:assert_eq (type $e $n) "error"; std:assert_eq (type $&&10) "ref_strong"; std:assert_eq (type $&10) "ref_hidden"; !x = $&&10; std:assert_eq (type ~ $w&x) "ref_weak"; ``` #### 10.0.2 - len _value_ Returns the length of _value_. Depending on the data type you will get different semantics. ```wlambda # Always zero for scalar non sequential/collection values: std:assert_eq (len 10) 0; std:assert_eq (len 10.1) 0; std:assert_eq (len $t) 0; std:assert_eq (len $f) 0; std:assert_eq (len $n) 0; std:assert_eq (len "\xFF") 2; # byte length of the UTF-8 string std:assert_eq (len $b"\xFF") 1; std:assert_eq (len $[1,2,3,4,5]) 5; std:assert_eq (len ${a=1, b=2}) 2; std:assert_eq (len ${a=1, b=2}) 2; ``` #### 10.0.3 - panic _message_ If your program runs into something that deserves a slap on the fingers of the developer you can use `panic` to do that. ## 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: ```wlambda std:srand 1234; !vec = $[1,2,3,4,5,6,7,8]; std:shuffle { std:rand :i64 } vec; std:assert_eq (str vec) "$[2,1,7,4,8,5,3,6]"; ``` An Example with std:rand:split\_mix64\_next: ```wlambda !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:delete _vector-or-map_ _index-or-key_ This removes the designated element from the collection (either vector or map). This works for: - Vectors: ```wlambda !v = $[1,2,3]; std:assert_eq (std:delete v 1) 2; std:assert_eq (str v) (str $[1,3]); ``` - Maps: ```wlambda !m = ${a = 10, b = 20}; std:assert_eq (std:delete m :a) 10; std:assert_eq (str m) (str ${b = 20}); ``` Please note that this operation is potentially O(n) on vectors. #### 11.0.3 - std:ref\_id _value_ Returns an integer identifier for a given referential value. The ID will stay the same as long as the reference is allocated. This returns a value for all data types that have some form of internal reference to a value on the heap. The main usage of this function is to get a pre process unique ID for an allocated value. But be aware, that once the value is deallocated, the reference ID does not belong to that value anymore. ```wlambda !v = $[1,2,3]; !v_id1 = std:ref_id v; std:push v 4; !v_id2 = std:ref_id v; std:assert_eq v_id1 v_id2; ``` #### 11.0.4 - std:copy _vec\_or\_map_ Makes a shallow copy of the given vector or map. ```wlambda !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.5 - std:values _collection-or-iter_ This function returns all values in the given collection or iterator as vector. _collection-or-iter_ can have be one of the following data types: - vector - numerical float or integer vector - map - iterator `$iter` ```wlambda std:assert_str_eq (std:values $iter 0 => 5) $[0,1,2,3,4]; std:assert_str_eq (std:values ${a = 10}) $[10]; std:assert_str_eq (std:values $iter ${a = 10}) $[10]; std:assert_str_eq (std:values $[1,2,3]) $[1,2,3]; std:assert_str_eq (std:values $i(1,2,3)) $[1,2,3]; ``` #### 11.0.6 - std:keys _collection-or-iter_ This function returns all keys in the given _collection_ or _iterator_. It's most useful for the map data type, but also returns the indices in a vector or numerical vector. ```wlambda std:assert_str_eq (std:keys ${a = 10}) $["a"]; std:assert_str_eq (std:keys $iter ${a = 10}) $["a"]; std:assert_str_eq (std:keys $[3,3,3]) $[0,1,2]; std:assert_str_eq (std:keys $i(4,4,4)) $[0,1,2]; std:assert_str_eq (std:keys $i(4,4)) $[0,1]; ``` #### 11.0.7 - 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). ```wlambda !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.8 - 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 | ```wlambda 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.9 - 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 | ```wlambda 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.10 - std:cmp:str:asc _a_ _b_ Compares _a_ and _b_ lexicographically by their byte values. This orders Unicode code points based on their positions in the code charts. | Cases | Return Value | |---------------|--------------| | _a_ > _b_ | -1 | | _a_ == _b_ | 0 | | _a_ < _b_ | 1 | ```wlambda std:assert_eq (std:cmp:str:asc "abc" "aba") -1; std:assert_eq (std:cmp:str:asc "abc" "abc") 0; std:assert_eq (std:cmp:str:asc "abc" "abd") 1; ``` #### 11.0.11 - std:cmp:str:desc _a_ _b_ Compares _a_ and _b_ lexicographically by their byte values. This orders Unicode code points based on their positions in the code charts. | Cases | Return Value | |---------------|--------------| | _a_ > _b_ | 1 | | _a_ == _b_ | 0 | | _a_ < _b_ | -1 | ```wlambda std:assert_eq (std:cmp:str:desc "abc" "aba") 1; std:assert_eq (std:cmp:str:desc "abc" "abc") 0; std:assert_eq (std:cmp:str:desc "abc" "abd") -1; ``` #### 11.0.12 - std:reverse _value_ Reverses the given sequence of values. This works for following data types: - Strings - Byte vectors - Vectors - Numeric vectors - Iterators ```wlambda std:assert_str_eq (std:reverse $[1, 2, 3, 4]) $[4,3,2,1]; std:assert_str_eq (std:reverse "ABC") "CBA"; std:assert_str_eq (std:reverse $b"ABC") $b"CBA"; std:assert_str_eq (std:reverse $i(1,2,3,4)) $i(4,3,2,1); std:assert_str_eq (std:reverse $f(1.1,2.2,3.3,4.4)) $f(4.4,3.3,2.2,1.1); ``` #### 11.0.13 - 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: ```text std:displayln "foo" ``` Will just print `foo` and a newline. If you need a less ambigous form, use `std:writeln`, which handles its argument like written via `std:ser:wlambda` instead of `str`. #### 11.0.14 - $DEBUG _arg1_ ... This is a special value that evaluates to a print function that supplies the current position in the source code. For example this: ```wlambda !k = $[1, 2, 3]; $DEBUG "I got values:" k 99; ``` Will print this (assuming it's at line 1 col 3 in file `file_foo.wl`): ```text [1,3:] DEBUG: "I got values:"(string) $[1,2,3](vector) 99(integer) ``` In case you want to directly write a string or some value, you will have to prefix the argument with the symbol `:\`: ```wlambda !k = 30; $DEBUG :\ "k =" :\ k; ``` Will print like this: ```text [1,11:] DEBUG: k = 30 ``` #### 11.0.15 - std:writeln _arg1_ ... This function writes the WLambda representation of its arguments (with a space inbetween) to standard output. This means that: ```text std:displayln "foo" ``` Will print `"foo"` and a newline. See also the description of `std:ser:wlambda`. If you need a more human readable form use `std:displayln`. #### 11.0.16 - 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. ```wlambda std:assert_eq (std:eval "1 + 2") 3; !:global X = 20; std:assert_eq (std:eval "1 + X") 21; ``` #### 11.0.17 - 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. ```norun_wlambda std:assert $false; #=> Panic std:assert 120; #=> 120 ``` #### 11.0.18 - std:assert\_eq _actual_ _expected_ \[_message_] This function checks if the _actual_ value is equal to the _expected_ value and panics if not. The optional _message_ is passed in the panic for reference. ```wlambda !x = 30 * 2; std:assert_eq x 60 "30 * 2 == 60"; ``` #### 11.0.19 - std:assert\_str\_eq _actual_ _expected_ This function stringifies _actual_ and _expected_ using the `str` function and compares the resulting strings. This is very useful to compare data structures, as map keys are sorted if the maps are stringified using `str`: ```wlambda std:assert_str_eq $[1, 2, 3] $[1, 2, 3]; ``` #### 11.0.20 - std:assert\_rel\_eq _l_ _r_ _epsilon_ \[_message_] This function checks if `l` is within `epsilon` of `r`. If the absolute value of the difference between `l` and `r` is greater than `epsilon`, this function will panic, also displaying the optional message if present. ```wlambda # these two are within 1 of each other !x = 10.5; !y = 11.3; std:assert_rel_eq x y 1; # but not within 0.5 of each other, so this line is commented out. # std:assert_eq x y 0.5; ``` #### 11.0.21 - std:measure\_time _unit_ _function_ This function measures the time the given _function_ took to execute. The _unit_ defines how precisely the time is measured. Following strings are supported units: - `s` - seconds - `ms` - milliseconds - `us` - microseconds - `ns` - nanoseconds The return value is a vector where the first element is the time it took to execute the function, and the second element is the return value of that function. ```wlambda !res = std:measure_time :ns { $@i iter i 0 => 100000 { $+ i } }; std:assert res.0 > 100; std:assert_eq res.1 4999950000; ``` ### 11.1 - I/O #### 11.1.1 - std:io:line Reads a line from standard input and returns it. Returns an error if something went wrong. ```text !line = unwrap std:io:line[]; std:displayln "you entered: " std:str:trim_end[line]; ``` #### 11.1.2 - std:io:lines [_value_] Calls the given _value_ for each line in standard input until EOF and returns the last returned value from that call. If _value_ is not given, all lines will be appended to a new vector and returned. Returns an error if some IO error occured. ```text !lines = std:io:lines[]; !lines = $@v std:io:lines $+; std:io:lines {!(line) = @; std:displayln "You entered: [" std:str:trim[line] "]"; }; ``` #### 11.1.3 - std:io:file:read\_text _filename_ Opens the file _filename_ and returns its contents interpreted as UTF8 text as string. ```wlambda 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.4 - std:io:file:read _filename_ Opens the file _filename_ and returns its contents as byte buffer. ```wlambda 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.5 - 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.6 - 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. #### 11.1.7 - std:io:stdout:newline Writes a newline to standard output. Returns an error if an error occured or `$true` otherwise. #### 11.1.8 - std:io:stdout:flush Flushes the standard output buffer. Returns an error if an error occured or `$true` otherwise. #### 11.1.9 - std:io:stdout:print _value_ Writes the given _value_ to standard output in a human readable form like `std:displayln` does. Returns an error if an error occured. `$true` if everything is fine. ```text std:io:stdout:write "xxx"; # => Writes `xxx` to standard output ``` #### 11.1.10 - std:io:stdout:write _value_ Writes a WLambda representation of _value_ to standard output. Returns an error if an error occured. `$true` if everything is fine. ```text std:io:stdout:write "xxx"; # => Writes `"xxx"` to standard output ``` #### 11.1.11 - std:io:flush _handle_ Flushes the internal buffers of _handle_. _handle_ can be any kind of IO handle, like a file handle or networking socket. ```text !socket = unwrap ~ std:net:tcp:connect "127.0.0.1:80"; std:io:write socket $b"GET / HTTP/1.0\r\n\r\n"; std:io:flush socket; ``` #### 11.1.12 - std:io:read\_some _handle_ Reads some amount of data from _handle_. The default maximum amount of bytes read is 4096. This function returns `$o(bytes)` if something was read. It returns `$o()` when EOF is encountered. `$none` is returned when the IO operation was interrupted or did timeout. An `$error` is returned if some kind of error happened, like loss of TCP connection. Here is an example how to read everything from a socket until EOF is encountered: ```text !socket = unwrap ~ std:net:tcp:connect "127.0.0.1:80"; std:io:write socket $b"GET / HTTP/1.0\r\n\r\n"; std:io:flush socket; !buf = $b""; !done = $f; while not[done] { match std:io:read_some[socket] $o(buf) => { .buf = buf +> $\.buf; } $o() => { .done = $t; } ($e _) => { .done = $t; }; }; ``` #### 11.1.13 - std:io:write _handle_ _data_ [_offs_] Write all data as byte vector to the IO _handle_ (socket, file handle, ...), starting at _offs_ (0 default). Returns the number of bytes written, an error or `$none` if interrupted. Note: This function does not respect the underlying write timeout in _handle_ properly. ```text !socket = unwrap ~ std:net:tcp:connect "127.0.0.1:80"; std:io:write socket $b"GET / HTTP/1.0\r\n\r\n"; ``` #### 11.1.14 - std:io:write\_some _handle_ _data_ Try to write some data as byte vector to the IO _handle_ (socket, file handle, ...), starting at _offs_ (0 default). Returns the number of bytes written, an error or `$none` if a timeout or interrupt ended the write operation. ```text !socket = unwrap ~ std:net:tcp:connect "127.0.0.1:80"; !bytes = $b"GET / HTTP/1.0\r\n\r\n"; !written = 0; while len[bytes] > written { match (std:io:write_some socket bytes written) $o(n) => { .written += n; } $none => {} ($e _) => { break[] }; } ``` ### 11.2 - Networking #### 11.2.1 - std:net:tcp:connect _socket-addr_ [_connect-timeout_] This tries to connect to _socket-addr_. If a _connect-timeout_ is given, it will try to connect within that time. Please note that the returned sockets are thread safe and can be passed to another thread via an _Atom_, _Atom Value Slot_ or _Channel_ for instance. _socket-addr_ can be: - A pair `$p(host, port)` - A string like "host:port". - A pair within a pair to specify whether to use IPv4 or IPv6 addresses only: - `$p(:v4, "host:port")` - `$p(:v4, $p(host, port))` - `$p(:v6, "host:port")` - `$p(:v6, $p(host, port))` About _connect-timeout_ see std:thread:sleep. ```test !socket = match (std:net:tcp:connect "127.0.0.1:8328") ($e err) => { panic ("Couldn't connect: " + str[$\.err]) } socket => $\.socket; ``` #### 11.2.2 - std:net:tcp:listen _socket-addr_ _function_ Tries to bind a local port to _socket-addr_ (see std:net:tcp:connect about _socket-addr_. Note: you can't use `$p(:v4 / :v6, ...)`). Returns an error if something bad happened. For every new connection _function_ is called with the socket as first argument: ```text unwrap ~ std:net:tcp:listen "0.0.0.0:8292" {!(socket) = @; std:io:write socket "Hello!\r\n"; }; ``` Please note that you can share the socket with other threads, see also `std:net:tcp:connect`. #### 11.2.3 - std:net:udp:new _socket-addr_ [_connect-addr_] Creates a new UDP socket and binds it to an endpoint. The arguments _socket-addr_ and _connect-addr_ have the same properties as the _socket-addr_ that `std:net:tcp:connect` receives. If _connect-addr_ is given, a connected UDP port is created and `std:net:udp:send` does not need to pass a _socket-addr_. Returns a socket or an error. The socket can be shared between threads, so you can have a receiving thread and a sending one. ```wlambda !socket = std:net:udp:new "0.0.0.0:31889"; std:net:udp:send socket $b"TEST" "127.0.0.1:31888"; ``` Here is a more elaborate example using threads: ```wlambda !hdl = std:thread:spawn $code { !socket = std:net:udp:new "0.0.0.0:31889"; _READY.send :ok; !(data, addr) = std:net:udp:recv socket; std:displayln "PING" data; unwrap ~ std:net:udp:send socket ("Test:" data) addr; }; hdl.recv_ready[]; !socket = std:net:udp:new "0.0.0.0:31888" "127.0.0.1:31889"; unwrap ~ std:net:udp:send socket $b"XYB123"; !(data, addr) = unwrap ~ std:net:udp:recv socket; std:displayln "PONG" data; std:assert_eq data $b"Test:XYB123"; hdl.join[]; ``` #### 11.2.4 - std:net:udp:send _socket_ _data_ [_socket-addr_] Sends the _data_ to the given _socket-addr_ or to the connected address of the _socket_. Returns the number of bytes sent or an error. ```wlambda !socket = std:net:udp:new "0.0.0.0:31889"; std:net:udp:send socket $b"TEST" "127.0.0.1:31888"; ``` #### 11.2.5 - std:net:udp:recv _socket_ [_byte-count_] Receives _byte-count_ number of bytes from the given _socket_. If _byte-count_ is omitted 512 is assumed. Returns the byte vector with the data and the endpoint address that it was received from. Or an error. ```text !socket = std:net:udp:new "0.0.0.0:31889"; !(buf, addr) = std:net:udp:recv socket; ``` ### 11.3 - Processes This chapter documents how to execute new processes. #### 11.3.1 - std:process:run _executable-path_ [_arguments_] Starts the given _executable-path_ with the given (but optional) _arguments_. _arguments_ can be a vector or an iterator. A data structure containing information about the finished child process or an error is returned if something went wrong. This function will block the current thread while the child process is executed. It collects the output of the child process and returns it in a data structure of the following form: ```text { status = 0, # exit code success = $true, # $true or $false stdout = $b"...", # data printed to stdout stderr = $b"...", # data printed to stderr } ``` Here is an example for Linux: ```text !ret = unwrap ~ std:process:run "sh" $["-c", "echo \"test\""]; std:assert_eq ret.status 0; std:assert_eq ret.success $true; std:assert_eq ret.stdout $b"test\n"; std:assert_eq ret.stderr $b""; ``` #### 11.3.2 - std:process:spawn _executable-path_ _arg-vector_ [:inherit\_out | :inherit\_all] Like `std:process:run` starts a process from the _executable-path_ and _arg-vector_. But it does not wait until the process finished running, it returns a child process handle or an error if something went wrong. The handle can then be used by functions like: * `std:process:kill_wait` - to kill and wait for the process to exit * `std:process:try_wait` - to check if the process exited * `std:process:wait` - to wait until the process exits The third argument specifies what happens with the standard I/O file handles. By default the child process gets _null_ handles, so neither output is captured nor input is passed: * _default_ - child process gets _null_ handles for stdin, stdout and stderr. * `:inherit_out` - child process inherits stdout and stderr, but stdin will be _null_. * `:inherit_all` - child process inherits all (stdout, stderr and stdin) from the parent and uses them until it exits. TODO: Implement pipe to/from the child process to be read/written to via `std:io:read_some` and `std:io:write`. ```wlambda !hdl = unwrap ~ std:process:spawn "bash" $[ "-c", "for i in `seq 0 10`; do echo $i; sleep 0.2; done; exit 20" ]; # do something in your program.... !result = unwrap ~ std:process:wait hdl; std:assert ~ not result.success; std:assert result.status == 20; ``` #### 11.3.3 - std:process:try\_wait _child-handle_ Checks if the child process behind _child-handle_ exited. Returns `$none` if it did not exit yet. Returns a map with the structure `${ status = ..., success = $true / $false }` if the child exited. Or an error if something failed. ```wlambda !hdl = unwrap ~ std:process:spawn "bash" $[ "-c", "for i in `seq 0 10`; do echo $i; sleep 0.2; done; exit 20" ]; !counter = 0; !ret = $none; while $true { std:thread:sleep :ms => 250; .counter += 1; .ret = unwrap ~ std:process:try_wait hdl; if ret { break ret; }; }; std:assert counter > 0; std:assert ~ not ret.success; std:assert ret.status == 20; ``` #### 11.3.4 - std:process:kill\_wait _child-handle_ Kills the child process behind _child-handle_ and waits for it to return the exit status. Returns a map with the structure `${ status = ..., success = $true / $false }` if the child exited. Or an error if something failed. ```wlambda !hdl = unwrap ~ std:process:spawn "bash" $[ "-c", "for i in `seq 0 10`; do echo $i; sleep 0.2; done; exit 20" ]; !res = std:process:kill_wait hdl; std:assert ~ not res.success; std:assert_eq res.status -1; ``` #### 11.3.5 - std:process:wait _child-handle_ Waits until the child process behind _child-handle_ exits by itself. Returns a map with the structure `${ status = ..., success = $true / $false }` if the child exited. Or an error if something failed. ```wlambda !hdl = unwrap ~ std:process:spawn "bash" $[ "-c", "for i in `seq 0 10`; do echo $i; sleep 0.2; done; exit 20" ]; !res = std:process:wait hdl; std:assert ~ not res.success; std:assert_eq res.status 20; ``` ### 11.4 - File System #### 11.4.1 - std:fs:rename _file-path_ _new-file-name_ Renames the file at _file-path_ to the new name _new-file-name_. This usually does only work on a single file system. Returns `$true` if renaming was successful, and an error object if it was not successful. #### 11.4.2 - std:fs:copy _src-file-path_ _dst-file-path_ Copies the file _src-file-path_ to the _dst-file-path_. Returns an error if something went wrong. #### 11.4.3 - std:fs:read\_dir _path_ _function_ Calls _function_ with the first argument being the directory entry as map of this structure: ```wlambda ${ atime = 1587628635, # seconds since UNIX Epoch ctime = 1557382099, # seconds since UNIX Epoch mtime = 1557382099, # seconds since UNIX Epoch len = 478, # bytes name = "test", # file name path = "..\\test", # path name read_only = $false, # read only flag type = :f # possible values: # - :f for files # - :l for symlinks # - :d for directories } ``` If the _function_ is called with a directory, you can recurse into that directory by returning a `$true` value. You can format the timestamps using `std:chrono:format_utc`. #### 11.4.4 - std:fs:remove\_file _file-path_ Removes the file at the given _file-path_. Returns an error if the file is missing or some other error occured. #### 11.4.5 - std:fs:remove\_dir _dir-path_ Removes the dir at the given _dir-path_. Returns an error if the dir is missing, is not empty or some other error occured. #### 11.4.6 - std:fs:remove\_dir\_all _dir-path_ Removes the given _dir-path_ recursively. Use with care! Returns an error if the directory does not exist. ### 11.5 - System This chapter contains a few system as in _operating system_ related functions. #### 11.5.1 - std:sys:os Returns the name of the operating system. These are some possible values: - linux - macos - ios - freebsd - dragonfly - netbsd - openbsd - solaris - android - windows ```wlambda std:assert (len std:sys:os[]) > 0; ``` ### 11.6 - Threading WLambda leverages the `std::thread` implementation of Rust's standard library to provide safe threading. Threading works by spawning new threads that get sent a piece of WLambda code (as string) and some arguments. Most WLambda data can be shared between threads. An exception are UserData values that are not thread safe. Also things like sharing cyclic data structures are not possible, as the references are currently broken up. Sharing data is done by WLambda by transforming the _VVal_ data structures into a thread safe shareable represenation called _AVal_. An AVal is a deep copy of the original VVal and can additionally contain atoms (see `std:sync:atom:new`), MPSC queues (see `std:sync:mpsc:new`) and value slots (see `std:sync:slot:new`). The scope of threading in WLambda is primarily to provide a way to do asynchronous work and not so much for high performance computing. You can of course enhance performance a bit with threading, but if you want to do heavy computing I recommend implementing your algorithm in Rust instead. You can of course still manage thread orchestration in WLambda if you just provide a simple function API to your algorithms. #### 11.6.1 - std:thread:spawn _string_ [_globals-map_] This evaluates the given _string_ as WLambda code in a new thread. It returns a thread handle, that can be used to join the thread or wait for it to have properly started. The new thread starts out with a completely empty global environment. If you need any builtin functions, use `!@wlambda`, `!@import std` and other import statements to load the needed functions. If a _globals-map_ is given, inside the thread the given global variables will be set to the given value. Inside the thread, a global variable called `_READY` is set to an atomic slot, which can be used to signal the parent thread that the new thread has successfully spawned. This is a very basic example how to calculate something in a worker thread and wait in a blocking manner: ```wlambda !handle = std:thread:spawn $code { # Attention: Even if this example does not use any # built in functions, it's a good practice # to load them into the global environment # of the thread! !@wlambda; !@import std; !sum = $@i iter i 0 => 10 { $+ i; }; _READY.send $[:ok, sum]; 100 }; std:assert_str_eq handle.recv_ready[] $[:ok, 45]; !res = handle.join[]; std:assert_str_eq res 100; ``` Here an example on how to pass values to the thread. Please be aware, that only a limited set of data types can be passed to a thread. Functions and iterators can not be passed for instance. ```wlambda !globals = ${ a = 99, b = $[1, 3, 4], }; !handle = std:thread:spawn $code { a + b.1 } globals; !res = handle.join[]; std:assert_str_eq res 102; ``` #### 11.6.2 - std:thread:sleep _duration_ Lets the current thread sleep for the given _duration_. _duration_ can either be an integer that will be interpreted as milliseconds to sleep. Or a pair, containing the duration unit as first element and the integer as second element. Following units are supported: - `$p(:s, _seconds_)` - `$p(:ms, _milliseconds_)` - `$p(:us, _microseconds_)` - `$p(:ns, _nanoseconds_)` ```wlambda !before = std:time:now :ms; !thrd = std:thread:spawn $code { !@import std; std:thread:sleep :ms => 150; }; thrd.join[]; !after = std:time:now :ms; std:assert (after - before) >= 150; ``` #### 11.6.3 - Thread Handle API ##### 11.6.3.1 - thdl.join This method will wait for the thread to finish and return the return value of the thread. ```wlambda !thdl = std:thread:spawn "4 + 3"; std:assert_eq thdl.join[] 7; ``` This method will return an error if the thread handle was already joined. ##### 11.6.3.2 - thdl.recv\_ready Waits for the global `_READY` atomic value slot to be sent a value by the thread. This is useful for waiting until the thread has started without an error before expecting it to run properly. If an error happens, you will receive an error object as return value of `recv_ready`: ```wlambda !thdl = std:thread:spawn $code { !@wlambda; # importing `panic` panic "SOME ERR"; _READY.send :ok; }; !err_msg = match thdl.recv_ready[] ($e err) => { $\.err.0 } v => $\.v; $DEBUG err_msg; std:assert err_msg &> $r/ *SOME\ ERR* /; thdl.join[]; ``` This method might return an error if the thread provider made a handle without a ready slot. #### 11.6.4 - Atom API For threads a VVal (WLambda data value) is transformed into a value that can be shared between threads safely. For this the data values are cloned deeply and transformed into a structure of atomic values. These values are then stored in a so called _Atom_. They can be safely changed by threads. ##### 11.6.4.1 - std:sync:atom:new _value_ Creates an Atom, containing the given _value_. The data types for _value_ is limited to these: - Numbers (Integer, Float) - Numerical Vectors - Vectors - Maps - Strings - Symbols - Byte vectors - Pairs - Booleans - Optionals - Errors And also these special types: - Atom - Atom Value Slot - Channel ```wlambda !at = std:sync:atom:new $[1, 2, 3]; !thdl = std:thread:spawn $code { at.write ~ $@i iter i at.read[] { $+ i; }; _READY.send :ok; } ${ at = at }; thdl.recv_ready[]; std:assert_eq at.read[] 6; thdl.join[] ``` ##### 11.6.4.2 - atom.read Returns the value stored in the atom. ```wlambda !at = std:sync:atom:new 99; std:assert_eq at.read[] 99; ``` This method might return an error if the internal mutex was poisoned. ##### 11.6.4.3 - atom.write _value_ Overwrites the contents of the atom with the given _value_. ```wlambda !at = std:sync:atom:new 99; at.write 100; std:assert_eq at.read[] 100; ``` This method might return an error if the internal mutex was poisoned. ##### 11.6.4.4 - atom.swap _value_ Returns the previous value of the atom and writes in the given _value_. ```wlambda !at = std:sync:atom:new 99; std:assert_eq at.swap[100] 99; std:assert_eq at.read[] 100; ``` This method might return an error if the internal mutex was poisoned. #### 11.6.5 - Atom Value Slot API An Atom value slot offers more synchronization than a normal Atom value. It allows you to set the value of the slot, wait for it to be collected and wait for a value becoming available. It can be thought of a single element queue, where the element will be overwritten when a new value is sent to the slot. You can theoretically receive or wait from multiple threads and also write from multiple threads. But be aware, that the order of which threads get to read or write is determined by the operating system and might lead to reader or writer starvation. Best recommendation here is to use a slot only from a single writer and a single reader. ##### 11.6.5.1 - std:sync:slot:new Constructs a new Atom slot and returns it. The slot has the initial status of being _empty_. If a value is sent to it, it will not be _empty_ anymore. After a value is received from the slot, the status is _empty_ again. ##### 11.6.5.2 - atom\_slot.send _value_ This method sends the value into the slot, overwriting any previous set values. The slot can also ha ```wlambda !slot = std:sync:slot:new[]; std:assert slot.check_empty[]; slot.send $[:ok, $i(1,2,3)]; std:assert ~ not slot.check_empty[]; std:assert_eq slot.recv[].1 $i(1,2,3); std:assert slot.check_empty[]; ``` This method might return an error if there was an issue with locking the internal mutex or the mutex was poisoned. ##### 11.6.5.3 - atom\_slot.recv If the slot is empty, it will wait for a value to become available. Once a value is available it is returned and the slot is set to _empty_ again. ```wlambda !slot = std:sync:slot:new[]; !thrd = std:thread:spawn $code { slot.send 99; } ${ slot = slot }; std:assert_eq slot.recv[] 99; thrd.join[]; ``` This method might return an error if there was an issue with locking the internal mutex or the mutex was poisoned. ##### 11.6.5.4 - atom\_slot.try\_recv This method returns an optional value. It will provide an empty optional value if no value is stored in the slot. But if the slot contains a value, it will return the value (wrapped in an optional) and set the slot to be _empty_ again. ```wlambda !slot = std:sync:slot:new[]; !start_flag = std:sync:slot:new[]; !thrd = std:thread:spawn $code { start_flag.recv[]; # sync with parent slot.send 99; _READY.send :ok; } ${ slot = slot, start_flag = start_flag }; std:assert_eq slot.try_recv[] $o(); start_flag.send :ok; thrd.recv_ready[]; std:assert_eq slot.try_recv[] $o(99); thrd.join[]; ``` This method might return an error if there was an issue with locking the internal mutex or the mutex was poisoned. ##### 11.6.5.5 - atom\_slot.recv\_timeout _duration_ Acts like `atom_slot.recv`, but it will only wait for the given _duration_. If no value was received in the given _duration_ (see std:thread:sleep), `$o()` is returned. Otherwise the optional value will contain the received value. ```wlambda !slot = std:sync:slot:new[]; std:assert_eq (slot.recv_timeout :ms => 100) $o(); slot.send 4; std:assert_eq (slot.recv_timeout :ms => 100) $o(4); ``` This method might return an error if there was an issue with locking the internal mutex or the mutex was poisoned. ##### 11.6.5.6 - atom\_slot.check\_empty Returns `$true` if the slot is empty. This method might return an error if there was an issue with locking the internal mutex or the mutex was poisoned. ##### 11.6.5.7 - atom\_slot.wait\_empty Waits until the slot is empty and then returns `$true`. This method might return an error if there was an issue with locking the internal mutex or the mutex was poisoned. ##### 11.6.5.8 - atom\_slot.wait\_empty\_timeout _duration_ Waits a predefined timeout until the slot is empty. If it did become empty within the given _duration_ (see std:thread:sleep) it will return `$true`. Otherwise it will return `$false`. This method might return an error if there was an issue with locking the interal mutex or the mutex was poisoned. #### 11.6.6 - Channel API A channel is a multiple sender, single consumer queue. It can be used to establish a message passing based communication between threads. It is basically a wrapper around the Rust `std::sync::mpsc::channel`. ```wlambda !chan = std:sync:mpsc:new[]; !thdl = std:thread:spawn $code { _READY.send :ok; iter i 0 => 10 { chan.send $p(:val, i); }; chan.send $p(:quit, $none); } ${ chan = chan }; match thdl.recv_ready[] ($e ?) => { std:assert $false }; !item = $none; !sum = $@i while { .item = chan.recv[]; item.0 == :val } { $+ item.1; }; std:assert_eq sum 45; thdl.join[]; ``` ##### 11.6.6.1 - std:sync:mpsc:new This creates a new channel. You can safely send from multiple threads while reading from one thread at a time. ```wlambda !chan = std:sync:mpsc:new[]; chan.send :a; chan.send :b; chan.send :c; std:assert_eq chan.recv[] :a; std:assert_eq chan.recv[] :b; std:assert_eq chan.recv[] :c; ``` ##### 11.6.6.2 - channel.send _value_ Sends the given _value_ to the channel queue. ```wlambda !chan = std:sync:mpsc:new[]; chan.send :a; std:assert_eq chan.recv[] :a; ``` This method might return an error if the channel failed, for instance due to a poisoned internal mutex. ##### 11.6.6.3 - channel.recv Receives the next element from the channel. If no element is available this method will block the thread until an element becomes available. ```wlambda !chan = std:sync:mpsc:new[]; chan.send :a; std:assert_eq chan.recv[] :a; ``` This method might return an error if the channel failed, for instance due to a poisoned internal mutex. ##### 11.6.6.4 - channel.try\_recv Tries to receive the next element from the channel and return it wrapped into an optional. If no element is available an empty optional `$o()` is returned. ```wlambda !chan = std:sync:mpsc:new[]; std:assert_eq chan.try_recv[] $o(); chan.send :a; std:assert_eq chan.try_recv[] $o(:a); ``` This method might return an error if the channel failed, for instance due to a poisoned internal mutex. ##### 11.6.6.5 - channel.recv\_timeout _duration_ Tries to receive the next element in the given _duration_ (see std:thread:sleep) and return it wrapped into an optional. If no element could be received within that time an empty optional is returned `$o()`. ```wlambda !chan = std:sync:mpsc:new[]; std:assert_eq (chan.recv_timeout $p(:ms, 100)) $o(); chan.send :x; std:assert_eq (chan.recv_timeout $p(:ms, 100)) $o(:x); ``` This method might return an error if the channel failed, for instance due to a poisoned internal mutex. ## 12 - Optional Standard Library ### 12.1 - serialization #### 12.1.1 - std:ser:wlambda _arg_ Returns the serialized 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. ```wlambda std:assert_eq (std:ser:wlambda "foo") $q|"foo"|; std:assert_eq (std:ser:wlambda $none) $q|$n|; std:assert_eq (std:ser:wlambda $[1,:a]) $q|$[1,:a]|; ``` #### 12.1.2 - 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. ```wlambda !str = std:ser:json $[1,2.3,${a=4}] $t; std:assert_eq str "[1,2.3,{\"a\":4}]"; ``` #### 12.1.3 - std:deser:json _string_ Deserializes the JSON formatted _string_ into a data structure. ```wlambda !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.4 - 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 '"'. ```wlambda !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.5 - 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. ```wlambda !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.6 - std:ser:msgpack _data_ Serializes the _data_ and returns a msgpack bytes value. ```wlambda std:assert_eq (std:ser:msgpack $b"abc") $b"\xC4\x03abc"; ``` #### 12.1.7 - std:deser:msgpack _bytes_ Deserializes the msgpack bytes value into a data structure. ```wlambda std:assert_eq (std:deser:msgpack $b"\xC4\x03abc") $b"abc"; ``` ### 12.2 - Regular Expressions (more classic syntax) WLambda supports a more common form of regular expression syntax if the "regex" feature is enabled when compiling WLambda. It uses the regex syntax as described in the Rust "regex" crate: [https://docs.rs/regex/newest/regex/](https://docs.rs/regex/newest/regex/#syntax). #### 12.2.1 - std:re:match _regex-string_ _input-string_ _function_ If the given regular expression _regex-string_ matches the given _input-string_ the _function_ will be called with a vector containing the matched string and the captures as first argument. The return value is the return value of the function call or `$none`. Returns an error if the _regex-string_ has a syntax error. ```wlambda !ret = std:re:match $q|(\d+)-(\d+)-(\d+)| "2020-05-01" { std:str:join "." std:reverse <& (1 => 3) <&_; }; std:assert_str_eq ret "01.05.2020"; ``` #### 12.2.2 - std:re:match\_compile _regex-string_ This function compiles the given _regex-string_ and returns a function that will execute the regex matching. If the syntax in _regex-string_ is wrong, an error is returned. The returned function takes the string to match against as first parameter and the function that is called if the regex matches as second parameter. ```wlambda !match_fun = std:re:match_compile $q|(\d+)-(\d+)-(\d+)|; !ret = match_fun "2020-05-01" { std:str:join "." std:reverse <& (1 => 3) <&_; }; std:assert_str_eq ret "01.05.2020"; ``` #### 12.2.3 - std:re:map _regex-string_ _function_ _input-string_ Executes _function_ for each match of the regex defined by _regex-string_ on the _input-string_. Returns an error if there is a syntax error in the regex. Otherwise returns the last return value of _function_. ```wlambda !res = $@vec std:re:map $q|(\d+)-(\d+)-(\d+)| {!(str, d1, d2, d3) = _; $+ $[int d1, int d2, int d3] } "foo bar 2020-05-01 fofo 2020-06-01 bar 2019-12-31"; std:assert_str_eq res $[$[2020,5,1], $[2020,6,1], $[2019,12,31]]; ``` #### 12.2.4 - std:re:replace\_all _regex-string_ _replace-function_ _input-string_ This function replaces all matches of the regex _regex-string_ in the _input-string_ by the return value of the _replace-function_. Returns an error if there is a syntax error in the _regex-string_. ```wlambda !res = std:re:replace_all $q"(\d+)" { str (int _.1) + 1 } "foo 32 fifi 99"; std:assert_eq res "foo 33 fifi 100"; ``` ### 12.3 - xml #### 12.3.1 - std:xml:read\_sax _xml-string_ _event-callback-function_ [_do-not-trim-text_] ```wlambda \:x { $@v _? :x ~ std:xml:read_sax "xfooo fweor weio ew foefoey" $+; }[] ``` #### 12.3.2 - std:xml:create\_sax\_writer [_indent_] Creates an XML SAX event based writer function. The function should be called with single events as received by `std:xml:read_sax`. To receive the final output of the writer, call the returned function without any arguments. ```wlambda !writer = std:xml:create_sax_writer[]; writer $[:start, "test"]; writer $[:end, "test"]; std:assert_eq writer[] ""; ``` #### 12.3.3 - std:xml:create\_tree\_builder This function returns a function that acts as WLambda data structure builder that accepts SAX events as given to the callback of `std:xml:read_sax`. The returned data structure is a tree build of the following elements: - `$[:decl, ${ version=..., encoding=..., standalone=... }]` - `$[:elem, name, ${ }, $[ ]]` - `$[:comment, text]` - `$[:pi, text]` - `$[:text, text]` - `$[:doctype, text]` - `$[:cdata, text]` Here is a more elaborate example on how to use it: ```wlambda !tb = std:xml:create_tree_builder[]; !tree = std:xml:read_sax $q$ hello blop lol $ tb; std:assert_str_eq tree $[:root,$n,$n,$[ $[:elem,"foo",$n,$[ $[:elem,"i",${x="here"},$n],$[:text,"hello"]]], $[:elem,"foo",$n,$[ $[:text,"blop"],$[:elem,"i",${x="10"},$n],$[:text,"lol"]]] ]]; # Here we use the structure matchers for finding the values of the x attributes # of the elements: std:assert_str_eq $S[ 3 / *:{0=elem,1=foo} / 3 / *:{0=elem,1=i} / 2 / x ] <& tree $["here","10"]; ``` ### 12.4 - chrono #### 12.4.1 - std:chrono:timestamp \[_format_] For the documentation of _format_ please consule the chrono Rust crate documentation: [chrono crate strftime format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html#specifiers). ```wlambda !year_str = std:chrono:timestamp "%Y"; std:displayln :XXXX ~ (year_str | int) == 2022; std:assert ~ (year_str | int) == 2022; !now_str = std:chrono:timestamp[]; ``` #### 12.4.2 - std:chrono:format\_utc _utc-timestamp_ [_format_] Formats the given _utc-timestamp_ in seconds according to _format_. For the documentation of _format_ please consule the chrono Rust crate documentation: [chrono crate strftime format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html#specifiers). ```wlambda !year_str = std:chrono:format_utc 1603796989 "%H:%M:%S %Y"; std:assert_str_eq year_str "11:09:49 2020"; ``` #### 12.4.3 - std:chrono:format\_local _utc-timestamp_ [_format_] Formats the given _utc-timestamp_ in seconds according to _format_ in the local timezone. For the documentation of _format_ please consule the chrono Rust crate documentation: [chrono crate strftime format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html#specifiers). ```wlambda !year_str = std:chrono:format_local 1603796989 "%H:%M:%S %Y"; std:assert_str_eq year_str "12:09:49 2020"; ``` ### 12.5 - color conversion This section highlights the color conversion functions available in WLambda. Numerical vectors are used in WLambda to represent colors. There are two representations of a color. If you use a float vector `$f(r, g, b, a)` the values for RGB are in the range of 0.0 to 1.0. For HSV `$f(h, s, v, a)` h is within 0.0 to 360.0 while the others are in the range 0.0 to 1.0. For integer vectors the values for RGB are in the range 0 to 255. And the values for HSV are in the range 0 to 360, and the others in the range 0 to 100. You can also use 3 dimensional vectors without the alpha value: `$i(r, g, b)` / `$i(h, s, v)` and `$f(r, g, b)` / `$f(h, s, v)`. #### 12.5.1 - std:v:rgb2hsv _color-vector_ Converts an RGB color into a HSV color representation. ```wlambda std:assert_eq std:v:rgb2hsv <& $i(0, 255, 0, 255) $i(120, 100, 100, 100); std:assert_eq std:v:rgb2hsv <& $i(0, 255, 0) $i(120, 100, 100); std:assert_eq std:v:rgb2hsv <& $f(0, 1.0, 0, 1.0) $f(120, 1, 1, 1); std:assert_eq std:v:rgb2hsv <& $f(0, 1.0, 0) $f(120, 1, 1); std:assert_eq std:v:rgb2hsv <& $f(0, 0.5, 0, 1.0) $f(120, 1, 0.5, 1); std:assert_eq std:v:rgb2hsv <& $f(0.1, 0.5, 0.1, 1.0) $f(120, 0.8, 0.5, 1); ``` #### 12.5.2 - std:v:hsv2rgb _color-vector_ Converts a color from HSV to RGB representation. ```wlambda std:assert_eq std:v:hsv2rgb <& $i(120, 80, 50, 100) $i(25,128,25,255); !clr = std:v:hsv2rgb <& $f(120, 0.8, 0.5, 1.0); std:assert_rel_eq clr.r 0.1 0.001; std:assert_rel_eq clr.g 0.5 0.001; std:assert_rel_eq clr.b 0.1 0.001; ``` #### 12.5.3 - std:v:rgba2hex _color-vector_ This function converts a color to a string of hex digits (without the common '#' prefix however). ```wlambda std:assert_eq std:v:rgba2hex <& $i(255, 128, 64, 32) "ff804020"; std:assert_eq std:v:rgba2hex <& $f(1.0, 0.5, 0.25, 0.125) "ff804020"; ``` #### 12.5.4 - std:v:hex2rgba\_f _string_ Interprets _string_ as an hex encoded color and returns a 4 element big float vector. The color components of the float vector go from 0.0 to 1.0. The string can be: - 8 characters: `"RRGGBBAA"` - 6 characters: `"RRGGBB"`, alpha will be 1.0 - 4 characters: `"RGBA"` - 3 characters: `"RGB"`, alpha will be 1.0 - 2 characters: `"YY"`, where YY is put into R, G and B. Alpha will be 1.0. ```wlambda !color = std:v:hex2rgba_f "FF00FFFF"; std:assert_rel_eq color.r 1.0 0.001; std:assert_rel_eq color.g 0.0 0.001; std:assert_rel_eq color.b 1.0 0.001; std:assert_rel_eq color.a 1.0 0.001; !color2 = std:v:hex2rgba_f "C83F"; std:assert_rel_eq color2.r 0.8 0.001; std:assert_rel_eq color2.g 0.533 0.001; std:assert_rel_eq color2.b 0.2 0.001; std:assert_rel_eq color2.a 1.0 0.001; ``` #### 12.5.5 - std:v:hex2rgba\_i _string_ Like `std:v:hex2rgba_f` this function converts a hex encoded color from _string_ but returns an integer vector with 4 elements. The integers are in the range of 0 to 255. About the format of _string_ please refer to `std:v:hex2rgba_f`. ```wlambda !color = std:v:hex2rgba_i "FF00FFFF"; std:assert_eq color.r 255; std:assert_eq color.g 0; std:assert_eq color.b 255; std:assert_eq color.a 255; !color2 = std:v:hex2rgba_i "C83F"; std:assert_eq color2.r 204; std:assert_eq color2.g 136; std:assert_eq color2.b 51; std:assert_eq color2.a 255; ``` #### 12.5.6 - std:v:hex2hsva\_i _string_ Converts the hex represenation of a HSVA color to an integer vector `$i(h, s, v, a)`. This function is probably not that useful, as the bit distribution along the 3 bytes is not ideal. If you want to store colors properly, don't use this. It's mostly useful for testing and quick experiments. ```wlambda !color = std:v:hex2hsva_i "FF8040FF"; std:assert_eq color $i(360,50,25,100); ``` #### 12.5.7 - std:v:hex2hsva\_f _string_ Converts the hex represenation of a HSVA color to a float vector `$i(h, s, v, a)`. This function is probably not that useful, as the bit distribution along the 3 bytes is not ideal. If you want to store colors properly, don't use this. It's mostly useful for testing and quick experiments. ```wlambda !color = std:v:hex2hsva_f "FF8040FF"; std:assert_rel_eq color.0 360.0 1.0; std:assert_rel_eq color.1 50.0 1.0; std:assert_rel_eq color.2 25.0 1.0; std:assert_rel_eq color.3 100.0 1.0; ``` ### 12.6 - hash #### 12.6.1 - std:hash:fnv1a _arg1_ ... Hashes all the arguments as FNV1a and returns an integer. ### 12.7 - rand #### 12.7.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.7.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.7.3 - std:rand:split\_mix64\_next _sm\_state_ \[_count_] Returns the _count_ next integer values generated from the given _sm_state_. #### 12.7.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_. #### 12.7.5 - std:rand:split\_mix64\_next\_open\_closed01 _sm\_state_ \[_count_] Returns the _count_ next float values (in an open (0, 1] interval) generated from the given _sm_state_. #### 12.7.6 - std:rand:split\_mix64\_next\_closed\_open01 _sm\_state_ \[_count_] Returns the _count_ next float values (in an open [0, 1) interval) generated from the given _sm_state_. ### 12.8 - Utility Functions #### 12.8.1 - std:dump\_upvals _function_ Returns a vector of all the upvalues of the _function_. Please use this function for debugging purposes only, as the order of the variables, while consistent for a specific WLambda version, is not defined at this point. ```wlambda !x = 1; !y = 2; !fun = { _ + x + y }; std:assert_eq fun[3] 6; .x = 3; std:assert_eq fun[3] 8; !upvs = std:dump_upvals fun; std:assert_eq (str upvs) "$[$&3,$&2]"; .y = 4; std:assert_eq (str upvs) "$[$&3,$&4]"; std:assert_eq $*(upvs.0) 3; std:assert_eq $*(upvs.1) 4; ``` #### 12.8.2 - std:wlambda:version Returns the version number of the WLambda crate when called. #### 12.8.3 - std:wlambda:sizes Writes a table of internal data structure sizes to stdout. Just for development purposes. #### 12.8.4 - std:wlambda:parse _string_ Parses _string_ as if it was a piece of WLambda code and returns an abstract syntax tree. ```wlambda std:assert_str_eq (std:wlambda:parse "1 + 2") $[$%:Block,$[$%:BinOpAdd,1,2]] ``` ### 12.9 - HTTP Client WLambda offers an optional integrated HTTP client by enabling the `reqwest` feature at compile time. With this you can create a new client using `std:http:client:new` and make HTTP requests using `std:http:get`, `std:http:post` and `std:http:request`. Also support for basic authentication and token based bearer authentication is there. #### 12.9.1 - std:http:client:new Creates a new HTTP client instance and returns it. You can use it to make HTTP requests afterwards. ```wlambda !client = std:http:client:new[]; !response = std:http:get client "https://duckduckgo.com/"; !body = std:str:from_utf8_lossy response.body; std:assert ($p(0, "") body) > 0; std:assert_eq response.status 200; std:assert_eq response.headers.content-type "text/html; charset=UTF-8"; ``` See also `std:http:get` for a more elaborate example with providing headers. #### 12.9.2 - std:http:get _http-client_ _url-string_ [_headers-and-options-map_] Performs a HTTP GET request to the given _url-string_ using the _http-client_. The _headers-and-options-map_ can contain following special keys apart from your custom HTTP headers themself: - `@basic_auth` with `$[user, $none]` or `$[user, password]` as value. - `@bearer_auth` with `token` as value. - `@timeout` with a timeout duration as value. (See also `std:thread:sleep`). - `@query` with a map of query parameters and their values to modify the query string of the _url-string_. This properly encodes the strings. The client will either return an error or a response map with following keys: - `status` contains the HTTP response code (usually `200` if everything went fine). - `reason` contains a human readable canonical reason string for the status. - `body` contains the byte vector with the body data. - `headers` contains a map of all headers, where the keys are the lower case header names. Here is an example on how to use this while providing HTTP Headers: ```wlambda !client = std:http:client:new[]; !response = std:http:get client "https://crates.io/api/v1/crates?page=1&per_page=10&q=wlambda" ${ Accept = "application/json", Cache-Control = "no-cache", DNT = 1, User-Agent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:97.0) Gecko/20100101 Firefox/97.0", }; !body = std:deser:json ~ std:str:from_utf8_lossy response.body; # std:displayln ~ std:ser:json body $f; std:assert_eq body.crates.0.name "wlambda"; std:assert_eq response.headers.content-type "application/json; charset=utf-8"; std:assert_eq response.status 200; ``` #### 12.9.3 - std:http:post _http-client_ _url-string_ _body-bytes_ [_headers-and-options-map_] This call is like `std:http:get` but makes a HTTP POST request with the given payload _body_. For HTTP requests with other methods please look at `std:http:request`. The rest of the options are the same as `std:http:get`. But here is an example how to transmit a piece of JSON easily: ```wlambda !client = std:http:client:new[]; !response = std:http:post client "http://httpbin.org/post" (std:str:to_bytes ~ std:ser:json $[ :x => 10, ${ y = 20 }, ]); !body = std:deser:json ~ std:str:from_utf8_lossy response.body; std:assert_eq body.url "http://httpbin.org/post"; std:assert_str_eq body.json $[ $["x", 10], ${ y = 20 } ]; std:assert_eq response.status 200; ``` #### 12.9.4 - std:http:request _http-client_ _method-string_ _url-string_ [_body-bytes_ [_headers-and-options-map_]] This call is like `std:http:post` but makes HTTP requests with an almost arbitrary method with the optional given payload _body_. The rest of the options are the same as `std:http:get`. ```wlambda !client = std:http:client:new[]; !response = std:http:request client :GET "http://httpbin.org/get"; !body = std:deser:json ~ std:str:from_utf8_lossy response.body; std:assert_eq body.url "http://httpbin.org/get"; ``` Or a POST request: ```wlambda !client = std:http:client:new[]; !response = std:http:request client :POST "http://httpbin.org/post" (std:str:to_bytes ~ std:ser:json $[ :x => 10, ${ y = 20 }, ]); !body = std:deser:json ~ std:str:from_utf8_lossy response.body; std:assert_eq body.url "http://httpbin.org/post"; std:assert_str_eq body.json $[ $["x", 10], ${ y = 20 } ]; std:assert_eq response.status 200; ``` ### 12.10 - MQTT Messaging WLambda offers an optional support for the MQTT protocol. You can setup a MQTT client as well as an embedded MQTT broker. The very simple integration offers you a very easy way to setup inter process communication between WLambda applications. Support for MQTT has to be explicitly compiled into WLambda by selecting the `mqtt` feature. #### 12.10.1 - std:mqtt:broker:new _config_ This function sets up an embedded MQTT broker. A handle is returned that you can use to publish messages using the locally connected client link. You can configure it's endpoints via the _config_. The _config_ offers following keys: ```text ${ id = 0, # Broker ID listen = "0.0.0.0:1883", # Broker server endpoint console_listen = "0.0.0.0:18088", # An extra HTTP console endpoint link = ${ # Configure the local link client_id = "some_id", # Link client ID, default is 'wl_local' recv = , # Channel to receive messages for the # subscribed topics. # Topics to subscribe to, if not given, '#' will be used: topics = $["test/me", "another/topic", ...] }, } ``` The default maximum MQTT payload the broker is setup to support is 10240 bytes (10 kb). Here is an example: ```wlambda !broker = std:mqtt:broker:new ${ listen = "0.0.0.0:1883", console_listen = "0.0.0.0:18080", }; # sleep a bit until the broker is initialized: std:thread:sleep :ms => 500; !chan = std:sync:mpsc:new[]; !cl = std:mqtt:client:new chan "test1" "localhost" 1883; # let it connect: std:thread:sleep :ms => 200; !_ = cl.subscribe "test/me"; !_ = cl.publish "test/me" $b"test123\xFF"; std:assert_str_eq chan.recv[] $p(:"$WL/connected", $n); std:assert_str_eq chan.recv[] $p("test/me", $b"test123\xFF"); ``` ##### 12.10.1.1 - broker.publish _topic-string_ _payload-bytes_ Publishes the _payload-bytes_ under the _topic-string_. Returns an error if something went wrong (client not connected, or some other error). It might block. #### 12.10.2 - std:mqtt:client:new _channel_ _client-id_ _broker-host_ _broker-port_ This sets up a MQTT client that connects to the given _broker-host_ and _broker-port_. It will connect and reconnect upon connection failure in the background automatically for you. So you don't have to manage the connection yourself. This function returns a client handle that is describe a little bit further below. The _client-id_ should be a unique ID to identify your MQTT client. The _channel_ must be a `std:sync:mpsc` channel that you can create using `std:sync:mpsc:new`. It's the source of incoming messages and connection control information. It will send you following possible message data: - `$p(:"$WL/connected", $n)` - When the client connection was setup - `$p(:"$WL/error", "some message here...")` - When an error occurred in the connection handling. - `$p(topic, payload_bytes)` - An incoming MQTT message that belongs to the _topic_. Here is an example of a common client setup: ```wlambda !broker = std:mqtt:broker:new ${ listen = "0.0.0.0:1883", console_listen = "0.0.0.0:18080", }; # sleep a bit until the broker is initialized: std:thread:sleep :ms => 500; !chan = std:sync:mpsc:new[]; !cl = std:mqtt:client:new chan "test1" "localhost" 1883; # let it connect: std:thread:sleep :ms => 200; !_ = cl.subscribe "test/me"; !_ = cl.publish "test/me" $b"test"; !_ = cl.publish "test/me" $b"quit"; !got_some_stuff = $n; while $t { !msg = chan.recv[]; match msg $p(topic, $b"quit") => { break[]; } $p(topic, data) => { .got_some_stuff = std:copy $\; }; # std:copy because $\ is changing! }; std:assert_eq got_some_stuff.topic "test/me"; std:assert_eq got_some_stuff.data $b"test"; ``` The returned client handle understands the following methods: ##### 12.10.2.1 - mqtt\_client.publish _topic-string_ _payload-bytes_ Publishes the _payload-bytes_ under the _topic-string_. Returns an error if something went wrong (client not connected, or some other error). It might block. ##### 12.10.2.2 - mqtt\_client.subscribe _topic-string_ Subscribes to the _topic-string_. Returns an error if something went wrong. It might block. ## 13 - WLambda Lexical Syntax and Grammar White space is everything that satisfies `std::char::is_whitespace`, so unicode white space is respected. Comments have the following syntax: ```ebnf comment = "#" ?anything except "\n"? "\n" ``` In the following grammar, white space and comments are omitted: ```ebnf ident_start = ( ?alphabetic? | "_" | "@" | "?" ) ident_end = { ?any character? - ( ?white space? | "." | "," | ";" | "{" | "}" | "[" | "]" | "(" | ")" | "~" | "|" | "=" ) } ; qident = ident_end (* a quoted identifier can not appear anywhere, it's usually delimited or follows something that makes sure we are now expecting an identifier *) | "`", { ?any character except '`'? }, "`" (* quoted identifier *) ; ident = ident_start, [ ident_end ] | "`", { ?any character except '`'? }, "`" (* quoted identifier *) ; ref_specifier = ":", qident ; digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; integer = digit, { digit } ; radix = integer ; radix_digits = (* digits in the radix specified earlier in the number. Default radix is of course 10. *) number = [ "-" | "+" ], [ ( radix, "r" | "0", "x" | "0", "b" | "0", "o" ) ], radix_digits, [ ".", radix_digits ] ; hexdigit = ?hexdigit, upper or lower case? ; ascii_c_name = (* note: upper and lower case versions are possible *) "NULL" | "SOH" | "STX" | "ETX" | "EOT" | "ENQ" | "ACK" | "BEL" | "BS" | "HT" | "LF" | "VT" | "FF" | "CR" | "SO" | "SI" | "DLE" | "DC1" | "DC2" | "DC3" | "DC4" | "NAK" | "SYN" | "ETB" | "CAN" | "EM" | "SUB" | "ESC" | "FS" | "GS" | "RS" | "US" | "DEL" | "SPACE" | "NBSP" ; string_escape = "x", hexdigit, hexdigit (* byte/ascii escape *) | "n" (* newline *) | "r" (* carriage return *) | "t" (* horizontal tab *) | "0" (* nul byte/char *) | "u", hexdigit, { hexdigit } (* unicode char, or in byte strings their utf-8 encoded form *) | "\"" | "\'" | "\\" | "\\<", ascii_c_name, ">" (* the corresponding ascii value *) ; string_lit = string | character | "$", quote_string | "$", byte_string | "$", code_string ; character = "'", ( "\\", string_escape | ?any character? - "\\" and "'" ), "'" ; string = "\"", { "\\", string_escape | ?any character? - "\\" and "\"" },"\"" ; byte_char = "b", character ; byte_string = "b", string ; quote_string = "q", ?any character as quote?, { ?any character? }, ?any character as quote? | "Q", ?any character as quote?, { ?any character? }, ?any character as quote? (* but Q generates a byte string instead! *) selector = "S", ?any character as quote?, selector_rs_syntax, ?any character as quote? (* parses substring like 'q', but constructs a selector_rs_syntax matcher at compile time *) ; pattern = "r", [ "g" ], (* "g" for find all *) ?any character as quote?, selector_rs_pattern_syntax, ?any character as quote? (* parses substring like 'q', but constructs a pattern matcher at compile time *) ; struct_match = "M", expr (* compiles expr as structure matcher function. If called, it matches the first argument against the literal structure and returns a map of matched variables. If nothing matches $none is returned. *) formatter = "F", string_lit (* compiles the string into a formatter, that takes as many arguments as there are format specifiers in the string. See also: String formatting syntax in the next section. *) list_expr = "*", expr (* splices the vector result of 'expr' into the currently parsed list *) | expr ; list = "[", [ list_expr, { ",", list_expr }, [ "," ] ],"]" ; map_expr = (ident | expr), "=", expr | "*", expr (* splices the map result of 'expr' into the currently parsed map *) ; map = "{", [ map_expr, { ",", map_expr }, [ "," ] ], "}" ; self = "s" | "self" ; true = "t" | "true" ; false = "f" | "false" ; none = "n" | "none" ; code_string = ("c" | "code" ), block | ("c" | "code" ), expr ; pair = "p", "(", expr, "," expr, ")" ; err = ("e" | "error"), expr ; nvec = ("i" | "f"), "(", expr, { ",", expr }, ")" ; ref = "&&", value ; ref_hidden = "&", value ; ref_weak = ("w&" | "weak&"), value ; syntax = "%:", {? any possible Syntax Type Identifier eg. "Block", "Call", ... ?} (* A syntax VVal, the possible identifiers are one of the possible result symbols of std:syn:type. This also saves the current parser position. *) ; accumulator = "@", ("i" | "int" |"s" | "string" |"f" | "float" |"b" | "bytes" |"v" | "vec" |"m" | "map" ), expr (* defines a new accumulator context *) | "@@" (* returns the current accumulator value *) | "+" (* resolves to the current accumulator function *) ; debug_print = "DEBUG" (* evaluates to a debug print function that also prints source position and dynamic type. very useful for debugging. *) ; import = "@import", ident, [ ident ] ; export = "@export", ident, [ "=" ], expr ; capture_ref = ":", var ; deref = "*", value ; special_value = list | map | none | true | false | self | err | nvec | pair | ref | ref_hidden | ref_weak | deref | capture_ref | accumulator | selector | pattern | struct_match | debug_print | "\" (* The global variable with the name "\" *) ; arity_def = "|", number, "<", number, "|" (* set min/max *) | "|", number, "|" (* set min and max *) | "|", "|" (* no enforcement *) ; function = [ "\:", ident ], "{", [ arity_def ], block, "}" | "\", [ arity_def ], statement ; var = ident ; symbol = ":", qident | ":", "\"", (? any char, quoted \\ and \" ?), "\"" (* symbols are usually used to specify fields in literal map definitions and lots of other places as stringy sentinel values *) ; value = number | string_lit | "$", special_value | "(", expr, ")" | function | symbol | var ; op = (* here all operators are listed line by line regarding their precedence, top to bottom *) "&>" | "&@>" (* call rhs with lhs operator *) | "<&" | "<@&" (* call lhs with rhs operator *) | "//" | "/?" | "/$n" | "/$o" | "/$e" (* default value operators *) | "^" | "*" | "/" | "%" | "-" | "+" | "<<" | ">>" (* binary shift *) | "<" | ">" | "<=" | ">=" | "==" | "!=" | "&" (* binary and *) | "&^" (* binary xor *) | "&|" (* binary or *) | "&and" (* logical and, short circuit *) | "&or" (* logical or, short circuit *) | "=>" (* pair constructor *) | "+>" (* take lhs, wrap it into list if not already and append the right side. if lhs is an iterator, append all elements. *) | "<+" (* take rhs, wrap it into list if not already and prepend the left side. if rhs is an iterator, prepend all elements. *) ; bin_op = call_no_ops, { op, bin_op } (* precedence parsing is done in a Pratt parser style *) ; arg_list = "[", [ expr, { ",", expr }, [ "," ] ], "]" | "[[", expr, "]]" (* apply result vector of expr as argument list *) ; field = ".", ( integer | ident | value ), [ field ] ; field_access = field, [ op ], "=", expr | field, arg_list | field (* please note, that a field access like: `obj.field` is equivalent to the call: `field[obj]`. That also means that `obj.field[...]` is transformed into `field[obj][...]`. The exception is "=" which assigns the field as specified. BUT: There is a special case, when you specify an identifier as field, it is quoted and interpreted as symbol. *) ; call_no_ops = value, { arg_list | field_access } ; call = value, { arg_list | field_access | bin_op | value }, [ "~", expr ] (* this is a tail argument, if present the expr is appended to the argument list *) ; expr = call, { "|", call } | call, { "|>", call } | call, { "||", call } ; simple_assign = qident, [ op ], "=", expr ; destr_assign = "(", [ qident, { ",", qident } ], ")", "=" expr ; definition = [ ref_specifier ], ( simple_assign | destr_assign ) ; import = "!", "@import", symbol, [ [ "=" ], symbol ] | "!", "@wlambda" ; export = "!", "@export", symbol, [ "=" ], expr ; statement = "!" definition | "." simple_assign | "." destr_assign | import | export | expr ; block = "{", { statement, ";", {";"}}, [ statement, {";"} ], "}" | { statement, ";", {";"} }, [ statement, {";"} ] ; code = block ; ``` ### 13.1 - Special Forms There are certain calls that are handled by the compiler differently. - `if _condition_ _then-block-or-expr_ [_else-block-or-expr_]` - `while _condition_ _block-or-expr_` - `iter _var_ _value-expr_ _block-or-expr_` - `next _x_` - `break` - `match _value-expr_ $p(structure_pattern, branch_block) ... [ branch_block ] - `jump _idx-expr_ _block1_ ...` ### 13.2 - String Formatting Syntax The `$F` special value takes a string and creates a formatting function. The syntax for formatting is very similar to Rust's string formatting: ```ebnf format_string = , { maybe-format, } ; maybe-format = '{', '{' | '}', '}' | ; format = '{' [ argument ], [ ':' format_spec ] '}' ; argument = integer (* index into argument vector *) | identifier (* requires a map as parameter *) ; format_spec = [[fill]align][sign]['#']['0'][width]['.' precision]['!' cast_type][type] ; fill = character ; align = '<' | '^' | '>' ; sign = '+' | '-' ; width = count ; precision = count | '*' ; cast_type = 'i' (* interpret arg as integer *) | 'f' (* interpret arg as float *) | 's' (* (default) interpret arg as string *) ; type = 'x' (* hex lower case *) | 'X' (* hex upper case *) | 'b' (* binary *) | 'o' (* octal *) | 'j', character (* a string join of a vector, separated by 'character' *) | 'C', character (* A CSV row, separated by 'character'. This also supports proper escaping using '"' *) | 'J', ['p'] (* JSON, optional pretty printed *) | 'w', ['p'] (* print written WLambda representation, optional pretty printed *) | '' ; count = parameter | integer ; parameter = argument, '$' ; ``` ### 13.3 - Format String Syntax for std:bytes:pack and std:bytes:unpack This syntax describes the accepted format strigns for the `std:bytes:pack` and `std:bytes:unpack`. The format is loosely adapted from the Lua syntax for `string.pack` and `string.unpack`. | Syntax | Semantics | |-|-| | `<` | Sets little endian | | `>` | Sets big endian | | `=` | Sets native endian | | `x` | One zero byte of padding | | `y` | Byte content with unspecified length. On `unpack` this reads to the end of the input. | | `z` | A zero-terminated string of bytes. | | `c` | An `n` long field of bytes. | | `b` | A single byte. | | `s` | A string of bytes that is prefixed with a `` wide binary length field. | | `u` | A `` wide unsigned integer field. | | `i` | A `` wide signed integer field. | | `f` | A float field (32 bits). | | `d` | A double field (64 bits). | - `` can be any number. - `` can be 8, 16, 32, 64 or 128.