The Urn Logo

Version 0.7.0 released

How time flies. It’s been almost three months since the last Urn release, and a whole year since the first one. I can’t say there’s one “super exciting” feature in this update, but instead there’s been many small improvements to the standard library and internals.

Configurable assertions

Often when I write Urn (or any other language for that matter), I find myself needing debug assertions. Namely, assertions which can be enabled at development time or disabled when I need to eck out every last bit of performance. Urn now has built-in support for this, thanks to the demand and desire macros.

Both of these macros take a value which must be upheld and, optionally, an error message which will be displayed if it is not.

> (defun negate (a)
.   (demand (number? a) "a must be a number")
.   (* a -1))

> (negate false)
[ERROR] demand not met: (number? a) (a must be a number).

stack traceback:
  lib/core/demand:29-31: in global 'demand-failure'
  <stdin>:2: in global 'negate'
  <stdin>:1 in main chunk

The difference between the two is when the assertions are enabled. demand is enabled by default, being disabled by passing -flax-checks to the compiler. Conversely, desire is disabled by default, with -fstrict-checks enabling such assertions.

Utilising a similar mechanic, one can also use -fstrict-structs to add typechecks to struct getters and setters. This will ensure the target is a struct of the expected type, preventing you from passing modifying unknown values.

You can enable all “strict” checks with the -fstrict flag, and disable all normal checks with -flax. Note that -flax arguments take precedence over their strict equivalents.

Rewritten native bindings

Interacting with Lua has always been a bit of a tricky thing with Urn. Whilst things have changed several times over the various Urn releases, we’ve never really hit a “sweet spot”. That being said, the latest redesign is substantially closer than we’ve got before.

Previously, one would define a .meta.lua file, which defined how various native definitions worked: whether they were some piece of Lua syntax, or a binding to an arbitrary Lua expression: whether they were some piece of Lua syntax, or a binding to an arbitrary expression. Whilst we are still sticking with this concept, all of these declarations have moved inline with the main define-native file.

This makes things a little cleaner (as we no longer need multiple files) and allows for dynamically generating definitions at compile time.

Compiler cleanup

As the Urn compiler is almost as old as Urn itself, most of it was written without access to some of the more recent features. One of the ways this is most apparent is how raw Lua tables and indexes are used for every structure, meaning you have no clue what (.> x :name) is referring to. Additionally, it makes it awfully easy to misspell field names, or set the correct field on entirely the wrong object.

This Urn release makes great strides in “sturdying-up” the compiler, converting many of the data structures to use defstruct. Not only does this provide better type safety, it also makes it much easier to read and write documentation on specific fields.

REPL reloading

One of the workflows I often rely on is loading files into the REPL, checking functions work as expected, and then modifying the files if not. Sadly, each time you change the file, you need to restart the REPL, meaning you have to start from scratch. This release adds the :reload (or :r) command to the REPL. This will scan all loaded modules, determine which ones have changed, recompile them. This means you can add or remove features without ever having to leave the comfort of Urn.

We’ve also rewritten the online REPL, using lua.vm.js to run the Urn compiler in the browser. This should make things more responsive (and removes the need to rely on friends’ servers). Many thanks to RawGit for their GitHub CDN service, which is used to fetch the standard library.

Do note that this is just a snapshot of the changes included in this release of Urn. Do check the full changelog or peruse the docs for more information.