100 Languages Speedrun: Episode 70: Wren
Tags: wren
Published: 2022-01-28 16:52:54

Wren is an small language meant for embedding that aims to displace Lua before JavaScript does it.

It's so committed to the embedding use case, that it doesn't come with any way to run it in the default package (`brew install wren`) - that one is just an embeddable library. To actually run it you need to `brew install wren-cli`, or write an app embedding wren. I think that might be going a bit too far. As a consequence of this philosophy, it has extremely minimal standard library, making it not really suitable for standalone use.

Its syntax feels about halfway between Ruby and JavaScript, which is totally reasonable.

It doesn't seem to be doing anything particularly innovative, but its main competitors are Lua and non-browser JavaScript, so it could win by just having fewer issues.

### Hello, World! with Wren CLI

We can use "Wren CLI" to print Hello, World:

```
#!/usr/bin/env wren_cli

System.print("Hello, World!")
```

```
$ ./hello.wren
Hello, World!
```

It also provides a REPL, with ASCII art wren:

```
$ wren_cli
\\/"-
 \_/   wren v0.4.0
> 2+2
4
> "Hello, World!"
Hello, World!
>
```

### Embedded Wren

Wren's main focus is being embedded, and it has C API for embedding Wren in your C application. As it's all C API, you could also create a bridge between Wren and pretty much any other language. It's a real thing, I even used one between Ruby and Lua once for a build script for some Factorio mods.

Anyway, I won't be doing any embedding in this episode, and I'll treat Wren CLI as a representative environment where Wren code might live.

### Unicode

Wren REPL will outright refuse to accept any non-ASCII characters, like if you try to write some accented characters in a string, this happens:

```
$ wren_cli
\\/"-
 \_/   wren v0.4.0
> "Unhandled key-code [dec]: 196
> "
```

I usually test how languages handle Unicode conversion to upper or lower case, but Wren doesn't have any such functions.

Wren makes some really unusual choices with how it handles UTF-8 and bytes:

```
#!/usr/bin/env wren_cli

var name = "Alice"
System.print("Hello, %(name)")

System.print("Lengths are codePoints by default:")
System.print("Żółw".count)
System.print("🍰".count)

var s = "Żółw"
System.print("Bytes vs codePoints counts:")
System.print("%(s) - %(s.bytes.count) bytes")
System.print("%(s) - %(s.codePoints.count) code points")

for (i in 0..6) {
  System.print("s[%(i)] = %(s[i])")
  System.print("s[%(i)] byte = %(s.bytes[i])")
  System.print("s[%(i)] code point = %(s.codePoints[i])")
}
```

```
$ ./unicode.wren
Hello, Alice
Lengths are codePoints by default:
4
1
Bytes vs codePoints counts:
Żółw - 7 bytes
Żółw - 4 code points
s[0] = Ż
s[0] byte = 197
s[0] code point = 379
s[1] = �
s[1] byte = 187
s[1] code point = -1
s[2] = ó
s[2] byte = 195
s[2] code point = 243
s[3] = �
s[3] byte = 179
s[3] code point = -1
s[4] = ł
s[4] byte = 197
s[4] code point = 322
s[5] = �
s[5] byte = 130
s[5] code point = -1
s[6] = w
s[6] byte = 119
s[6] code point = 119
```

Step by step:

* variables are declared with `var`
* string interpolation uses `%()` - I keep complaining about how every language now has string interpolation, but every single one decided to use different syntax for it, and well, Wren picked yet another one.
* Wren has JavaScript-style loops with Ruby-style ranges, so we can do `for (i in 0..6)` and it will iterate from `0` to `6`
* `string.count` returns correct UTF-8 character count
* you can access bytes for a string with `string.bytes` - or code points with `string.codePoints`

And the wild part is how indexing works:

* even though `s.count` returns number of characters, `s[i]` uses byte indexes
* `s.bytes` acts like a normal array of bytes, `[197, 187, 195, 179, 197, 130, 119]`, `s[2]` meant byte at position `2` (counting naturally from 0)
* `s.codePoints` returns array of codePoints, but it has a bunch of `-1`s inserted to align it with byte position, so `s[2]` means "codepoint at byte position `2`", not "codepoint at codepoint position 2".
* `s.codePoints` is `[379, -1, 243, -1, 322, -1, 199]`
* `s[i]` returns a character (not byte), at byte position `i` - or garbage if that is not a valid start of a character

Julia is the only language I know which does something similar. I understand that this solution offers good performance and simplicity, but it must be a huge pain to actually use.

### Lists

```
#!/usr/bin/env wren_cli

var a = [1, 2, 3, 4, 5]

System.print([1, 2, 3])
System.print(1..3)
System.print(a.map{|i| i*2})
System.print(a.map{|i| i*2}.toList)
System.print(a.where{|i| i%2 == 1}.toList)
System.print(a.reduce{|i,j| i+j})

System.print(a == a)
System.print([] == [])
```

```
$ ./lists.wren
[1, 2, 3]
1..3
instance of MapSequence
[2, 4, 6, 8, 10]
[1, 3, 5]
15
true
false
```

* lists have standard syntax `[]`
* they support `.map`, `.reduce`, and `.where` with Ruby-style blocks - languages are slowly converging towards the same names for these, but as you can see from `where`, we're not quite there yet
* `.map` and `.where` return `MapSequence` not a list, so once you're done you need extra `.toList`
* Wren does not have structural `==` - it only works on primitive types, on lists it just checks for object identity, two lists with identical contents will not `==` each other

### Maps

```
#!/usr/bin/env wren_cli

var person = {
  "name": "Alice",
  "surname": "Smith",
  "age": 25,
}

System.print("%(person["name"]) %(person["surname"]) is %(person["age"]) years old")

for (prop in person) {
  System.print("Person's %(prop.key) is %(prop.value)")
}

System.print({} == {})
```

```
$ ./maps.wren
Alice Smith is 25 years old
Person's name is Alice
Person's surname is Smith
Person's age is 25
false
```

Maps behave pretty much as you'd expect. Iteration returns objects with `.key` and `.value`, in undetermined order, which is a nice API.

Unfortunately Wren doesn't have structural `==` for Maps either.

### Classes

```
#!/usr/bin/env wren_cli

class Point {
  x { _x }
  y { _y }
  construct new(x,y) {
    _x = x
    _y = y
  }
  toString { "Point(%(_x), %(_y))" }
  +(other) { Point.new(_x + other.x, _y + other.y) }
  ==(other) { other.type == Point && x == other.x && y == other.y }
}

var a = Point.new(400, 60)
var b = Point.new(20, 9)
var c = a + b
var d = Point.new(420, 69)

System.print(a)
System.print(b)
System.print(c)

System.print(c == null)
System.print(c == d)
System.print(c != d)
```

```
$ ./classes.wren
Point(400, 60)
Point(20, 9)
Point(420, 69)
false
true
true
```

Step by step:

* like most things, syntax for classes is a mix between JavaScript and Ruby
* instance variables have `_` sigil
* Wren has getters `.x` and zero argument methods `.clear()` as separate things - it recommends to use `()` form if method has side effects - that looks like a fairly unique design decision
* you can override `toString` and various operators like `+`
* by default `==` only compares object identity
* we can override `==` - interestingly that doesn't automatically override `!=` so here we have simultaneously `c == d` (using overriden structural `==`) and `c != d` (using default object identity). Ouch

### Should you use Wren?

The language is brutally minimalistic. It also makes a lot of quirky choices, but each one seems to be motivated by seeking implementation simplicity and [performance](https://wren.io/performance.html), traits that matter for its embedded use case.

So Wren gets a hard no from me for standalone use, but that's likely something even its creators would agree with.

For embedded use case, it actually looks like a very reasonable choice. I didn't really cover its C API, but it seems small and practical, and it compares quite well to its main competitors Lua and non-browser JavaScript, as well as other alternatives like Tcl, Guile, creating your application-specific language, or embedding a whole general purpose language like Ruby or Python.

I think Wren is a well designed language given tradeoffs it decided to make.

As Wren is still pre-1.0, here's what I'd recommend it to change, without compromising its goals:

* have some kind of module system for optional code, like a module with common string operations - not every application needs them, but it's better to make them available if you do
* really reconsider making `==` work as structural equality (or at least add `===` or something that does full structural equality)
* fix Wren CLI to support non-ASCII input
* maybe link `==` with `!=`? That was definitely an unexpected quirk

### Code

[All code examples for the series will be in this repository](https://github.com/taw/100-languages-speedrun).

[Code for the Wren episode is available here](https://github.com/taw/100-languages-speedrun/tree/master/episode-70-wren).