Values¶
Expressions are evaluated into values at run-time (or live performance). There are two kinds of values:
-
scalar or atomic values, described in chapter Scalar Value, include the undefined value, booleans, integers, floats (IEEE double), symbols, function definitions, process definitions and running processes (exec);
-
data structures or compound values like strings (sequences of characters), tabs (tables, vectors), maps (dictionaries), and interpolated functions NIMs. Such data structures, described in chapter Data Structures, can be arbitrarily nested, to obtain for example a dictionary linking strings with vectors of interpolated functions.
A compound value is referred to using a handle (or pointer). So, the same compound value can be shared between variables or shared between nested data structures (see data structure).
This is important because a compound value v is a mutable data structure: you can change an element in the data structure and this does not change the value itself. It means that the variables referring to the value v will refer to the changed data structure. On the contrary, atomic values are immutable: you cannot change an atomic value, you can only build a new atomic value.
Functions can be used to combine values and build new ones. The programmer can define his or her own functions (see chapter Function), also having access to a large predefined Functions Library. The figure below shows a simple score excerpt employing a simple expression and value. The text score on the right declares four expressions to be sent to receivers “hr1-p” to “hr4-p” (harmonisers) whose final value is being converted from semi-tones to pitch-scale factor. This graphical representation shows their evaluation.
In this example we are able to use the final values of the expression in the graphical display of the score by AscoGraph since the arguments of the expression are constant. So these expressions are recognized itself as constant and their value is computed when the score is loaded (a mechanism known as “constant propagation”). If a variable were to be used, the expression would stay intact in the visual representation to be evaluated at run-time. Variables will be discussed in section Variable.
Dynamic Typing¶
From a programming language perspective, Antescofo is a dynamically typed programming language: value types in Antescofo do not need to be explicitly specified; the type of values are checked during the performance and this can lead to an error at run-time.
When a wrong argument is provided to an operator or a predefined function, an error message is issued on the console and the returned value depends of the operators involved (often, the undef value). See section Dealing with Errors for useful hints on how to debug an augmented score.
Compound values are not necessarily homogeneous : for example, the first element of a vector (tab) can be an integer, the second a string and the third a boolean.
Interpreting a Value as a Boolean¶
Each kind of value can be interpreted as a boolean. So any kind of value
may appear where a boolean is expected. For instance integer
0
is interpreted as false and all other integers are
interpreted as true.
The conversion rule are given for each value type when reviewing the types in chapter scalar Values and in chapter Data Structures.
String Representation of an arbitrary Value¶
The string representation of a value is the string that denotes this
value in an Antescofo program. For a value v
, the expression
"" + v
returns the string representation of v
. There is nothing
magic in this expression: the operator +
is an heavily
overloaded operator that is used to denote numerical addition but also
string concatenation. Arguments of +
are implicitly
converted if needed (for example the addition of an integer and a float
converts first the integer into the corresponding float). Here, the
expression denotes the concatenation of the empty string with an
arbitrary value v
and the string concatenation converts
implicitly v
into the corresponding string to computes the
concatenation.
Checking the Type of a Value¶
Several predicates check if a value is of some type:
-
@is_numeric (which returns true if the argument is either @is_int or @is_float),
-
@is_fct (which returns true if the argument is an intentional function)
-
@is_function (which returns true if the argument is either an intentional function or a map)
-
@is_obj_xxx (where
xxx
is the name of an object definition).
Value Comparison¶
Two values can always be compared using the relational operators
< <= = != => >
@min
and @max
operators.
The comparison of two values of the same type is as expected: arithmetic comparison for integers and floats, lexicographic comparison for strings, etc. When an integer is compared against a float, the integer is first converted into the corresponding float. Otherwise, comparing two values of two different types is well defined but implementation dependent.
Defining a value in JSON format¶
Json (for JavaScript Object Notation) is an open-standard format that uses human-readable text to transmit data objects consisting of attribute–value pairs. It is the most common data format used for asynchronous browser/server communication.
The Json format can be used to defines Antescofo values using the
keyword JSON::
in front of the json value definition. The
Antescofo notation and the Json notation coincides, at the exception
of map (dictionnaries). The correspondance between Json the value
types and the Antescofo types is given by the following table:
Json value type | Antescofo value type |
---|---|
bool | bool |
string | string |
number | int or float |
object | map |
array | tab |
null | undef |
The other Antescofo value types, like NIM or functions, cannot be written in Json.
For example (see Json for the format definition):
$m := JSON:: {
"1" : 1,
"2" : 2,
"map" : {
"a" : 0.10000000000000001,
"b" : 1.00000000000000000,
"pi" : 3.31415926534999983
},
"tab" : [
-1,
-1.11109999999999998,
"string",
[
111,
222,
333
],
{
"nested_map 1" : "1",
"nested_map 2" : null,
"nested_map 3" : [
]
},
0
],
"trois" : "trois"
}
defines the map
MAP{ ("1", 1),
("2", 2),
("map", MAP{ ("a", 0.1), ("b", 1.0), ("pi", 3.31416) }),
("tab", TAB[-1,
-1.1111,
"string",
TAB[111, 222, 333],
MAP{ ("nested_map 1", "1"),
("nested_map 2", <undef>),
("nested_map 3", TAB[]) },
0]),
("trois", "trois") }
Several predefined functions are defined to handle the json format:
-
A Json file can be read using the function @json_read which returns the corresponding Antescofo value
-
An Antescofo value can be written in a file in json format with @json_write (if the Antescofo value can be represented in json)
-
A string containing the description of a Json value can be translated into an Antescofo value with @json_string
-
And the same function can be used to turn an Antescofo value which is not a string into a string containing the json translation of this value.
The various kind of values are reviewed in chapters Scalar Values and Data Structures. Antescofo is a high-order language, so Functions and Processes are also values, as well as Actors.
In the rest of this chapter, we review: