Build/install
Is there a nix expression for Unison?
Check out theunison-nix repository.
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 onslackif you get stuck.
I geterror while loading shared libraries: libtinfo.so.5
Some people see the following error when they try to run theucm
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.)
When I save my scratch file,ucm
doesn't react
On Linux, this can happen if your system has run out ofinotify watches
.(Backup applications often use a lot of these.)
You can see if this is the case by runningtail -f <file>
on a file of your choice, and looking out for the following output.
tail: inotify cannot be used, reverting to polling: Too many open files
If so you can up the limit by doing
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
Search stackoverflow for more tips or get in touch with us in the community slack if that command doesn't work for you.
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. Seethis postfor some initial observations.
Currently there are two ideas which have been developed at least to some extent.
- Like this:
mconcat : [a] ->{Monoid a} a
,whereMonoid a
is an ability. - Like this:
mconcat : Monoid a -> [a] -> a
,whereMonoid a
is a record type.
Both of these would require a set of supporting language enhancements, for example to supply theMonoid 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 callsabilities,which replace monads and monad transformers for writing effectful code.
- Unison uses a strict evaluation model, whereas Haskell's model is lazy.
- The
do
keyword in Unison starts a codeblock as a delayed computation, it does not match Haskell's "do notation" for any monads. - The
let
keyword in Unison starts a codeblock. It is not the start of a variable definition.
🌻 While there are similarities between the two languages, it is not an expectation that people know Haskell in order to program in Unison.
Does Unison have an FFI?
Unison does not currently support a Foreign Function Interface, for invoking code written in other languages.
Your programs can interact with the outside world via theIO
ability, and this includes interaction via network sockets - so you can interact with code written in other languages if that code can expose a network interface, for example as a web service. We'd like to improve on this position in the future.
The sketch design for how it will work is as follows.
- A foreign API, let's say one for working with the GPU, will be exposed through a top-level ability, let's say
GPU
. - The Unison runtime will let you run a program of type
'{GPU} ()
just as it lets you run a'{IO} ()
. - There will be an extensible binding mechanism for defining new such abilities and forwarding their operations to external APIs.
There's still plenty to work out, for example in
- designing the binding mechanism
- managing
ucm
linkage dependencies to external binaries - making it play nicely with distribution, so computations can jump between nodes, and make use of the varying FFI abilities on each one.
Note how using the ability mechanism gives a great story for testing. You can define a test handler, in pure Unison, to handle yourGPU
ability, and use it to add regular Unison tests for your GPU code, that can run anywhere regardless of whether the right GPU is installed.
Distributed computing
Is there a way to run Unison on my own cluster? How do I develop and test Unison distributed programs locally?
Currently we don't have an easy way for folks to run and manage a distributed Unison program on their own cloud computing resources. The Unison team is hard at work on theUnison Cloud Platformfor this purpose and support for locally simulating a distributed program is a fast follow-on. Sign up for beta access to the Cloudhere.Unison Cloud is our platform as a service product and we hope its success sustainably funds the open source development of the Unison language, tooling, and ecosystem as a whole.
You can still get start writing distributed Unison programs today by downloading thedistributed libraryfrom Unison Share and using the locala4.distributed.Remote.pure.run
handler. Our interface for distributed programming is independent from the hardware that runs it, so familiarizing yourself with the API and writing programs with it will make it easy to transition to the Cloud when it's ready.
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 and is in active development. Here are some resources to give you a preview.
- TheREADME for the distributed library contains examples and information.
- Paul'sScale By The Bay 2018 talk,from about 8 minutes in.
- The 2015blog poston distributed evaluation.
- The currentAPI sketch.
- Read our article,Spark-like distributed datasets in Unison
- A preview of whatmicroservices might look like in Unison
Codebase model
If definitions never change, how do you fix a bug or refactor an old definition?
Definitions never change, but the names we give them do. Suppose I have a term with hash#1krif5
,and I've given it the namefoo
.We can draw that as follows.
foo -> #1krif5
Then I decide my code forfoo
is bugged, so I present Unison with a new definition for it. Then Unison will work out the hash of thenewdefinition forfoo
,say#amrl61
,and change the namefoo
to point to it. We end up with the following.
#1krif5
foo -> #amrl61
So, no terms havechanged, but a new term has been added, and anamehas changed to point to a different term.
Propagation
So that's all good, but what about the functionbar
which calledfoo
?
Suppose we actually originally started with the following - one term referencing (calling out to) another, those terms being calledbar
andfoo
respectively.
foo -> #1krif5
↑
bar -> #doq7s1
Then our edit tofoo
actually left the codebase (for a moment) in the following situation, withbar
still referencing theoldversion offoo
.
#1krif5
↑
bar -> #doq7s1
foo -> #amrl61
However, Unison assumes that this is not where you want to end up, and that actually, you want topropagatethe updated definition offoo
to the set oftransitive dependentsof the old term#1krif5
.
As long as the oldfoo
and the newfoo
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 ofbar
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 ofbar
had typeInt
,the new one has typeNat
,andbar
requiresfoo
to be anInt
.Then thetodo
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 editingbar
(or rather, switching the namebar
to refer to some new definition) so that it copes withfoo
being aNat`. 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 bytodo`, 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''andbar
.And at no stage did we have to wade through a mass of compile errors: instead, thetodo
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 apatch
.Suppose you publishfoo
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.
See our document aboutresolving conflicts for more information.
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), or add a document literal to the term like{{wow this is unlikely!}}
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 the 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?
If you want topubliclyshare your code (or look at code that others have shared), check outUnison Share.
You use the Unison Codebase Manager'spush
command, to write it to a git repository - for example, one hosted on GitHub. Runhelp push
withinucm
for more information.
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 index0
.
This transformation makes the elements of the cycle independent of any names. Then we hash each element of this new structure individually. Let’s sayf
gets the hash#222
andg
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
wheren
is the position of that definition in the cycle. Hereg
gets#ccc.0
andf
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 usesIO
?
Inucm
,you can typerun myProgram`, where
myProgram''is some term of type `'{IO, Exception} ()`.
Check out this document about the ways to run Unison programs
Does Unison have IDE support? Editor support? Language Server Protocol (LSP) support?
LSP support is an area of active development! Since version M4a the UCM ships with an LSP integration.Here's how to set it up.
We have editor support for the Unison syntax in Vim, Atom, and VS Code - see our document aboutsetting up your editor.
No editors currently understand the Unison codebase format, or offer functionality like "find references", "extract function", etc. You can useucm
commands to get some of these abilities, for examplefind
.
Unison ships with alocal UIto browse your codebase. Libraries can be explored and published onUnison Share
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 the 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 the 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.
Contributing Unison code
Is there a library you could write in Unison? 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. There's a catalog of librarieson the front page of Unison Share- do add yours! Let us know what you're working on in the slack (channel #libraries). 😎
You could also try fixing some omissions from the base library - seeherefor the contributions that would be most welcome.
Contributing to the Unison language/compiler/toolchain
You can dip your toes by finding small ways to contribute a little piece:
- take a look at the issues labelledgood first issueandhelp 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!