Module /fusion/base
A reduced language that may be preferable to using the full
/fusion language in some situations. In particular, this
dialect may exhibit faster startup time since it requires fewer parts of the
full library.
To use this dialect for your module, declare it as follows:
(module my_module "/fusion/base"
// ...
)
Exported Bindings
*+-/<<=======>>=all_defined_outandannotateannotationsapplybeginconddefineelementeltidentifis_blobis_boolis_clobis_decimalis_emptyis_floatis_intis_listis_nullis_null_nullis_procedureis_sexpis_stringis_structis_symbolis_timestampis_voidlambdaletlet_valuesletrecletsmodulenotonly_inorprefix_inprovidequasiquotequoterename_inrename_outrequiresamesizetype_annotationsunlessunquotevaluesvoidwhen#%top
(* num ...)
Returns the product of the numbers, which must be int or decimal. With no
arguments, returns integer 1.
(+ num ...)
Returns the sum of the numbers, which must be int or decimal. With no
arguments, returns integer 0.
(- num ...+)
With two or more int or decimal numbers, returns their difference,
associating to the left. With one int or decimal argument, returns its
negation.
(/ dividend divisor)
Returns a decimal whose numeric value is (dividend / divisor). Both
arguments must be decimals. An exception is thrown if the result cannot be
represented exactly.
(< a b)
Returns true if a is less than b.
Numbers are compared without regard to precision or negative zeros; if the
values have different concrete types they are both coerced to decimal.
Timestamps are compared to each other without regard to precision or local
offset. Annotations are ignored.
Warning: The behavior of this procedure is undefined for values +inf,
-inf, and nan.
See issue #64.
(<= a b)
Returns true if a is less than or equal to b.
Numbers are compared without regard to precision or negative zeros; if the
values have different concrete types they are both coerced to decimal.
Timestamps are compared to each other without regard to precision or local
offset. Annotations are ignored.
Warning: The behavior of this procedure is undefined for values +inf,
-inf, and nan.
See issue #64.
(= left right)
Returns true if the arguments are equivalent in shape and content, possibly by coercing values to a common type. Annotations and precision are ignored.
- Any value is
=to itself. - Nulls of any type are
=to each other. - Bools are
=in the obvious way. - Numbers are
=when they represent the same numeric value, without regard to precision or negative zeros. When a float is compared to an int or decimal, it is coerced to a decimal. - Timestamps are
=when they represent the same point-in-time, without regard to precision or local offset. - Strings and symbols are
=(interchangably) when they contain the same sequence of code points. - Blobs and clobs are
=(interchangably) when they contain the same sequence of bytes. - Pairs are
=when their heads and tails are=. - Sequences are
=when they have the same size, and elements at the same index are=. - Structs are
=when they have the same set of field names, and each name maps to a set of elements that are=. - Void is
=only to itself. - Exceptions are not thrown due to mismatched types; instead the result is
false.
For example:
(= null (quote a::null)) --> true
(= null null.clob) --> true
(= 1 1.00) --> true
(= 0 -0e-3) --> true
(= 2014T 2014-01-01T02:00+02:00) --> true
(= 2014T 2014) --> false
(= "text" (quote text)) --> true
(= "text" (quote a::"text")) --> true
(= [1, 2] (sexp 1 2.00)) --> true
(= null.list []) --> false
(= (struct "f" 1) (mutable_struct "f" 1.0)) --> true
(= {f:1, f:1} {f:1}) --> false
At present, the coercion of float to decimal is precise, without rounding to approximate a prettier decimal form. This may lead to strange behavior since most decimal numbers don't have a precise binary representation:
(= 1.2 1.2e0) --> false
(= 1.5 1.5e0) --> true
It's possible this may change in a future release.
Warning: The behavior of this procedure is undefined for values +inf,
-inf, and nan.
See issue #64.
(== left right)
Like = but does not coerce values to a common abstract supertype.
Annotations and precision are ignored.
- Any value is
==to itself. - Nulls are only
==to nulls of the same type. - Numbers are
==when they have the same type and numeric value, without regard to precision or negative zeros. - Strings are only
==to strings, and symbols to symbols. - Blobs are only
==to blobs, and clobs to clobs. - Pairs are
==when their heads and tails are==. - Lists (and sexps) are
==when they have the same type and size, and elements at the same index are==. - Structs are
==when they have the same set of field names, and each name maps to a set of elements that are==.
For example:
(== null (quote a::null)) --> true
(== null null.clob) --> false
(== 1 1.) --> false
(== 1. 1.0) --> true
(=== 0. -0.) --> true
(== 2014T 2014-01-01T02:00+02:00) --> true
(== 2014T 2014) --> false
(== "text" (quote text)) --> false
(== "text" (quote a::"text")) --> true
(== [1, 2] (sexp 1 2 )) --> false
(== [1, 2] (list 1 2.00)) --> false
(== [1, 2] (list 1 2 )) --> true
(== [1, 2] (mutable_list 1 2)) --> true
Warning: The behavior of this procedure is undefined for values +inf,
-inf, and nan.
See issue #64.
(=== left right)
Like == but annotations and precision are not ignored.
- Values are
===only when their annotations are equal. - Decimals are
===only when they have the same numeric value and precision. Negative zeros are not===to positive zeros. - Timestamps are
===only when they represent the same point-in-time and have the same precision and local offset.
For example:
(=== null (quote a::null)) --> false
(=== (quote a::null) (quote a::null)) --> true
(=== null null.clob) --> false
(=== 1 1.) --> false
(=== 1. 1.0) --> false
(=== 1.0 1.0) --> true
(=== 0. -0.) --> false
(=== 2014T 2014-01-01T02:00+02:00) --> false
(=== 2014-01-01T00:00+00:00
2014-01-01T00:00-00:00) --> false
(=== (quote a::1) (quote a::a::1)) --> false
(=== (quote b::a::1) (quote a::b::1)) --> false
Warning: The behavior of this procedure is undefined for values +inf,
-inf, and nan.
See issue #64.
(> a b)
Returns true if a is greater than b.
Numbers are compared without regard to precision or negative zeros; if the
values have different concrete types they are both coerced to decimal.
Timestamps are compared to each other without regard to precision or local
offset. Annotations are ignored.
Warning: The behavior of this procedure is undefined for values +inf,
-inf, and nan.
See issue #64.
(>= a b)
Returns true if a is greater than or equal to b.
Numbers are compared without regard to precision or negative zeros; if the
values have different concrete types they are both coerced to decimal.
Timestamps are compared to each other without regard to precision or local
offset. Annotations are ignored.
Warning: The behavior of this procedure is undefined for values +inf,
-inf, and nan.
See issue #64.
(all_defined_out)
A provide clause that exports all bindings defined by the enclosing module.
Imported bindings are not exported.
This form can only appear within provide.
(and expr ...)
Evaluates the exprs from left to right, returning the first untruthy value
that results (and ignoring further expressions). If they all return truthy
values, the last one is returned.
The last expr is in tail position. Given no exprs, the result is true.
(annotate value text ...)
Applies the given annotations to a value, returning a (shallow) copy if
necessary to avoid mutating it. Any annotations on value are removed,
so (annotate value) effectively de-annotates a value.
The value must be annotatable; that is, it must be one of the Ion types.
The text arguments must be non-null strings or symbols.
(annotate 123 "a") => a::123
(let [(v (quote a::123))]
(annotate v)) => 123
(let [(anns ["a", (quote b)])]
(apply annotate 123 anns)) => a::b::123
To extract annotations from a value, use
annotations.
(annotations value)
Returns a non-null immutable list of symbols containing the user type
annotations on the value.
(annotations 123) => []
(annotations (quote a::b::123)) => [a, b]
To put annotations onto a value, use annotate.
(apply proc arg ... sequence)
Calls the given proc with arguments that are the (optional) args prepended
to the elements of sequence. The proc is called in tail position.
(apply + [1, 2]) => 3
(apply + 10 11 (sexp 1 2)) => 24
(begin expr ...)
Evaluates the exprs in order, returning the final result. The last expr is
in tail position. If there are no exprs the result is void.
(cond (test body ...) ...)
Evaluates the test expressions left to right until one returns a truthy value,
then evaluates the corresponding body expressions in tail position.
If no test is truthy, the result is void.
(define id value)
Binds a namespace-level variable id to the result of value.
At top-level, the value expression is evaluated before top-level bindings for
the ids are created.
(define (id arg ...) body ...+)
Defines a procedure id, with formal arguments arg ... and the body.
This form is equivalent to (define id (lambda (arg ...) body ...)).
(element collection key)
Returns an element within a collection. The collection must be a non-null,
non-empty list, sexp, or struct. The key must have a type appropriate for
the collection: an int for lists or sexps, a string or symbol for structs.
(element [0, 1] 0) => 0
(element (sexp 0 1) 1) => 1
(element {f:2} "f") => 2
(element {f:3} (quote f)) => 3
Since element is a procedure, field names must be quoted or else they will
be evaluated as a variable reference:
(element {f:2} f) => ERROR: Unbound variable reference
(let [(g "f")]
(element {f:2} g)) => 2
An exception is raised if the collection has an unsupported type, if the
key isn't appropriate for the collection, or if the key doesn't identify
an element within the collection.
(element [0, 1] 2) => ERROR
(element [0, 1] "2") => ERROR
(element {f:2} "g") => ERROR
(elt collection key)
Returns an element within a collection, being lenient. The collection must
be a list, sexp, struct, or void. The key must have a type appropriate for
the collection: an int for lists or sexps, a string or symbol for structs.
(elt [0, 1] 0) => 0
(elt (sexp 0 1) 1) => 1
(elt {f:2} "f") => 2
(elt {f:3} (quote f)) => 3
If the collection is empty, null, or void, the result is void. If the key
isn't appropriate for the collection, or if the key doesn't identify an
element within the collection, the result is void.
(elt null.list 0) => void
(elt [0, 1] 2) => void
(elt [0, 1] "2") => void
(elt {f:2} "g") => void
Since elt is a procedure, field names must be quoted or else they will
be evaluated as a variable reference:
(elt {f:2} f) => ERROR: Unbound variable reference
(let [(g "f")]
(elt {f:2} g)) => 2
An exception is raised if the collection has an unsupported type.
(ident left right)
Returns true if and only if the arguments are the same object; that is, when they have the same object identity.
(ident 1 (quote a::1)) --> false
(let [(v "hi")] (ident v v)) --> true
(let [(v (quote a::"hi"))] (ident v v)) --> true
(ident (void) (void)) --> true
At the implementation level, ident is a trivial pointer comparison.
As such, it exposes some implementation details that may change over time or
across platforms.
Since Fusion symbols are interned, those with the same text and annotations are
always ident:
(ident (string_to_symbol "barn")
(quote barn)) --> true
(ident (string_to_symbol "barn")
(string_to_symbol
(string_append "ba" "rn"))) --> true
(ident (quote a::barn) (quote barn)) --> false
Fusion allows implementation optimizations that copy and/or intern numeric values as needed. Therefore there's no promise of stable object identity for numbers, and this operator may behave in unexpected and implementation-defined ways when applied to them.
(ident 2 (+ 1 1)) --> // unspecified
(let [(v 2)] (ident v v)) --> // unspecified
Other than symbols, it is not specified whether literals or quoted data are
interned, so there's no promise of identity across two different syntactic
instances of the same value:
(ident 10600439 10600439) --> // unspecified
(ident "hi" "hi") --> // unspecified
(ident (quote a::"hi") (quote a::"hi")) --> // unspecified
(if test then else)
Evaluates the test expression first. If the result is truthy, evaluates the
then expression and returns its value. Otherwise, evaluates the else
expression and returns its value.
All values are "truthy" except for false, void, and any variant of null.
Note that only one of then or else expressions is evaluated, and both are
in tail position.
(is_blob value)
Determines whether a value is of type blob, returning true or false.
(is_bool value)
Determines whether a value is of type bool, returning true or false.
(is_clob value)
Determines whether a value is of type clob, returning true or false.
(is_decimal value)
Determines whether a value is of type decimal, returning true or false.
(is_empty collection)
Returns true if the size of the collection is zero, otherwise returns
false.
(is_float value)
Determines whether a value is of type float, returning true or false.
(is_int value)
Determines whether a value is of type int, returning true or false.
(is_list value)
Determines whether value is a list, returning true or false.
(is_null value)
Returns true when value is any Ion null, false otherwise.
(is_null_null value)
Determines whether a value is null.null, returning true or false.
(is_procedure value)
Returns true when value is a procedure, false otherwise.
(is_sexp value)
Determines whether a value is a sexp, returning true or false.
(is_string value)
Determines whether a value is of type string, returning true or false.
(is_struct value)
Determines whether value is a struct, returning true or false.
(is_symbol value)
Determines whether a value is of type symbol, returning true or false.
(is_timestamp value)
Determines whether a value is of type timestamp, returning true or
false.
(is_void value)
Determines whether a value is the Fusion void value.
(lambda (arg ...) body ...+)
Returns a procedure. When invoked, the caller's arguments are bound to the
arg identifiers (the formal arguments) and the body is evaluated and
returned.
body may be one or more forms; the last form is in tail position and its
result is the result of the procedure invocation.
The Fusion runtime system may optimize procedure instantiation, so it is
unspecified whether one or more lambda expressions will return distinct
or identical objects for any evaluation.
(lambda rest_id body ...+)
This variant, which declares a single formal argument rather than a sequence
of them, returns a procedure that accepts any number of values, which are
collected into an immutable sexp and bound to the rest_id:
((lambda args args) 8 9 10) --> (8 9 10)
(let ((ident expr) ...) body ...+)
Evaluates the exprs left to right, then binds each ident to its
corresponding result, then evaluates body. The scope of the idents only
covers the body, not the exprs.
body may be one or more forms; the last form is in tail position and its
result is the result of the entire expression.
(let loop_id [(ident expr), ...] body ...+)
This variant also creates a procedure, bound to the given name loop_id, that
accepts the same number of arguments as there are idents. When invoked, the
procedure binds the idents to the arguments and evaluates the body.
For example, this snippet loops through the standard input port and writes
the title field of each item:
(let loop [(item (read))]
(unless (is_eof item)
(let [(title (. item "title"))]
(writeln title)
(loop (read)))))
(While illustrative of looping, this is not the recommended way to accomplish this; see /fusion/io for better approaches.)
(let_values (((ident ...) expr) ...) body ...+)
Captures multiple results, creating local
bindings for the idents, with the body in scope. The exprs are evaluated
left-to-right, and must return as many values as there are corresponding
idents, which are then bound to those results. After the bindings are
installed the body is evaluated. body may be one or more forms; the last
form is in tail position and its result is the result of the entire expression.
(letrec ((ident expr) ...) body ...+)
Creates new binding locations for each ident, binds them to their exprs,
then evaluates body. The exprs are evaluated left-to-right, and the
idents are bound in all exprs and bodys. body may be one or more forms;
the result of the last form is the result of the entire expression.
(lets [(ident expr), ...] body ...+)
Like let, but each binding is created (and its expr evaluated) one by one,
and the idents are bound in the following exprs as well as the body.
(lets [(a 1),
(b (+ a 1))]
[a, b]) => [1, 2]
(module name language body ...+)
Declares a module containing the given body. The name must be a symbol; it is
ignored when loading a module from a file.
The language must be an absolute module path. The denoted module is
instantiated and all of its provided bindings are immediately imported. This
"bootstraps" the module with a set of bindings that form the base semantics
for the body. Unlike bindings that are required, these bindings can be
shadowed by module-level definitions and by require statements.
When compiling a module, the body forms are partially macro-expanded in
order to discover certain core forms like require and provide. The former
are handled immediately, before other forms. The latter are handled after
all other forms.
At module level, the elements within begin forms are spliced into the
enclosing module body, replacing the single begin form with its elements.
This effectively enables module-level macro uses to expand into multiple forms.
(not value)
Returns true if value is untruthy, false if value is truthy. Truthiness
is as defined by if.
(only_in module_path id ...)
A require clause that imports only the given ids from a module.
If an id is not provided by the module, a syntax error is reported.
Bindings introduced by this form use the lexical context of the module path, not that of the given identifiers.
This form can only appear within require.
(or expr ...)
Evaluates the exprs from left to right, returning the first truthy value
that results (and ignoring further expressions). If they all return untruthy
values, the last one is returned.
The last expr is in tail position. Given no exprs, the result is false.
(prefix_in prefix_id module_path)
A require clause that adjusts each identifier to be bound by prefixing it
with prefix_id.
Bindings introduced by this form use the lexical context of the module path, not that of the prefix identifier.
This form can only appear within require.
(provide provide_clause ...)
Declares bindings to be exported from the enclosing module. This form may only appear at module level.
Each provide_clause denotes some names to be exported. The following clause
forms are allowed:
- An identifier defined at module-level or imported from another module.
all_defined_outexports all module-level definitions.rename_outexports selected bindings, giving them new names on the way out.
Within a module, a single provide form with multiple clauses behaves
identically to multiple provide forms with single clauses.
(quasiquote template)
Like quote, but the template datum may contain nested unquote
forms that act as escapes. The unquoted expression is evaluated when the
containing quasiquote is evaluated, and its result is inserted into the
containing quoted datum.
(quasiquote [(+ 1 2), (unquote (+ 1 2))])
==> [(+ 1 2), 3]
unquote forms only escape one "level" of quasiquotation:
(let [(v 1)]
(quasiquote (a (quasiquote (b (unquote v) (unquote (unquote v)))))))
==> (a (quasiquote (b (unquote v) (unquote 1))))
(quote datum)
Returns the Ion datum as-is, without evaluation.
(rename_in module_path (exported_id local_id) ...)
A require clause that imports each exported_id using the name local_id.
If an exported_id is not provided by the module, a syntax error is reported.
In contrast to other require-clauses, bindings introduced by this form use the
lexical context of the local_ids, not that of the module path.
This form can only appear within require.
Warning: This behaves differently than Racket's rename-in, which imports
everything from the given module (or nested require-clause) while renaming
selected items from that set. In contrast, this form ignores exported bindings
that are not renamed.
(rename_out (local_id exported_id) ...)
A provide clause that exports each local_id using the name exported_id.
This effectively renames the binding on export.
This form can only appear within provide.
(require require_clause ...+)
Declares bindings to be imported into the enclosing namespace. This form may only appear at module level or top level.
Each require_clause denotes some bindings to be imported. The following
clause forms are allowed:
- A string or symbol containing a module path; all names
provided by the referenced module are imported. The bindings introduced by this form use the lexical context of the module path. only_inenumerates a set of names to import.prefix_inprovides a prefix to imported bindings.rename_inrenames specified bindings.
Within a module, require declarations are processed before other forms,
regardless of their order within the module source, and imported bindings are
scoped across the entire module. No identifier may be imported multiple times,
unless all such bindings refer to the same originating definition. Furthermore,
no identifier may have both an import and a module-level definition.
In other words: module-level bindings introduced by require or define must
not conflict, although either may shadow same-named bindings introduced by the
module's language declaration.
At top level, require will replace an existing import, and may shadow an
existing top-level definition.
(same left right)
Like ident but sound for numbers. That is, this procedure guarantees
equivalence of (unannotated) numbers with the same type, value, and precision,
while ident does not. In effect, unannotated numbers behave as if
they were interned.
As with ident, values with different annotations are never same.
(same 1 (quote a::1)) --> false
(same 2 (+ 1 1)) --> true
(let [(v 2)] (same v v)) --> true
(same 10600439 10600439) --> true
(same (quote a::"hi") (quote a::"hi")) --> // unspecified
(size collection)
Returns the number of elements in the collection.
The size of null.list (etc.) is zero. If collection is an improper sexp,
an exception is thrown.
Warning: Computing the size of an sexp takes linear time, since it must traverse the linked list of pairs to count elements.
(type_annotations value)
DEPRECATED as of Fusion R20; renamed to
annotations.
(unless test body ...)
Evaluates the test, and if it is not truthy, evaluates the body forms
left to right. The last body is in tail position, and its result is the
result of the entire form. If the body isn't evaluated, the result is void.
A companion form is when.
(unquote expr)
Used as an escape within a quasiquote form; not valid in any other context.
(values value ...)
Produces multiple results, returning the zero
or more values. Usually used in conjuction with let_values to bind the
results to names.
(void arg ...)
Returns the singular void value, ignoring all args.
(when test body ...)
Evaluates the test, and if it is truthy, evaluates the body forms left
to right. The last body is in tail position, and its result is the result of
the entire form. If the body isn't evaluated, the result is void.
A companion form is unless.
('#%top' id)
References a top-level definition for symbol id, skipping over any
surrounding local bindings. Within a module, id must be defined within the
module and not locally.
As suggested by the awkward name, this form is rarely needed by application code and is primarily an artifact of the macro-expansion process.