ucode Language Notes
ucode reads like JavaScript, and that is exactly what makes it worth writing these notes down: the places where the resemblance stops are the places where a module breaks. What follows are the differences that come up while writing modules for this collection. It is not a tutorial - the ucode documentation is, and it is worth keeping open.
Functions
Parameters have no default values. Writing function f(x = 1) does not compile, so test for null inside
the function and fill the default in yourself.
Rest parameters work: function f(a, ...rest) collects the remaining arguments into an array, empty when
none were passed, and it has to be the last parameter. Spreading works too, in calls and in array and object
literals:
push(cmd, 'add', ...packages);
let merged = { ...defaults, ...overrides };
There are no classes. Where you would reach for one, write a function that builds an object and returns it,
with the state captured in the closure - which is what AnsibleModule() is.
Values and collections
Arrays and objects are manipulated with global functions rather than methods: push(), length(),
sort(), map(), split(), join(), substr(), replace(), match(). There is no
array.push() and no string.length.
for (let item in array) iterates over the values of an array, not over its indices. Over an object, it
iterates over the keys. Use a counting for loop when you need an index.
There is no undefined: a key that is not there reads as null, so value == null is the test for
“not set”. Strings cannot be indexed with [] - take one character with substr(s, i, 1).
type() names the type of a value, and the names are not always the ones you expect: a string is
string, a floating point number is double, an array is array and a dictionary is object.
Numbers
+ concatenates as soon as either side is a string, so "1.5" + 0.0 is the string "1.50" and not
the number 1.5. Nothing complains: you get a string where you expected a number, and only notice further
down. Convert with unary plus instead - +"1.5" is the double 1.5 - and note that it reads a whole
number as an int, so add + 0.0 when you need a double. Unary plus does not read a leading dot
either: +".5" is NaN, not 0.5.
Dividing two integers yields an integer: 7 / 2 is 3, not 3.5. Make one side a double when you
want the fraction.
Errors
Errors are raised with die(), not throw, and caught with try/catch as usual:
let meta;
try {
meta = json(dump.stdout);
} catch (e) {
meta = null;
}
Standard library corners
A few behaviors are worth knowing before they cost you an afternoon:
printf()andsprintf()take%Jto format a value as JSON, andjson()parses a JSON string.Template literals in backticks interpolate with
${...}, as in JavaScript.Regular expressions are written as literals -
match(value, /^[0-9]+$/)- andreplace()takes thegflag for a global replacement.math.rand()seeds itself from the clock with millisecond resolution, so two processes starting together produce the same sequence. Do not use it to make something unique.fs.mkstemp()unlinks the file it creates and hands back an open handle, so there is no path to give to another program.fs.mkdtemp()returns a directory path, andopen(path, "x")creates a file only if it does not exist yet.There is no
getpid(). Reading the link/proc/selfgives the process id as a string.