This document contains code snippets with a minimum of exposition. Links are provided throughout to more comprehensive docs sections. If you haven't downloaded the UCM,you might want to do that first. π
Hello World
Write your Unison code in any.u
suffixed"scratch" file.
The classichelloWorld
program performs console interactions via the IO ability.Read about how abilities model effects in Unison.
Execute the entry point to your program with therun
UCM command.
.> run helloWorld
Oradd
yourhelloWorld
program to the codebase and run it by packaging it up in a binary executable!
.> add
.> compile helloWorld helloFile
This will produce an executable file calledhelloFile.uc
in the same directory as the codebase. You can then start your program in your terminal.
$ ucm run.compiled helloFile.uc
Basic functions
The following introduces a functiondouble
with one parameter.Unison conventions for defining functions are detailed here
> double 4
The>
in a scratch file runs thedouble
function in awatch expression.
πThe Unison tour walks through watch expressions and other workflow features
Delayed computation syntax
The'
symbol is syntactic sugar for introducing athunkand the!
symbol is syntactic sugar for calling the thunk.
delayGreet : 'Text
!delayGreet⧨"Hi Joy"
List literals
Square brackets introduce aUnison list.
[0, 1, 2, 3] List.++ [4, 5]⧨[0, 1, 2, 3, 4, 5]
TheList.++
is ouroperatorfor list concatenation.
List transformations
Nat.range 0 10
|> List.map (x -> x Nat.* 100)
|> List.filter (const true)
|> List.foldLeft (Nat.+) 0⧨4500
The|>
operator is a "pipe" which passes the result of executing the expression on the left as an argument to function on right.
The parenthesizedx -> x Nat.* 100
argument toList.map
is an example oflambdas in Unison.
if/else and pattern matching
The expression below is written with bothif then and else syntaxand withpattern matching syntax
use Nat mod
isEven1 num =
use Nat ==
if mod num 2 == 0 then true else false
isEven2 = cases
n | mod n 2 === 0 -> true
_ -> false
Unison'spattern matching featuresinclude variable binding, pattern guards (separated by|
),and as-patterns (indicated with an@
).
match Some 12 with
None -> "none"
Some n| Nat.isEven n -> "n is a variable and | is a pattern guard"
opt@(Some n) -> "opt binds to the entire optional value"⧨"n is a variable and | is a pattern guard"
Type declarations
A unison data type with uniqueness determined by its name:
A recursive Tree data type with a single type parameter:
structural type glance.Tree a
structural type glance.Tree a
= Empty
| Node a (glance.Tree a) (glance.Tree a)
The structural keyword means that types defined with the same structure are identical.
More on data types and the difference between structural and unique.
Record types allow you to name the fields of your type.
unique type Pet = {
age : Nat,
species : Text,
foodPreferences : [Text]
}
Creating a record type generates a series of helper methods to access and update the fields of the data type.
.> add Pet
Exception handling
nonZero : Nat ->{Exception} Nat
nonZero = cases
n
| n Nat.== 0 ->
Exception.raise (Generic.failure "Zero was found" n)
| otherwise -> n
An exception is "raised" with theException
ability and "caught" with a handler.
Our error handling with abilities doc describes this pattern and more error types in detail.
Using abilities
Abilities are used for effect management in Unison.
getRandomElem : [a] ->{Random} a
getRandomElem : [a] ->{Random} a
getRandomElem list =
index = Random.natIn 0 (List.size list)
List.unsafeAt index list
splitmix 42 '(getRandomElem [1, 2, 3, 4, 5])⧨5
This plucks a random element from the list withRandom.natIn
,a function using theRandom ability.
splitmix
is an example of anability handler.
Distributed computations
Distributed computations can be expressedin the Unison language itselfthrough theRemote
ability.Read about the Remote ability and its features
This simple map/reduce code can operate over a distributed sequence, where the data may live in many different nodes in a cluster.This distributed computation use case has been fleshed out in an article.
Issuing an http request
Pull the library fromUnison Share.with thepull
command.
.> pull stew.public.projects.httpclient.latest lib.httpclient
exampleGet : '{IO, Exception} Response
The first part of this code uses data constructors from the http library to create a full uri out of an authority and path. The request is handled by passing it to the Http handler.
Basic file operations
glance.readFile : '{IO, Exception} ()
glance.readFile : '{IO, Exception} ()
glance.readFile _ =
filePath = FilePath "tmp.txt"
read : Handle ->{IO, Exception} Bytes
read fileHandle =
go acc =
use Bytes ++
use Nat <
bs = getBytes fileHandle 4096
if Bytes.size bs < 4096 then acc ++ bs else go (acc ++ bs)
go Bytes.empty
fileHandle : '{IO, Exception} Handle
fileHandle _ = open filePath Read
bracket
fileHandle
Handle.close
(file -> read file |> fromUtf8 |> printLine)
File operations are accomplished by setting theFileMode
and manipulating a fileHandle
.Seeking through the file can be done via thegetBytes
function, which we've opted to perform in arecursive function.
These and other file functions are located in thebase.io
namespace.
.> find file
Concurrency primitives
Concurrency primitives likeMVar
,TVar
,andSTM
are built into the base library.TVar
andSTM
make it easy to write lock-free concurrent mutable data structures. For instance, hereβs a simple lock-free queue implementation and a few helper functions:
The block introduced bySTM.atomically
below ensures that no one can access state of the queue until after the actions in the block have taken place.
queueExample : '{IO, Exception} ()
queueExample : '{IO, Exception} ()
queueExample _ =
runQueue : '{STM} Nat
runQueue _ =
queue = TQueue.fromList [1, 2, 3, 4, 5]
enqueue 6 queue
dequeue queue
dequeue queue
result = STM.atomically runQueue
printLine (Nat.toText result)