Skip to main content
This guide walks you through writing a real Sifr program from scratch, running it with sifr run, type-checking it with sifr check, and compiling it to a standalone native binary with sifr build. The example uses union types and Result-based error handling — two of Sifr’s most important features — so you leave with a clear picture of how the language works in practice.
1

Create your Sifr file

Create a new file called greet.sifr. This program looks up a user’s age from a dictionary (demonstrating safe indexing with int | None), parses a string into an integer (demonstrating Result and compile-enforced error handling), and narrows a union type with isinstance.
greet.sifr
A few things to notice before you run this:
  • users["charlie"] returns int | None. The compiler prevents you from using age as a plain int until you check the None branch.
  • parse_age declares Result[int, ParseError]. If you remove the except ParseError block in main, the compiler rejects the program.
  • isinstance(val, int) narrows the type of val inside each branch automatically — no cast required.
2

Type-check without compiling

Before running the program, use sifr check to catch any type errors. This is the fastest feedback loop during development because it skips code generation entirely.
If everything is correct, the command exits silently with a zero status code. If you have a type error — for example, you forgot to handle the ParseError branch — sifr check tells you exactly which line and why.
Run sifr check in your editor’s save hook or CI pipeline to catch mistakes before they become build failures.
3

Run the program

Use sifr run to compile and execute greet.sifr in a single command. You do not need to manage intermediate build artifacts.
Expected output:
user not found appears because "charlie" is not in the users dictionary and the missing-key path returns None. parsed age: 25 confirms that parse_age("25") returned Ok(25) and the compiler unwrapped it automatically at the try site.
4

Build a native binary

When you are ready to ship, use sifr build to compile greet.sifr into a standalone native binary. No runtime or interpreter is bundled — the output is a plain executable linked against the system.
Run the resulting binary directly:
The output is identical to sifr run, but the binary is self-contained and can be distributed to any compatible platform without installing Sifr.
5

Inspect the generated Rust

sifr emit prints generated Rust without producing a binary. The packaged compiler keeps both cold and warm invocations within the stable qualification timeout.
The output is valid Rust source that you can read, audit, or paste into the Rust playground for further experimentation.

What you just did

You wrote a Sifr program that exercises three core language features:
  • Safe indexing — dictionary access returns int | None, eliminating key-not-found crashes at the type level.
  • Result-based error handlingparse_age returns Result[int, ParseError], and the compiler enforces that every call site handles the error branch.
  • Automatic type narrowingisinstance checks inside if/else narrow the union type in each branch without explicit casts.
All three guarantees are enforced at compile time. If your program compiles, none of these categories of bug can occur at runtime.

Next steps

Language: Type System

From Python

CLI Overview