Two releases today.

  • 1.1.0 is the full release: new standard-library functions, correctness fixes, and the security hardening described in our companion post.
  • 1.0.2 is a conservative patch off 1.0.1: the same security and bug fixes, but no new features and no new language surface. If you pin to ~1.0, you can still take the safety fixes without moving to 1.1.

Both are on npm.

New in 1.1.0

  • slice(s, start, end) — extract a substring by start and (exclusive) end index. It’s the pair to substring(s, position, length): one takes an end, the other a length. The stdlib page now documents them side by side, because reading one as the other was a real source of silent wrong results. slice('hello world', 6, 11) gives 'world'.
  • chr(n) — the character for a Unicode code point, so an invisible character like a non-breaking space (chr(160)) can be written explicitly instead of smuggled through the source as a literal.
  • round(x, n) — round to n decimal places (round(3.14159, 2) → 3.14), with the same half-away-from-zero rule as round(x).
  • Two-argument min(a, b) and max(a, b) alongside the list forms, so max(min(x, hi), lo) no longer builds lists to compare two numbers. They compare rather than coerce.
  • sortBy(list, key) is now documented on the stdlib page. It already existed and worked — it was just missing from the page the playground sends people to.

Correctness fixes (in both releases)

Several defects that produced a wrong value rather than an error — the kind that propagates silently — are fixed: Python dropping parentheses around dynamically-typed operands, float literals collapsing to integers, type selectors losing their operand’s grouping, at()/first() leaking a nullish fallback into the caller, string functions emitting unparseable JavaScript on an integer literal, and replace()/replaceAll() treating their search string as a regular expression on PostgreSQL while every other target matched it literally.

A note on arithmetic. Some of these fixes change results that were previously wrong — so output can change:

  • 7 / 2 is now 3.5 in Ruby (was 3 — Ruby was truncating integer division);
  • round(2.5) is now 3 in Python, round(-2.5) is now -3 in JavaScript (halves now round away from zero in every target);
  • 1.0 / 4 is now 0.25 in Ruby (was 0).

If any of your code compensated for the old behaviour, revisit it. The CHANGELOG lists every change with this warning spelled out.

Security hardening (in both releases)

Both releases include a round of denial-of-service and sandbox-boundary fixes made under coordinated disclosure. Rather than repeat them here, see the companion post, How we think about Elo security, and the refreshed security page — which now separates what Elo guarantees (the compiler can’t be subverted; the emitted code can’t reach host capabilities) from what you must provide (a memory-and-timeout sandbox for untrusted expressions). Thanks to uaaw and Atsushi Sada for the reports.

Which should I take?

  • Move to 1.1.0 if you can — it’s a superset: every fix, plus the new functions.
  • Stay on 1.0 with 1.0.2 if you’re not ready to adopt new features — you still get the security fixes and the correctness fixes, with no new surface to review.

Either way, read the arithmetic note above before upgrading a system that does money or rounding.