Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Taste the Rainbow

This is Prism for Python programmers. It assumes you know variables, functions, lists, and if, but it assumes no functional-programming experience. By the end you will be able to read and write a small multi-file Prism program, model data, transform collections, follow inferred types and effects, write a test, and understand the ideas that make Prism unusual.

The tutorial is a bridge, not a compressed language specification. Every new idea starts from a Python habit, replaces it with a Prism mental model, and ends with something you can run or deliberately break.

What changes when you leave Python?

The punctuation is the easy part. These are the larger shifts:

Python instinctPrism model
execute statements and returnevaluate expressions to values
reassign names freelybind immutable values with let
represent alternatives with classes or tagsdefine the exact alternatives with an algebraic data type
use match as convenient control flowlet exhaustive patterns prove every case is covered
discover IO, mutation, and exceptions from the bodyread observable effects from the function type
document “call once” or “does not allocate” in proseexpress usage and resource promises as coeffects
build nested copies by handfocus and update immutable data with optic paths
use iterators to avoid intermediate listsfuse stream producers, transformations, and consumers
identify code by filenames and source hashesidentify canonical Core definitions by content

Prism is strict: arguments are evaluated before a function runs, as in Python. It is also deliberately impure: useful programs print, read files, fail, and communicate. The difference is accountability. Prism infers those observations as named effects and lets handlers interpret them at explicit boundaries.

How to use this tutorial

The quickest route is the browser-based Playground. It needs no installation and is ideal for the single-file examples in the first five chapters.

For the project, module, test, and content-identity sections, use a local compiler. Prebuilt Prism supports Apple Silicon macOS and glibc Linux on x86-64 or AArch64. Native code generation needs LLVM 22:

# macOS
brew install llvm@22

# Debian or Ubuntu
curl -fsSL https://apt.llvm.org/llvm.sh | sudo bash -s 22

Install the compiler and confirm it is available:

curl --proto '=https' --tlsv1.2 -fsSL https://sdiehl.github.io/prism/install.sh | sh
prism --version

Homebrew users may instead run brew install sdiehl/prism/prism. The repository README covers Nix, containers, Linux packages, and building from source.

Code marked output is what the preceding program prints. A Try it prompt is small enough to do immediately. Making the change matters more than merely reading the answer. All ordinary Prism blocks in this book are checked by the compiler, and intentionally broken blocks are checked to ensure they really do fail.

Run your first program

Put this in the Playground or save it as rainbow.pr:

fn main() =
  let name = "Python programmer"
  println("Welcome, {name}. Taste the Rainbow!")
Welcome, Python programmer. Taste the Rainbow!

Run a local file through the interpreter:

prism check rainbow.pr
prism run rainbow.pr
prism fmt rainbow.pr

That three-command loop is the ordinary workflow:

  • check finds problems without executing the program.
  • run interprets it immediately.
  • fmt gives the source its canonical layout.

A program begins at main. A let binds a value without making a mutable variable. There is no ordinary return: the last expression is the value of the body. Indentation forms the body, comments start with --, and {name} inside a string interpolates an expression.

Most type annotations are optional. The compiler inferred that name is a String, println produces Unit, and main performs console IO. Hover those expressions in the rendered book to see the inferred facts.

Let the compiler teach you

Python type annotations are usually advice to a separate checker. A Prism annotation is part of the program and the compiler must prove the body agrees with it:

fn square(n : Int) : Int = n * n

fn main() = println(square("six"))

This program is supposed to fail: square requires an Int, but the call supplies a String. Prism diagnostics carry stable codes. When an unfamiliar one appears, ask for its explanation:

prism explain E1002

Use the code printed by your own diagnostic. E1002 is simply an example. The explanation includes the cause, a minimal reproducer, and a fix.

Try it: Change the greeting to accept an Int named count, interpolate it into the message, then deliberately pass a string. Read the error before repairing the call.

The route ahead

The chapters build on one another:

  1. Functions and Values replaces statements and reassignment with expressions, immutable data flow, and higher-order functions.
  2. Data and Patterns introduces records, algebraic data types, Option, and exhaustive matching.
  3. Purity and Effect Types makes observation visible in types and explains effect rows and row polymorphism.
  4. Handlers and Continuations shows how a handler controls the rest of a computation.
  5. Coeffects turns usage and resource promises into compile-time contracts.
  6. Lenses and Streams scales immutable updates and collection pipelines.
  7. Projects and Content Identity assembles a package, modules, tests, and Prism’s content-addressed view of code.
  8. The Prism Way collects the themes into practical working habits.

One honest warning: Prism is an active language project, not a production ecosystem. Explore it, steal ideas from it, and expect a few sharp experimental edges as it evolves.

Further reading: language goals, command-line interface, and diagnostics.