The Urn Logo

Version 0.4.0 released

Oh my, it’s been a while since one of these. Rest assured, we haven’t been sitting idle - there have been 100 commits to master since our last release. So, let’s get into the changes:

We broke things

Firstly, this is a great opportunity to remind you that Urn, whilst pretty stable, is still in beta. This release has several breaking changes you should be aware of:

  • #s and # are now one function called n.
  • The # symbol is used to denote hexadecimal and binary literals. In order to stick closer to Common Lisp, you should now write #x23 and #b011011 when you require other bases. Escape codes inside strings have not changed.
  • Several pattern matching expressions have become infix. This makes some pattern matching code slightly easier to read. Please refer to the documentation to see what has changed.

Standard library improvements

Pattern matching improvements

As mentioned in the breaking changes section, pattern matching got tweaked a little. There are now support for struct patterns and additional guards. For instance, you can now trivially decompose a structure:

(define my-vector { :x 1 :y 2 :z 3 })
(destructuring-bind [{ :x ?x :y ?y} my-vector]
  (print! x y))

and add additional requirements:

(case foo
  [((string? @ ?x) :when (= (string/char-at x 1) "f")) x] ;; Find strings which start with "f".
  [_ "boring"]) ;; Anything else

Set functions

demhydraz has added functions for a whole host of set manipulation functions. This includes set difference, union, and probably more. Actually I’m not sure if it’s more. I haven’t checked. Which is fine, as no one actually got this far through the post. Many thanks to incinirate for his work on improving the performance of the nub function.

bignum and bit32

Very many thanks to CrazedProgrammer here for these contributions. Urn now includes a big integer library, as well as a various functions for manipulating bits. I’m sure both of these will prove immensely valuable in the future.

Compiler improvements

Let’s be honest, only I’m excited by these. There have been a couple of minor improvements to the compiler which results in more efficient or smaller code being produced:

Tail recursion into loops

In order to keep a more minimal core language, Urn has no loops. These are emulated via tail recursive functions. However, these are less efficient than conventional loops and so a solution needed to be found. In this version of Urn, top level definitions will be converted into loops where appropriate. For instance, consider this definition of part-all.

partAll1 = (function(xs12, i8, e1, f3)
  while true do
    if (i8 > e1) then
      return true
    elseif f3(xs12[i8]) then
      i8 = (i8 + 1)
    else
      return false
    end
  end
end)

We do not currently optimise tail recursive functions in letrec (and so the while or for macros), though this is something you can expect to see in a later release. I’m also working on ways to generate even more idiomatic Lua, perhaps replacing the above code with a normal for loop.

Rewrite rules/loop fusion

We’ve also added “loop fusion” as an optional compiler plugin. This allows you to define “patterns” which will get simplified at compile time. For instance:

(fusion/defrule (map ?f (map ?g ?xs))
                (map (lambda (%x) (?f (?g %x))) ?xs))

Will rewrite two nested maps into one map over a composition of the two functions. This reduces the number of temporary lists required when using lots of list functions. However, it is not enabled by default as it can technically change the order of operations. This shouldn’t affect most programs, as loop iteration code should generally be pure, but I don’t feel comfortable enabling it by default.

Operators support multiple arguments

This is a relatively small one: All “Lua” operators now accept a variable number of arguments. For instance:

(print! (+ a b c d))

will compile to:

print1((a1 + b1 + c1 + d1))