This is a list of frequently asked questions about Unison, compiled and curated by the community. The source of this doc is here and contributions are welcome!

Build/install

Is there a nix expression for Unison?

Nix users can launch the Unison Codebase Manager with nix-shell like this:

% nix-shell -p unison-ucm --command ucm

   _____     _
  |  |  |___|_|___ ___ ___
  |  |  |   | |_ -| . |   |
  |_____|_|_|_|___|___|_|_|

  Welcome to Unison!

  I'm currently watching for changes to .u files under ~/ucm

  Type help to get help. 😎

.>

(This gives you a new shell with unison-ucm package already in it and then executes ucm inside.)

nix is not officially supported, but there seems to be a substantial overlap between nix enthusiasts and Unison enthusiasts, so feel free to give it a try and ask on slack if you get stuck.

  • Here is the nix derivation for installing Unison from the pre-built binaries.
  • See here for instructions on how to install the development environment in which Unison itself is built.

I get error while loading shared libraries: libtinfo.so.5

Some people see the following error when they try to run the ucm executable for the first time.

ucm: error while loading shared libraries: libtinfo.so.5: cannot
open shared object file: No such file or directory

As a workaround try the following:

-- assumes APT is your package manager
sudo apt-get install libtinfo5

(This issue is tracked under #602.)

Language

Does Unison have Haskell-style type classes?

No, or at least, not yet. We're considering our options for how to allow people to express bounded polymorphism. See this post for some initial observations.

Currently there are two ideas which have been developed at least to some extent. 1) Like this: mconcat : [a] ->{Monoid a} a, where Monoid a is an ability. 2) Like this: mconcat : Monoid a -> [a] -> a, where Monoid a is a record type.

Both of these would require a set of supporting language enhancements, for example to supply the Monoid a handlers/values automatically.

How similar is Unison to Haskell?

As a programming language, Unison can be viewed as a descendant of Haskell, with many similarities including

  • type inference
  • pattern matching
  • ADTs
  • purity (i.e. no side effects).

Some key differences...

  • As a new language, Unison is a good deal smaller and simpler than Haskell. The flip side of this is that it's currently missing a number of conveniences which Haskell users take for granted.
  • Unison supports algebraic effects, which it calls 'abilities', which replace monads and monad transformers for writing effectful code.
  • Unison uses a strict evaluation model, whereas Haskell's model is lazy.

Distributed computing

How can Unison's distributed execution be made secure?

There are actually a bunch of questions here - some are listed below. Some aspects of this need more research, but overall we believe that it will be possible to build secure systems using Unison.

How can a Unison node ensure that a peer which sends it a computation (a) is who it says it is, (b) is authorized to consume compute resources on the node, (c) is authorized to access data or perform effects on the node?

Computations from remote notes will be sandboxed, with resource and capability permissions applied to the sandbox. There will be a system for authenticating remote nodes and authorizing them for a given usage.

How can a Unison node know that code which it syncs in from a peer is safe to run, and will only use resources or privileges that the sender is authorized for?

Resource usage will be limited dynamically through the sandbox mechanism, with code execution throttled or cancelled if it breaches the limits. A program will only be able to use effects and I/O if the sandbox explicitly provides it the ability to do so. Unison’s type system enforces that programs only have access to the abilities they’re given.

How can a piece of Unison code/data be kept private to a node - so it doesn't get synced to other nodes?

For data, there is a plan to add a 'pinned data' facility, see issue #798.

For code, this is an active topic of research - see issue #799.

Given that Unison transfers code and data to peers, are any special code patterns required in Unison to prevent vulnerabilities?

Certainly yes, and it will be fun to build out our understanding of these patterns as we gain experience with the distributed execution API!

What does each node need to run to enable distributed execution?

A Unison runtime environment must be running on each node. This environment will accept network connections from Unison peers, and run the code syncing and distributed evaluation protocol.

Details on how this environment looks are TBD.

Where can I learn more about Unison's support for distributed computation?

This area of Unison has had plenty of thought and research, but isn't built yet. But here are some resources to give you a preview.

Codebase model

If definitions never change, how do you fix a bug or refactor an old definition?

You can also check out Paul's Scale By The Bay 2019 talk on the Unison codebase model for an explanation!

Definitions never change, but the names we give them do. Suppose I have a term with hash #1krif5, and I've given it the name foo. We can draw that as follows.

  foo   ->   #1krif5

Then I decide my code for foo is bugged, so I present Unison with a new definition for it. Then Unison will work out the hash of the new definition for foo, say #amrl61, and change the name foo to point to it. We end up with the following.

             #1krif5

  foo   ->   #amrl61

So, no terms have changed, but a new term has been added, and a name has changed to point to a different term.

Propagation

So that's all good, but what about the function bar which called foo?

Suppose we actually originally started with the following - one term referencing (calling out to) another, those terms being called bar and foo respectively.

  foo   ->   #1krif5
                ↑
  bar   ->   #doq7s1

Then our edit to foo actually left the codebase (for a moment) in the following situation, with bar still referencing the old version of foo.

             #1krif5
               ↑
  bar   ->   #doq7s1

  foo   ->   #amrl61

However, Unison assumes that this is not where you want to end up, and that actually, you want to propagate the updated definition of foo to the set of transitive dependents of the old term #1krif5.

As long as the old foo and the new foo have the same (or compatible) types, then Unison does this propagation automatically and leaves you with the following.

  (unused)   #1krif5

  bar   ->   #doq7s1
                ↓
  foo   ->   #amrl61

This propagation works recursively, so it deals with dependents of bar as well, and their dependents in turn, and so on.

If the types aren't compatible, then Unison can't automatically propagate the change. Let's suppose the old definition of bar had type Int, the new one has type Nat, and bar requires foo to be an Int. Then the todo command in ucm reports the following:

.> todo

  🚧

  The namespace has 1 transitive dependent(s) left to upgrade. Your edit frontier is the dependents
  of these definitions:

    foo#1krif5 : .base.Int

  I recommend working on them in the following order:

    bar : .base.Int

This is your cue to resolve the type discrepancy by editing bar (or rather, switching the name bar to refer to some new definition) so that it copes with foo being a Nat. If that edit to bar preserves its type, then Unison can take it from there and continue the propagation process automatically.

Notice that even while we were in the transient state as reported by todo, our codebase was still valid, and all our tests would still pass - it's just that at least some of them would still refer to the old definitions of foo and bar. And at no stage did we have to wade through a mass of compile errors: instead, the todo command leads us through a structured refactoring process, in which everything is valid and well-typed at all times.

Another thing: the instruction to 'replace #1krif5 with #amrl61' gets recorded by Unison in a 'patch'. Suppose you publish foo in a library for other people to use. Then your library users can take advantage of your patch to help them update their own code.

What happens if I hit a hash collision?

Your name will go down in history!

Unison uses 512-bit SHA3 digests to hash terms and types. The chance of two Unison objects hashing to the same digest is unimaginably small. If we wrote one billion unique Unison terms every second, we could expect a hash collision roughly every 100 trillion years.

If it did happen, you could simply tweak your term so it gets a different hash. For example, you could wrap it in a call to the identity function (which does nothing).

Is it inefficient to have the codebase be a purely functional data structure? Won't that involve a lot of copying?

Since the codebase is an immutable data structure where the elements of the structure never change, we can get the same kind of structural sharing we’re used to getting with data structures in functional programming.

In fact, Unison may waste a lot less disk space than traditional languages, since Unison does not have builds and therefore doesn’t generate build artifacts.

Does the codebase always keep getting bigger over time? Is there any way to remove code?

The codebase stores its complete history - the same as git does. That gives you a complete view of the operations that brought the codebase to its current state. It also means that you can refer to a given definition in the codebase, by hash, and be sure that that reference will never become undefined.

In future we may introduce a 'prune' operation, to allow you to remove definitions which have no inward references (from within the codebase).

How do I share my code?

You use the Unison Codebase Manager's push command, to write it to a git repository - for example, one hosted on GitHub.

Currently git is the only supported VCS protocol, but it would be perfectly feasible to add others.

(There's also always the option of zipping up your .unison directory! Its contents are free-standing and portable.)

How does hashing work for mutually recursive definitions?

We treat a set of mutually recursive definitions as a single “cycle” for hashing purposes.

For example:

f x = g (x - 1)
g x = f (x / 2)

First we replace any recursive calls in the cycle with the De Bruijn index of the cycle itself, like this (not valid Unison syntax):

$0 =
  f x = $0 (x - 1)
  g x = $0 (x / 2)

Nested cycles will get higher De Bruijn indices, but a top-level cycle will have index 0.

This transformation makes the elements of the cycle independent of any names. Then we hash each element of this new structure individually. Let’s say f gets the hash #222 and g get the hash #111. We then sort them to get a canonical order that is independent of their order in the source. This yields something like:

$0 =
  $0.0 x = #111
  $0.1 x = #222

We then hash this structure. Let’s say that hash is #ccc. Each definition gets a hash #ccc.n where n is the position of that definition in the cycle. Here g gets #ccc.0 and f gets #ccc.1. The final cycle will be:

#ccc =
  $0 x = $1 (x / 2)
  $1 x = $0 (x - 1)

This creates two new hashes, #ccc.0 and #ccc.1. Note that their definitions don’t refer to each other by hash, but by position in the overall cycle.

General

How do I run a program that uses IO?

In ucm, you can type run myProgram, where myProgram is some term of type '{IO} ().

Does Unison have IDE support? Editor support? Language Server Protocol (LSP) support?

We have editor support for the Unison syntax in Vim, Atom, and VS Code - see https://www.unisonweb.org/docs/editor-setup.

No editors currently understand the Unison codebase format, or offer function like 'find references', 'extract function', etc. You can use ucm commands to get some of these abilities, for example find.

There is a Unison Github repo explorer project (under development) which gives a browseable, hyperlinked view of the contents of a codebase.

Since the Unison codebase is a structured object containing full type information and metadata, we expect it will be possible to build developer tooling which beats that which exists for today's languages!

A previous incarnation of the Unison project actually started with a structure editor, meaning that the 'input some freeform text' stage is bypassed entirely. This is a direction we hope to come back to in future.

Does Unison compile to LLVM?

Not yet! We have done some exploratory work on this in the past, and expect to come back to it. For the moment, we have a nice, clean, but unoptimized interpreter. We definitely want to be able to compile Unison to native binaries in future.

Are you looking for help with developing Unison?

Yes! Please come and get involved 😊

The first step is to play with the language, and get familiar with writing Unison code. Also come join the slack, and browse through the issue tracker to see what's going on.

You can dip your toes by finding small ways to contribute a little piece:

  • take a look at the issues labelled 'good first issue' and 'help wanted'
  • see if you can find any tidying or refactoring you think needs doing in the parser-typechecker source (chat to us on slack before spending much time)
  • are there any contributor docs you'd like to see which you could make a start on?

Once you've had your first PR merged and gotten to know how we work, have a think whether you want to take on a bigger project! There's lots of cool stuff to do. Get in touch with Paul Chiusano via the slack and give him an idea of your areas of interest and level of experience. If you have your own suggestions for what you could work on then let him know!

Also think about any Unison libraries you could write - that's a way to contribute which requires very little coordination with the compiler team, and can have a big impact on the usability of the overall Unison ecosystem. Let us know in the slack what you're working on 😎