Gren 24W: Streams, static executables and the compiler as a package

Published: 2024-12-16

Gren is a pure functional programmming language that aims to be easy to learn and to reason about, while remaining powerful and portable enough for real-world use.

A new version of the Gren programming language is now available. There are some changes in syntax, an option for compiling to a static executable, a rewrite of the Stream and String modules and parts of the compiler are now available in package form.

What is this 24W thing?

Robin gave a demo on some of the features in 24W

June and December each year is when we do backward incompatible releases of the compiler and the core packages. Since each of these components have their own version it hasn't been completely clear what to call a specific release.

From now on we'll refer to a set of packages based on when they were released. 24W, or the 2024 Winter release, consist of the following software:

Syntax changes

case of has now been renamed to when is. That means that code like this:

case String.toInt "42" of
  Just num ->
    "The string is a valid integer."

  Nothing ->
    "The string is _not_ a valid integer."

Must now be written as:

when String.toInt "42" is
  Just num ->
    "The string is a valid integer."

  Nothing ->
    "The string is _not_ a valid integer."

The rationale here is simply that when is reads better.

A change with bigger impact is that custom types are now limited to either 0 or 1 parameters, where they previously could have many more.

Code like this is no longer valid:

type Person = Person String Int

If you want to associate multiple fields with a variant, you can use a record instead:

type Person = Person { name : String, age : Int }

The reasoning is the same as what lead to the removal of tuples. Positional parameters leads to cryptic and brittle code where named parameters don't.

Static executables

The rewrite of the compiler from Haskell to Gren continues at a slow but steady pace. In 24W the command line interface of the compiler has been rewritten to Gren. As we like to add new features while doing a rewrite, there's now a new command that gives you the option of compiling a Gren program to a static executable.

If you compile a program with make-static a couple of interesting things will happen.

First, it will compile your program to a JavaScript bundle using the --optimize flag. This bundle will then be executed in the NodeJS instance you're using to run the Gren compiler, stopping just shy of invoking the main function. The in-memory representation of your program at this point will be saved to disk as a snapshot, and then injected into a copy of the NodeJS executable.

When executed, your program will start up faster, as much of the costs associated with initialization -- like parsing JavaScript, evaluating constants etc. -- has already been done when building the snapshot.

How much faster your static executable will run depends on the size of your program. We've seen anything from 10-25% improvement in startup time.

Robin held a demo of this feature in A Tour of Gren 24W.

Stream rewrite

When reading a large file from the filesystem, or when retrieving a large payload over HTTP, you typically receive a couple kilobytes at a time instead of the entire thing all at once. Streams allows you to perform operations on these chunks as they come in, instead of waiting to perform the operation until you have the entire payload, saving both time and memory.

In 24S there was a Stream module in gren-lang/node, but functionality was limited to simple reads and writes and the only available streams where the standard input streams for communicating with the terminal.

For 24W the Stream module has been completely rewritten, giving you the possibility of defining your own streams over arbitrary types, and the option of combining several streams to a single pipeline. The module has also moved to gren-lang/core, meaning you can use streams no matter which platform you're targetting.

This new Stream implementation is based on Web Streams, which gives us access to some native stream transformations like compression/ decompression and converting between strings and bytes.

You can also use stream pairs to create a line of communication between different parts of your code.

For more information you can read the documentation of the Stream module.

String rewrite

The String module has been rewritten so that all functions, unless otherwise noted, deal with code points as opposed to character units. Code points is a variable sized representation of a unicode character, consisting of 1 or 2 character units. This change makes it less likely that you'll unintentionally operate on half a character, at cost of performance for certain operations.

On top of that, some functions have been introduce and some have been renamed, to be more in line with other modules like Array and Dict.

Misc. changes

Thank you, contributors

This release was made possible through contributions from the following people: