The Urn Logo

Latest posts

Version 0.3.1 released

It’s a little earlier than normal, but it’s still update time.

New documentation

This isn’t very exciting, but we’ve re-done a fair bit of documentation, adding explanations on syntax and special forms, as well as more tutorials on Lua interop and the compiler API & plugin system.

General recursion helper

demhydraz has been working on a whole range of improvements to the standard library. One of these is the new loop construct. This makes it possible to define a tail-recursive construct without an explicit letrec: Instead of calling yourself, you call recur. For instance, here is a naive way to reverse a list:

(loop [(o '())
       (l '(1 2 3))]
  [(empty? l) o]
  (recur (cons (car l) o) (cdr l)))

Version 0.3.0 released

Another update! Firstly, about Urn itself: someone posted a link to Urn’s repo on Reddit, which meant we got lots of useful feedback from various people.

Struct literals

I finally caved in and added “struct literals”. This means the compiler now has the notion of a table built in to it, and so can generate more efficient code. It also makes your code a significantly prettier:

(with (obj { :name "Bertie"
             :age 23 })
  (print! (.> obj :name)))

Compiler improvements

We’ve put some work into making the compiler easier to work with. Firstly, if you attempt to use an undefined variable, we’ll now suggest variables in scope with similar name. This makes it much easier to see when you’ve got a typo.

We’ve also improved multi-line support in the REPL. If Urn detects that an expression isn’t finished, it will automatically prompt for another line: meaning you input complex expressions with ease.

Version 0.2.9 released and a new collections library

It’s update time! Now with even more meta-programming.

Querying variables at compile time.

In addition to being able to add your own optimisation and analysis passes, we’ve also added the option to query the current context from macros and unquotes. This allows a way to dynamically query variable names, values and definitions at compile time. For instance, given:

(defun foo () (print! "Hello") 23)

we can then query foo using several methods from the compiler/resolve package.

,(with (var (var-lookup 'foo)) ;; Lookup the variable in the "active scope".
  (print! "Definition" (pretty (var-definition var))) ;; Print the variable's definition.
  (print! "Value" (pretty (var-value var))) ;; Print the variable's value
  (print! "Call" (pretty ((var-value var))))) ;; Print the result of calling the variable.

Collections 0.1

This is, as clearly evidenced by the lack of previous releases, the first release of the collections library. urn/collections is a collection (pun intended) of useful data structures and supporting architecture. While this version does not have many data structures, it does present a great leap forward in the supporting architecture area.

Namely, this release includes two much-needed critical bits of functionality, namely algebraic data types (algebraic.lisp) and lenses (lens.lisp).

Algebraic data types are a nice abstraction for presentation and decomposition of structured data, and, since they’re implemented as a relatively thin layer over lists, they are performant and compatible: namely, no modification has been needed for the standard library pattern matching system to support these ADTs.

Lenses are, basically put, purely-functional, composable getters and setters on steroids. You can use them to zoom into (pun intended, again) a bit of a data structure and potentially change it (or apply a function to it.) Additionally, there’s rudimentary support for lazy sequences, but those haven’t been integrated with either ADT or lenses.

Codegen improvements

There are also been a couple of minor optimisation and codegen improvements, fixing a couple of bugs, and reducing code size very slightly. For instance, this see these lines were reduced to a single if statement.

Version 0.2.8 released

Because Urn updates are like London busses: you have to wait for ages, then several come at once.

Multiple returns from macros and top level unquotes.

This has been on the todo list for an age, and I’ve finally got round from it. One of the biggest limitations of macros was that they could only return one value, meaning anything which needed to define multiple variables wouldn’t work. As of this release, you can return multiple values from these, meaning multiple pieces of code can be spliced in. This commit also allows passing multiple values to top-level unquotes, as well as also allowing top-level unquote-splices. For instance:

,@(list
  `(define foo ,(* 2 3))
  `(define bar ,(+ 2 3))

will now evaluate the body of the unquote splice, and push it in, resulting in

(define foo 6)
(define bar 5)

It is worth noting that multiple returns can only be used in blocks (lambda and conditional bodies).

Code-gen improvements

I won’t re-iterate previous posts about code-gen issues, but this release has also seen significant code-size reductions (about 780 LOC). We’ve grown even smarter about detecting various conditional expressions, meaning complex, multi-line if statements can be reduced to a single line. You only need to look at the first line of the diff to see how effective this optimisation is. Looking over the compiled code, I feel we’ve reached the point where the emitted code isn’t high quality, but it is acceptable. We’ve come away since the initial release.

Version 0.2.7 release

It’s been a while since the last update - sorry about that. However, work on Urn has continued, resulting in some significant improvements to various parts. So then, let’s dive in to the major changes.

Compiler Plugins

One cool little feature we’ve added is the ability to register custom optimisation and warning passes with the compiler. The primary purpose of this is library-specific optimisation and warnings: for instance, you could reduce (reverse (reverse x)) to x, (assuming the list is not subsequently modified). We’ve got plans to add several builtin plugins, such as basic type checking, but that will come at a later date.

Code-gen improvements

One of Urn’s biggest failings right now is that it doesn’t generate very readable or idiomatic Lua. Whilst we are never going to be perfect, this release has seen several improvements in the emitting of conditionals. For instance, 4 line if statements are now reduced to a single line and or or.

We’ve also extended the cases where directly-called-lambdas are inlined. Before it would only inline functions which were called with constant terms. Now we will inline any time we can guarantee the execution order of expressions will not be changed. For instance:

local msg3 = _2e2e_2("Expected ", arg12["var"], " after --", key6, ", got ", (function(xs15, idx5)
  return xs15[idx5]
end)(args3, idx3))

would not have been inlined before as idx3 is rebound elsewhere. Thanks to this optimisation, we now emit:

local msg3 = _2e2e_2("Expected ", arg11["var"], " after --", key6, ", got ", args3[idx2])

as expected. This has halved the number of directly-called-lambdas in the code base.

Over all, the code gen improvements have resulted in a 600 line reduction in the compiled compiler. Other files have seen similar reductions (urn/traceback went from 246 lines to 212).