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
- Create a new Share account at share.unison-lang.org. You can use your GitHub account.
- Start
ucm
in your terminal, pointing to your Unison codebase.
- From
ucm
, issuepush myUsername.public.project mycode
wheremycode
is whatever namespace you want to push to Share, andmyUsername
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:
- Name lookup was slow for large codebases.
- 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.
- 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:
- Start up UCM.
cd
into a new namespacefoo
.- Start coding with everything from your codebase in scope.
Now you do this:
- Start up UCM.
cd
into a new namespacefoo
.fork
any namespaces you want to use, and put them underfoo.lib
. For example,fork .base foo.lib.base
. You can even have a namespace in your codebase that serves as a template, andfork
that for each new project.- 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:
mutable.Array
is a mutable boxed array.mutable.ByteArray
is a mutable array of unboxed bytes.data.Array
is an immutable boxed array.data.ByteArray
is an immutable array of unboxed bytes.
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 anInstant
.Clock.monotonic
returns a monotonically increasingtime.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.
Pattern.run (Pattern.capture (Pattern.many patterns.digit)) "123abc"
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)
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! 🚀