announcements
Jul 14, 2022

Announcing Unison Milestone Release 4

Rúnar Bjarnason

We have just released a major milestone of Unison: version M4, and it's a big one. Here are the details on the most significant changes.

Code hosting on Unison Share

We built our own codebase hosting specifically designed for Unison, as a feature of Unison Share.

Before now, if you wanted to collaborate on a Unison codebase or share your code with others, you had to do so through binary files managed with Git. When viewing your code on e.g. GitHub, all you saw was a directory with a binary file in it.

This all changes today. You can now push your Unison code directly to Share via the Codebase Manager, and then browse and share it in a beautiful interface, all gloriously hyperlinked.

How to get started with Unison Share

  1. Create a new Share account at share.unison-lang.org. You can use your GitHub account.
  1. Start ucm in your terminal, pointing to your Unison codebase.
  1. From ucm, issue push myUsername.public.project mycode where mycode is whatever namespace you want to push to Share, and myUsername is your Share user name (which is the same as your GitHub user name).

You can now share your code with the world!

Here's a short video of Simon pushing his code:

What will happen to the old Unison Share?

The old Unison Share site is still available for now at share-prev.unison-lang.org, but it's deprecated. If you have Unison code on the old site, we encourage you to push it to the new Share as described above.

Self-contained namespaces

In past versions of Unison, name resolution was global for your whole codebase. That is, if you used a name like foo, Unison would look for things named foo everywhere in your codebase to figure out what hash you meant.

This had a number of problems:

  1. Name lookup was slow for large codebases.
  2. If you had a lot forks of a namespace, you might end up with ambiguous names, and Unison would insist on overly long fully-qualified names to disambiguate.
  3. Namespaces implicitly depended on each other in a way that was hard to understand. As namespaces evolved separately, you might end up with code referencing hashes that no longer had names. If you pushed a namespace to share, but not its dependencies, you might end up with code on Share with hashes instead of names.

Starting with version M4, Unison's namespaces are self-contained, meaning that name resolution is local to each namespace. This makes the Unison workflow slightly different, but it's a lot easier to understand in the end. Whereas before M4, you could do the following:

  1. Start up UCM.
  2. cd into a new namespace foo.
  3. Start coding with everything from your codebase in scope.

Now you do this:

  1. Start up UCM.
  2. cd into a new namespace foo.
  3. fork any namespaces you want to use, and put them under foo.lib. For example, fork .base foo.lib.base. You can even have a namespace in your codebase that serves as a template, and fork that for each new project.
  4. Start coding with just the namespaces you want to use in scope.

In future releases of Unison, we will add lots of quality-of-life features to make managing your namespaces and projects more ergonomic.

Mutable and immutable arrays

Unison M4 gets some new built-in types representing in-memory arrays:

Arrays allow for efficient memory usage as well as fast random access, splitting, and slicing. What's more, mutable arrays are scoped using the Scope ability, which means mutable memory can't escape the scope in which it was allocated. For example, you cannot have a global mutable array, and the type system enforces this.

See arrays for more information on how to use arrays.

A date/time library

There are new builtins for nanosecond-precision system clocks. For example:

  • now returns the current time, as an Instant.
  • Clock.monotonic returns a monotonically increasing time.Duration that is guaranteed to never go backwards (which can otherwise happen if the system clock is adjusted).

The base.time namespace contains a number of useful functions and data types for working with time and dates.

Efficient regular expressions for text and binary data

The Base library has a new type, Pattern, which is a regular expression pattern. A p : Pattern Bytes is a pattern that matches Text input, and a p : Pattern Bytes is a pattern that matches Bytes input.

The ["123"] in the above output is a list of captures, and the "abc" is the remainder of the input after the Pattern.many patterns.digit successfully matches.

A Pattern is built up using functions like Pattern.many, patterns.literal, charRange, and so on, and run with either Pattern.run or isMatch:

Pattern.run : Pattern a -> a -> Optional ([a], a)
isMatch : Pattern a -> a -> Boolean

For more information on patterns, see Pattern.

New keyword: do

The do keyword is new syntactic sugar for 'let, which introduces an unevaluated block of code. For example, you can now write:

main : '{IO, Exception} ()
main = do
  printLine "Greetings, earthling! 👽🪐"
  printLine "You can still use 'let if you really want."

The pretty-printer uses do wherever it can, so all your existing code is already updated to use this new syntax.

And lots more

Those are the big changes, but there have also been a ton of bugfixes, performance enhancements, and various ergonomic improvements.

Check out the release notes for the full list of changes.

Head to the Unison website for download/upgrade instructions.

Do create a Unison Share account and put all your amazing code there.

Enjoy! 🚀