Gren 24W: Streams, static executables and the compiler as a package
Published: 2024-12-16Gren 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?
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:
- compiler v0.5.0
- gren-lang/core v6.0.0
- gren-lang/browser v5.0.0
- gren-lang/node v5.0.0
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
- All functions named
filter
have been renamed tokeepIf
. Similarly,filterMap
has been renamed tomapAndKeepJust
andflatMap
tomapAndFlatten
. - Functions named
length
are expected to return in constant time. Where this isn't the case, the function has been renamed tocount
. Node.endWithCmd
has been replaced withNode.endSimpleProgram
.- The time-travelling debugger has been removed.
- The
ChildProcess
module has seen some incremental changes to fix a design mistake and to support streams for communication between processes. - The
FileSystem
module now has functions for opening files as streams. - Fixed a few sourcemapping bugs when compiling on a Windows system.
Thank you, contributors
This release was made possible through contributions from the following people: