Insert snappy bird pun here

This week progress on the latest exercise was non-existent. Instead, I took some days off work, hung out at some science museums, did some kitchen reno work, and played around with Wren some more.

Usually Lua is my go to language of choice for embedding in applications for scripting purposes, so what follows is a little comparison of how Wren and Lua differ in this regard,

Before I start, I’d like to point out that I am by no means an expert in either Lua or Wren, and that Wren is still in it’s early stages of development as of the time of this writing. As such anything I say here is subject to change at a moment’s notice.

One of the design goals of Wren is a small expressive language with an interpreter that is very easy to read. Since currently documentation is still fairly sparse (it is early days, after all), going to the source to see how things are fitting together is almost a requirement.

In my opinion, the code for Wren is incredibly well written and commented, which is a stark contrast to the Lua interpreter. This is of course a matter of personal style, but I personally do not fall naturally into the group of people that comment very sparsely because “clear concise code is easy to read”.

Yes Virginia, I can read your code, but since I’m not as familiar with it as you are, I have no idea why it’s doing what it is clearly doing without scooting all over everywhere trying to piece things together. Where the Lua source is somewhat inscrutable without some study, the Wren code is much more clear in this regard.

The syntax of Lua proper is designed such that it can be used by non-programmers, which makes it a little more verbose (e.g. you use begin and end instead of { and } for blocks). On the other hand, Wren comes off as a programmer’s language. Allow me to explain.

One of the design decisions made for Wren is that there shall be no statement separator (e.g. ; as in C). In order to make the grammar unambiguous, the brace following a conditional statement must be on the same line as the conditional. The following is not allowed in Wren:

if (value == true)
{
    System.print ("True)
}

Of course this is one of those stylistic debates that will rage forever with no real winner. If you happen to be of the clan that likes braces on the same line, you’re in luck and Wren will be that much easier for you to use. If like me you find that style counter-intuitive to look at, you’re in for some pain.

The standard libraries for Wren are very sparse, owing both to it’s youth and also due to it’s being designed to be embedded in your application, where you are expected to bind it to what you want. Although the official distribution comes with, for example, an io module in the included command line interpreter, the code is (by design) tightly coupled with the internals of that code, making it more than a trivial task to put into your own code. This may or may not be a deal breaker.

By far the most interesting aspect of Wren that I’ve come across so far is the method by which you give it code to run (both its own native code which it must compile or native methods that you provide yourself). It operates much differently than Lua does, and the way that it works makes it potentially more of a pain to bind your own code, but it still seems much more elegant. I haven’t gone too far down this path yet with a big serious binding (this is still pending in my tests, more on that later).

As an example, if you were embedding Lua into your application to bind some methods and then run some code, you would first create a Lua state, then either set up a table of methods (possibly in the Lua state’s package table for other code to require() or as a global). When you want to run some Lua code, you can give the VM some source to compile, some precompiled bytecode, or look up a function or method. All return a method on the Lua stack for you to execute.

Wren is a little more complex in this nature, but more powerful. I assume that the way the embedding API works is built around the notion that Wren is class based (whereas in Lua, you can take advantage of syntax to make classes, but this is not required).

Wren instead takes the tack that instead of being able to define classes or methods wholely from within your embedding code, you use a mix of Wren code and embedded code to do so. The main flow is roughly:

  1. Initialize a new Wren VM instance (akin to a Lua state). A part of this is providing a configuration object which provides various (optional) callbacks that the VM will invoke as needed to interface with your embedded code.
  2. Everything is defined in terms of actual Wren code. This means that if you want to create a class that uses native code for its methods, you would write a description of the class as if you were implementing it in Wren, and use the syntax of the language to mark methods as being foreign (native).
  3. As the code is being compiled, Wren uses the callbacks that you provided to give you a chance to provide it with the actual native code that associates with each native piece of code.

 

On the whole, this makes writing the glue code for your native code a bit more complex than it would be for Lua. For example, when the callback for providing a foreign method is invoked, you are given the name of the module and class that the method is for and the signature of the method (i.e. the number of arguments it takes as well as extra information if it is static or an operator overload or some such). You must then use this information to look up and return the native method.

This is sort of a funky world view when you’re used to the Lua method of doing things, but I think it’s a lot more expressive. One issue that I have with it is that it seems like it shifts method binding to be a run time issue instead of a compile time issue. It’s a fast process, but I like the idea of knowing that if the binary compiled, I likely bound the methods correctly in my source code.

My testing got stalled out a bit in its early stages when I went looking for some information and came across the discovery that the Wren VM is not re-entrant (yet; I don’t know if that’s a planned thing or not, but I assume so). This means that your native method can’t invoke any other Wren code (native or not); it can only provide a value back to the caller or do its work and exit.

To be honest I don’t know if that’s a show stopper per se; Wren by it’s design uses Fibers (coroutines in Lua parlance) extensively. So it may well be that my mind set for how the Wren code is structured does not line up with the intended use case of the API’s as they currently stand.

A particular failing of this particular design (which there is now an issue for as a result of my query on the mailing list) is that it is not currently possible for native methods to trigger a runtime error.

In Lua, there is a function (error() in Lua code or lua_error() from C) which you can invoke to trigger a runtime failure. For example, if your method expects to be invoked with a number as an argument, but the caller provided a string instead.

The way to accomplish this in Wren is to abort the current fiber; however there is no way to do that from C, only from Wren code. Due to the lack of re-entrancy in the VM, you can’t look up the appropriate method and invoke it. Instead you must work around the problem by having the native Wren code do it. For example:

class Sample {
    construct new () {}

    calculate (parm1, parm2) {
        if (!(parm1 is Num)) Fiber.abort ("first parameter must be a number")
        if (!(parm2 is Num)) Fiber.abort ("first parameter must be a number")
 
        return calculate_ (parm1, parm2)
    }

    foreign calculate_ (parm1, parm2);
 }

That is to say, the Wren part of the class has to do any error checking that you need so that the actual foreign method (with it’s slightly manged name) can operate knowing that the parameters are valid. Of course, if what you want to check is something that is more complex, then things get more complicated and your method needs to return a special sentinel value and the Wren code gets more complex to boot.

It was looking into this particular situation that stalled out my integration testing to try and put things through their paces. Now that I have a definitive answer, I can continue on. As stated, the author admits that this is somewhat of a failing of the current API and wants to work to resolve it. So who knows how long this might be an issue?

The take away that I have so far is that the workflow is different, better in some ways, a little worse in others. Which is about par for what I would expect for any scripting language. The real test is to try and bind some simple classes and see how easy it is to get things working,and if the code seems better or worse than what might otherwise be availabnle.

I plan to do more work in this regard in the coming weeks. I spent more time on it this week than anything else because it’s an interesting thing to play with, but also I knew time was going to be short in general with all of the RL stuff happening.