DEV Community

Alex Spinov
Alex Spinov

Posted on

Unison Has a Free API: A Language Where Code Is Content-Addressed Like Git

Unison is a functional programming language where code is stored as a hash-addressed content database — not text files. This means no build errors from dependency conflicts, instant code sharing, and effortless distributed computing.

Why Unison Matters

Every programming language stores code as text in files. This causes: dependency hell, merge conflicts on renames, broken imports, and the inability to safely share code. Unison stores code by its hash — like Git stores content.

What you get for free:

  • Content-addressed code: no dependency conflicts ever
  • Rename anything without breaking anything
  • Built-in distributed computing (send functions to remote nodes)
  • Instant code sharing via Unison Share
  • Structural typing — if it fits, it works
  • Algebraic effects for clean side-effect handling
  • Built-in test framework

Quick Start

# Install
curl -fsSL https://www.unison-lang.org/install | sh

# Start the Unison codebase manager
ucm

# Create a new project
.> project.create myproject

# Edit code
myproject/main> edit myFunction
Enter fullscreen mode Exit fullscreen mode

The Basics

-- Functions
greet : Text -> Text
greet name = "Hello, " ++ name ++ "!"

-- Pattern matching
fibonacci : Nat -> Nat
fibonacci = cases
  0 -> 0
  1 -> 1
  n -> fibonacci (n - 1) + fibonacci (n - 2)

-- Lists and higher-order functions
doubleEvens : [Nat] -> [Nat]
doubleEvens list =
  list
    |> List.filter (n -> Nat.mod n 2 == 0)
    |> List.map (n -> n * 2)

-- Result types
parseAge : Text -> Either Text Nat
parseAge input =
  match Nat.fromText input with
    Some n | n > 0 && n < 150 -> Right n
    _ -> Left "Invalid age"
Enter fullscreen mode Exit fullscreen mode

Distributed Computing

-- Send a computation to a remote node
remoteCompute : Node -> Nat -> {Remote} Nat
remoteCompute node input =
  Remote.at node do
    -- This code runs on the remote node
    heavyComputation input

-- Fan out work across multiple nodes
parallelMap : [Node] -> (a -> b) -> [a] -> {Remote} [b]
parallelMap nodes f items =
  List.zip nodes items
    |> List.map (cases (node, item) ->
         Remote.at node do f item)
    |> Remote.collectAll
Enter fullscreen mode Exit fullscreen mode

Abilities (Algebraic Effects)

-- Define an ability (like an interface for side effects)
ability Store where
  get : Key -> Optional Value
  put : Key -> Value -> ()
  delete : Key -> ()

-- Use the ability in functions
cacheOrCompute : Key -> '{Store, Exception} Value
cacheOrCompute key = do
  match Store.get key with
    Some value -> value
    None ->
      let result = expensiveComputation key
      Store.put key result
      result

-- Provide different implementations
handleWithMap : Map Key Value -> Request {Store} a -> a
handleWithMap map = cases
  { Store.get key -> resume } ->
    handleWithMap map (resume (Map.get key map))
  { Store.put key value -> resume } ->
    handleWithMap (Map.insert key value map) (resume ())
  { result } -> result
Enter fullscreen mode Exit fullscreen mode

Content-Addressed Magic

-- In traditional languages:
-- 1. Rename function -> all imports break
-- 2. Update dependency -> version conflicts
-- 3. Move file -> everything breaks

-- In Unison:
-- 1. Rename function -> nothing breaks (code is stored by hash)
-- 2. Update dependency -> old version still works (different hash)
-- 3. There are no files -> nothing to move
Enter fullscreen mode Exit fullscreen mode

Testing

test> fibonacci.tests = do
  ensureEqual (fibonacci 0) 0
  ensureEqual (fibonacci 1) 1
  ensureEqual (fibonacci 10) 55
  ensureEqual (fibonacci 20) 6765

test> greet.tests = do
  ensureEqual (greet "World") "Hello, World!"
  ensureEqual (greet "") "Hello, !"
Enter fullscreen mode Exit fullscreen mode

Useful Links


Building innovative developer tools? Check out my developer tools on Apify for ready-made web scrapers, or email spinov001@gmail.com for custom solutions.

Top comments (0)