Language
Source encoding
The source encoding is UTF-8.
Comment
# This is a comment
Types
Table
Name | Description |
---|---|
List | ( ... ) |
Number | Positive and negative 64-bit integers |
Symbol | 16-character string |
Character | A ^ -prefixed printable character |
T |
Stands for true |
NIL |
The empty list, also stands for false |
_ |
Wildcard, used as a placeholder during deconstruction |
Symbols
Symbols are bound to values. Undefined symbols are unbound and resolve to NIL
.
Symbols can be created or altered globally using the def
and setq
functions.
Symbols can also be defined locally using the let
function.
Strings
The grammar supports the string type. Represented as a "
-delimited string of
characters, it is internally stored as a list of characters. For example:
: "hello"
> (^h ^e ^l ^l ^o)
Expression evaluation
Generic rules
- Numbers, strings,
NIL
,T
, and_
evaluate to themselves - Symbols are dereferenced to their bound values
- Lists are evaluated as expressions
Expressions
Expression take the following form: (FUNCTION ARGS ...)
. The FUNCTION
can be:
- A symbol that must resolve to a function definition
- A quoted function definition
- A lambda definition
The forms 1, 2, and 3 are mutually interchangeable:
: (def add (a b) (+ a b))
> ((a b) NIL (+ a b))
: (add 1 2) # Form 1
> 3
: ('((a b) NIL (+ a b)) 1 2) # Form 2
> 3
: ((\ (a b) (+ a b)) 1 2) # Form 3
> 3
Functions
Functions are represented internally as the following 3-uple:
(ARGUMENTS CLOSURE . BODY)
The ARGUMENTS
element is a list of symbols representing the arguments of the
functions. The CLOSURE
element is an association list that contains the
context of the function at the definition site. Lastly, the BODY
element is
the expression of the function. When defined through plugins, the body may also
be a number representing the memory address of the native implementation of the
function.
Bindings
The BINDINGS
list may be one of these form:
NIL
or()
: the function takes no argumentSYMBOL
: the unevaluated list of arguments is associated withSYMBOL
LIST
: arguments are evaluated and assigned to the symbols inLIST
Lambdas
Lambda functions are defined using the \
keyword. Invocation of \
is
similar to def
:
: ((\ (x y) (+ x y)) 1 1)
> 2
: ((\ (x) (map (\ (n) (+ n 1)) x)) '(1 2 3 4))
> (2 3 4 5)
The closure available at the time a lambda is defined is placed in its signature. This is the lambda's define-site closure. When the lambda is called, its closure is extended with the environment available where the call is performed. This is the lambda's call-site closure.
Currying
Minima.l supports currying. It uses the function's closure to store the curried
arguments. Curryring is available to all lisp
and native functions. For example:
: (def add (a b) (+ a b))
> ((a b) NIL (+ a b))
: (setq +1 (add 1))
> ((b) ((a . 1)) (+ a b))
: (+1 2)
> 3
Recursion
A function defined using def
can be recursive. When functions are defined,
symbols with their names are not resolved in their closure and are resolved
dynamically in the symbol domain instead.
A lambda defined using let
can also be recursive. Similarly as above, the
recursive calls are resolved dynamically. However, the resolution process
first looks into the define-site closure of the lambda, then its call-site
closure, and finally in the symbol domain.
There is a caveat however: since the function symbols are resolved dynamically,
redefining these symbols will lead to undefined behavior. For instance, the
following is a valid recursive lambda and returns 0
:
(let ((fn . (\ (A) (if (= A 0) 0 (fn (- A 1))))))
(fn 10))
However, the following is not and returns 10
:
(let ((fn . (\ (A) (+ A 1))))
(let ((fn . (\ (A) (unless (= A 0) (fn (- A 1))))))
(fn 10)))
Tail-call optimization
When functions are defined with def
or \
in let
, the function's body is
scanned for potential tail calls. During evaluation, when a tail call is
encountered, its argument are evaluated at its call-site and returned to the
parent. These arguments are then used at the root of the function in a new
application of that function, executed in a tight loop.
Value deconstruction
Assignation of arguments in def
, lamda
, or let
functions support
deconstruction. For instance, with def
:
: (def sum3 ((a b c)) (+ (+ a b) c))
> sum3
: (sum3 (list 1 2 3))
> 6
Or with a lambda:
: (setq data '(("hello" . 1) ("world" . 2)))
> (("hello" . 1) ("world" . 2))
: (foldl (\ (acc (_ . v))(+ acc v)) 0 data)
> 3
Global variables
ARGV
When mnml
is executed as a #!
interpreter, the ARGV
global contains the
argument vector of the script.
CONFIG
The CONFIG
global contains the current runtime of the interpreter. It contains
the installation PREFIX
, the interpreter VERSION
, its build timestamp
BUILD_TS
, and the name COMPNAME
and version COMPVER
of the compiler used to
build it.
ENV
The ENV
global contains the environment at the time of the invocation of the
interpreter.