Navigation
Syntax Guide
Learn Logic
StudioCratesRoadmapContactNewsBenchmarks
ProfileGitHub
Interactive Guide

LOGOS Syntax Guide

Write English. Get Logic. Run Code. A comprehensive guide to programming in LOGOS, from basics to advanced features.

1.Introduction

After this section you can
  • decide which mode — Imperative or Logic — fits what you want to build
  • choose your reading path: front-to-back as a beginner, jump-around as a veteran

What is LOGOS?

LOGOS is a programming language where you write code in natural English. Instead of cryptic symbols and arcane syntax, you express your ideas in sentences that read like plain prose—and those sentences compile into efficient, executable programs.

LOGOS has two modes:

ModeWhat It DoesOutput
Imperative ModeWrite executable programsRust code (compiled to native binaries)
Logic ModeTranslate English to formal logicFirst-Order Logic notation

This guide focuses primarily on Imperative Mode—using LOGOS as a programming language. Part VI covers Logic Mode and verification for those interested in formal semantics.

The Vision

The name LOGOS comes from the Greek λόγος, meaning "word," "reason," and "principle." In LOGOS, these concepts unify:

  • Words become executable code
  • Reason becomes verifiable logic
  • Principles become formal proofs

When you write LOGOS, you're not writing comments that describe code—you're writing sentences that *are* the code.

How to Read This Guide

If you're new to programming:

  • Read each section in order
  • Try every example yourself
  • Don't skip ahead—each concept builds on the previous

If you're an experienced programmer:

  • Use the Table of Contents to jump to what interests you
  • The Quick Reference section provides rapid lookup
  • The Complete Examples show real-world patterns

Have questions or feedback? Join our Discord community.

Join Discord →

2.How LOGOS Reads English

A Python programmer reads `item 1 of scores` and expects the second element. A C programmer reads `items 1 through 2` and expects one element. Both are wrong — and the reasons will make the rest of LOGOS feel obvious.
After this section you can
  • predict what `item 1 of xs` and `items 1 through 2 of xs` return
  • translate any symbolic operator to its English twin and back
  • choose the equality spelling that keeps intent unambiguous
  • spot the three deliberate exceptions to programmer tradition

The phrasing you would guess

LOGOS is a deterministic grammar, not an AI guessing your intent. Every sentence the

parser accepts has exactly one meaning, and the design rule is least surprise: the

phrasing you would naturally write is the phrasing that works. Where several natural

phrasings exist, they are synonyms for the same operation — 2 + 3 and 2 plus 3

compile to the same addition, and neither is more "real" than the other.

English first, symbols beside

Every operator has an English form and a symbolic twin: plus and +, is at least

and >=, equals and ==. Use whichever reads better in context; they are the same

program. The full table lives in Operators and Expressions.

The deliberate exceptions

A few places where LOGOS chooses the human reading over programmer tradition — learn

these and the rest of the language follows your intuition:

  • Counting starts at 1. item 1 of items is the first element, and slices are

inclusive on both ends: items 1 through 2 is two elements.

  • Equality has a canonical spelling: equals (or ==). Bare is also

compares — x is 5 runs and is true — but is wears many hats in LOGOS

(descriptions, definitions, predicates like is even), so this guide writes

equals wherever equality is meant: the dedicated spelling keeps intent

unambiguous.

  • Fields read as possessives. A struct's field is alice's age — the apostrophe

is grammar, not decoration.

The examples below preview indexing, comparisons, and If — each is taught fully in

Part II; here they carry the mental model only.

*Try it: in the first example below, change plus to times — then make the symbolic

line disagree and watch the last line turn false.*

You might expect
In Python or C, `scores[1]` is the SECOND element — counting starts at zero, and slices exclude their end.
LOGOS instead
LOGOS counts the way humans do: `item 1` is the first element, and slices include both ends — `items 1 through 2` is exactly two items. One convention, applied everywhere, so there are no off-by-one gymnastics to rehearse.
See it run ↓
English and Symbols Are TwinsImperative
Expected output
5 5 true
The Phrasing You Would GuessImperative
Expected output
First score is an A Same check, symbol style
Counting Starts at 1 (Inclusive)Imperative
What will this print?
Now you tryFill in the blankFill the hole so the program prints `true` — use the canonical equality spelling.
practices: equals-not-is rule
Now you tryPracticePrint the third letter twice: once with `item N of`, once with brackets.
practices: 1-based inclusive indexing exception
Check yourself
What does `item 1 of xs` return — and is `items 2 through 3 of xs` one item or two?
Which spelling does the guide prefer for equality, and why?
Name the two spellings of the ≥ comparison — which one is more "real"?

Have questions or feedback? Join our Discord community.

Join Discord →

3.Getting Started

After this section you can
  • run your first LOGOS program in the playground
  • write a `## Main` block that prints text
  • name the one-line installer for your platform

Installing largo

Everything on this site runs in your browser — but for real projects you want largo,

the LOGOS build tool, on your machine. One line, no toolchain required:

curl -fsSL https://logicaffeine.com/install.sh | sh

On Windows:

powershell -ExecutionPolicy Bypass -c "irm https://logicaffeine.com/install.ps1 | iex"

The installer fetches the prebuilt binary for your platform (Linux/macOS x64 + arm64,

Windows x64), verifies its SHA-256 checksum, and installs it to ~/.local/bin — no sudo,

no shell-config edits. Add --full (sh -s -- --full) for the build with Z3 verification

bundled. Then:

largo new hello && cd hello && largo run

Hello World

Every programming journey begins with Hello World. In LOGOS:

The Program Structure example below is a deliberate preview — a struct and a function

before either is taught. Every piece gets its full section in Part II; here it shows

the shape of a real program.

Hello WorldImperative
Expected output
Hello, World!
Program StructureImperative
Expected output
Hello, World!
Your First Real ProgramImperative
Expected output
Name: Alice Age: 25
Now you tryPracticeChange the greeting: make the program print exactly `Hello, LOGOS!`.
practices: hello world

Have questions or feedback? Join our Discord community.

Join Discord →

4.Tutorial: Teach the Computer to Guess

You think of a number between 1 and 100; the program finds it in at most 7 guesses — every time, guaranteed. By stage five you will have written the whole thing: the loop that narrows the range, the function that plays any secret, and the struct that reports the win.
After this section you can
  • build a complete program in five runnable stages
  • trace how halving the range guarantees at most 7 guesses
  • extend a working program without breaking its earlier stages

One small program, built in five stages — each stage runs, and each adds exactly one idea.

The game: you think of a number between 1 and 100; the *program* finds it by binary

search, and it never needs more than 7 tries.

Stage 1 sets the scene with variables and Show. Stage 2 makes one guess: the

midpoint, and an If to say which way it missed. Stage 3 turns that into the whole

game with While and Break, halving the range each round. Stage 4 extracts a

function so any secret can be found. Stage 5 packages the result in a struct and

announces it with interpolation.

The secret is written into the program so every stage runs right here — change it at any

stage and watch the search adapt. Every construct the game uses — While, Break,

functions, structs, interpolation — gets its full treatment in Part II; the tutorial

shows them working together first.

*Try it: in Stage 4, try secrets 1 and 100 — the edges. Does it ever need more than 7?*

Look back: the halving move generalizes to any sorted search — dictionaries,
version bisecting, guess-the-bug. What breaks if the range starts at 0? Nothing —
but if the secret is OUTSIDE the range, the loop never ends: bounds are the
contract. Try counting comparisons instead of tries.
You might expect
The midpoint of 1 and 100 is 50.5, so the program's first guess should print 50.5.
LOGOS instead
The guess uses `//` — floor division. `(1 + 100) // 2` is exactly 50: integer arithmetic stays in the integers unless you ask otherwise, which is precisely what a guessing game needs.
See it run ↓
Code Examples5 examples
Stage 1: The SceneImperative
Expected output
Think of a number. Mine is between 1 and 100.
Stage 2: One GuessImperative
What will this print?
Stage 3: The Whole GameImperative
Expected output
Got 37 in 3 tries.
Stage 4: Extract a FunctionImperative
Expected output
37 found in 3 tries. 99 found in 6 tries.
Stage 5: A Struct FinaleImperative
Expected output
Found 37 in 3 tries — never more than 7.
Now you tryFill in the blankStage 3 with two holes: count each try, and move the bottom of the range after a low guess.
practices: staged build-one-program tutorial
Now you tryPracticeWiden the search to 1 through 1000 and report the hunt for 999 in the stage-4 style. Predict first: can it still finish in 10 tries?
practices: staged build-one-program tutorial
Check yourself
Why can the program always find the secret in at most 7 tries?
What does `(low + high) // 2` print when low is 1 and high is 100 — and why not 50.5?
Which statement ends the `While true:` loop when the guess is right?

Have questions or feedback? Join our Discord community.

Join Discord →

5.Checkpoint: First Steps

After this section you can
  • combine the twin spellings, midpoint arithmetic, and printing in one program
  • diagnose which Part I section to reread when a challenge stumps you

Part I is done — prove it to yourself. The three challenges below blend what you

met across the part: the program shape, the twin spellings, and the tutorial's

midpoint arithmetic. Then the recall cards collect on the part's promises.

Nothing here is new. If a challenge stumps you, the section to reread is named

in its hints — that is the point of a checkpoint.

Now you tryChallengePrint exactly `Hello, LOGOS!`, then print whether `2 plus 3` equals `2 + 3`.
practices: hello world · English/symbolic twins
Now you tryChallengeCompute the game's first guess for the range 1–100, print it, then print whether it is at least 50 — English spelling.
practices: staged build-one-program tutorial · English/symbolic twins
Now you tryChallengeThe secret is 88 and the range is 1–175. Print the first midpoint guess, then print whether it already equals the secret.
practices: staged build-one-program tutorial · equals-not-is rule
Check yourself
What does `## Main` mark?
What are the English twins of `>=` and of `==`?
`items 2 through 4 of xs` — how many items?
Why does the guessing game never need more than 7 tries for 1–100?
Which statement creates a variable, and which changes one?
What happens if you run `Show total.` without a `Let total` line first?

Have questions or feedback? Join our Discord community.

Join Discord →

Part II: The Language

6.Variables and Types

A program is mostly nouns: the player's score, the bill's total, today's date. Before anything can happen those nouns need names and values — and three of the types below catch bugs that float straight through every other language.
After this section you can
  • predict which numeric type keeps 0.1 + 0.2 exact
  • write `Let` and `Set` bindings, with type annotations where they add meaning
  • choose Money, Decimal, or Rational when the arithmetic must not drift
  • name what `Let mutable` signals to the reader

Variables are containers that hold values. In LOGOS, you create and modify variables using natural English sentences.

Creating Variables

Use Let to create a new variable. The word be assigns a value to the variable.

Changing Values

Use Set to change an existing variable. The difference between Let and Set:

  • Let creates a *new* variable
  • Set modifies an *existing* variable

Let mutable x be 0. flags a binding you intend to reassign — loop counters, running

totals. The checker does not currently reject Set on a plain Let, so the mutable

is for your reader: it marks the moving parts.

Primitive Types

TypeDescriptionExamples
IntWhole numbers5, -10, 0, 1000000
BoolTrue or falsetrue, false
TextStrings of characters"Hello", "LOGOS", ""
Real / FloatDecimal numbers3.14, -0.5, 98.6
CharSingle charactersee examples below
Byte8-bit unsigned (0-255)42: Byte, 255: Byte

Decimal literals have type Real, and Float is another name for exactly the same type —

either annotation accepts them.

Characters (Char)

Characters represent single Unicode characters, wrapped in backticks. See the example below.

Bytes (Byte)

Bytes are 8-bit unsigned integers (0-255), useful for binary data and low-level operations.

Type Annotations

Usually, LOGOS infers the type from the value you assign. But you can be explicit with : Type.

The exact numeric tower

---

Nat — counts by intent

Annotate : Nat for values that are counts: Let count: Nat be 5. documents that the

number is never meaningfully negative.

Rational — exact fractions

A Rational does arithmetic exactly — no rounding, ever. Give a value the annotation and

every division stays a true fraction: half of 7 plus a third of 7 is exactly 35/6, not

5.8333….

BigInt — integers that never overflow

When Int arithmetic exceeds 64 bits, LOGOS promotes the result automatically instead of

wrapping or crashing. The example below adds 1 to the largest 64-bit integer — the sum no

64-bit integer can hold — and prints it exactly.

Decimal — money-grade exactness

Binary floats cannot represent one tenth: 0.1 + 0.2 famously prints

0.30000000000000004. decimal("0.1") is exact base-10 arithmetic — the same sum is

exactly 0.3. When the number is money, use decimal (or the Money type).

Complex

complex(re, im) builds an exact complex number: complex(0, 1) is the imaginary unit,

and squaring it gives -1.

The math toolbox

sqrt(x), floor(x), ceil(x), round(x), pow(a, b), abs(x), min(a, b),

max(a, b) — all built in, no import needed.

*Try it: in the Decimal example, add a third decimal("0.1") to the sum — then try the

same with plain floats and compare.*

Time, money, and measures

---

Time

  • 500ms, 2s, 5min — a Duration (SI time).
  • 2026-05-20 — a Date.
  • 4pm, 9:30am, noon — a Time of day.
  • 2 weeks, 3 months — a calendar Span.

Calendar extractors read components: year_of(d), month_of(d), day_of(d),

weekday_of(d).

Money

$19.99, €5, £10 — exact amounts in a real currency, quantized to the currency's

minor unit. No float cents, ever: $19.99 + $0.01 is exactly 20.00 USD, and splitting

a bill rounds to real cents.

Quantities

quantity(3, "meter") is a dimensioned physical value. Adding a length to a time is not

a rounding error — it is a caught, impossible operation (run the mismatch example).

Identifiers

uuid("…") parses an RFC 9562 UUID; uuid_version(id) reads its version nibble.

Pausing

Sleep 2ms. (or Sleep 500. — milliseconds) pauses the program.

*Try it: change the bill split to four ways — where does the remainder cent go?*

The complete type map

Every type in the language, in one place. The ones marked with a section name get their

deep dive there; everything else is taught right here.

FamilyTypes
PrimitivesInt, Nat, Real / Float, Bool, Text, Char, Byte, Nothing (spelled Unit in stdlib signatures)
Exact numbersRational, BigInt (automatic on overflow), Decimal, Complex, modular integers via modular(v, m)
TimeDuration, Date, Time, Span, Moment
Measures & identityMoney, Quantity, Uuid
Machine wordsWord8, Word16, Word32, Word64 — wrapping ring arithmetic (see Bits, Bytes, and Machine Words)
CollectionsSeq of T, Map of K to V, Set of T, tuples (see Collections)
Maybe & failureOption of T (some x / none), Result of T and E (see Error Handling and Optionals)
Your ownstructs and enums (see User-Defined Types), generics (see Generics)
ConcurrencyPipe of T (see Concurrency)
DistributedConvergentCount, Tally, LastWriteWins of T, Divergent T, SharedSet of T, SharedSequence of T, CollaborativeSequence of T, SharedMap from K to V (see Distributed Types)
You might expect
0.1 + 0.2 is 0.3 — floats hold decimals, that's their whole point.
LOGOS instead
Binary floats cannot represent one tenth, so 0.1 + 0.2 prints 0.30000000000000004. When the number is value-critical, `decimal("0.1")` does exact base-10 arithmetic — the same sum is exactly 0.3.
See it run ↓
You might expect
Splitting $20.00 three ways gives $6.666666… — someone rounds it later and hopes.
LOGOS instead
Money is quantized to the currency's minor unit: the split rounds to real cents at the operation itself, so `bill / 3` is exactly 6.67 USD. No float cents exist anywhere in the pipeline.
See it run ↓
You might expect
Add 1 to the largest 64-bit integer and you get a crash, a wrap to negative, or undefined behavior — pick your language.
LOGOS instead
LOGOS promotes the result to BigInt automatically: the arithmetic stays mathematically correct and the program keeps running. Fixed-width wrapping still exists — as an explicit choice, in machine words.
See it run ↓
You might expect
Adding meters to seconds compiles fine and produces garbage at runtime — units live in comments.
LOGOS instead
Dimensions are part of the value: a length plus a time is refused with a named error, not a number. The mistake is impossible, not merely discouraged.
See it run ↓
Code Examples19 examples
Creating VariablesImperative
Expected output
5 Bob
Changing VariablesImperative
Expected output
5 10 11
Byte TypeImperative
Expected output
255 0 128
Nat: Counts by IntentImperative
Expected output
5
Rational: Exact FractionsImperative
Expected output
35/6
BigInt: Past 64 Bits Without OverflowImperative
What will this print?
Decimal: 0.1 + 0.2, AnsweredImperative
What will this print?
Complex: i SquaredImperative
Expected output
-1
Float and Real Are One TypeImperative
Expected output
true
The Math ToolboxImperative
Expected output
4 3 4 4 1024
DurationsImperative
Expected output
500ms
Dates and Calendar ExtractorsImperative
Expected output
2026-05-20 2026
Times of Day and Calendar SpansImperative
Expected output
16:00:00 14 days
Money: No Float CentsImperative
Expected output
20.00 USD
Splitting a BillImperative
What will this print?
Dimensioned QuantitiesImperative
Expected output
3 m
Adding Meters to Seconds (Fails on Purpose)Imperative
What error do you expect?
UUIDsImperative
Expected output
4
SleepImperative
Expected output
tick tock
Now you tryFill in the blankFill the hole so the score changes to 25.
practices: Set mutation
Now you tryPracticeAdd `decimal("19.99")` and `decimal("0.01")` and print the sum — watch it come out exact.
practices: Decimal exactness
Check yourself
What is the difference between `Let` and `Set`?
Why does `$19.99 + $0.01` never produce float dust?
What happens when Int arithmetic exceeds 64 bits?

Have questions or feedback? Join our Discord community.

Join Discord →

7.Text

Build the line `Ada — level 3` out of a name and a number. Gluing strings with `+` works, and buries the sentence's shape under punctuation. Interpolation keeps the sentence readable — and it formats π to two decimals for free.
After this section you can
  • write interpolated Text with format specs
  • distinguish `+` concatenation from `followed by`
  • spot which brace expressions evaluate and which need computing first

Strings in LOGOS are Text. Build them by concatenation or — usually better — by

interpolation: write the variable inside braces and it renders in place.

Interpolation

"Hello, {name}!" inserts name's value. A format spec after a colon controls the

rendering: "{pi:.2}" rounds to two decimal places. And "{v=}" is the print-debugging

form — it renders as v=42, the name and the value together.

Braces evaluate names and arithmetic: "{a + b}" renders the sum. English operator

phrases are the exception — "{length of tasks}" does not parse inside braces. Compute

those first (Let n be length of tasks.), then interpolate the name: "({n})".

Joining sequences

+ concatenates Text. Sequences join with followed by: a followed by b is one new

sequence with b's elements after a's.

Characters

A Char is a single Unicode character in backticks: a , \n , \t .

*Try it: in the format-spec example, change .2 to .4 — then remove the spec entirely.*

You might expect
Braces evaluate anything — f-strings taught me `{length of tasks}` should print the count.
LOGOS instead
Braces evaluate names and arithmetic (`{a + b}` renders 9), but English operator phrases don't parse inside them. Compute first, then interpolate the name — the error names the first word it couldn't resolve.
See it run ↓
Code Examples7 examples
InterpolationImperative
Expected output
Hello, Ada!
Format SpecsImperative
Expected output
3.14
Debug InterpolationImperative
Expected output
v=42
Braces Don't Parse `of`-Phrases (Fails on Purpose)Imperative
What error do you expect?
followed by: Joining SequencesImperative
Expected output
[1, 2, 3, 4]
Text ConcatenationImperative
Expected output
Hello, World!
Character LiteralsImperative
Expected output
a Char type uses backticks
Now you tryFill in the blankFill the hole so π prints with three decimal places.
practices: format specs
Now you tryPracticeLet name be "Ada" and level be 3; print exactly `Ada — level 3` with one interpolated Show.
practices: string interpolation
Check yourself
Which of these render inside braces: a variable name, `a + b`, `length of xs`?
`+` joins Text. What joins two sequences?
How do you write a Char literal, and what wraps it?

Have questions or feedback? Join our Discord community.

Join Discord →

8.Operators and Expressions

You know `+` and `<`. Now try: is 7 between 1 and 10? Is 15 divisible by 5? Does 0.1 + 0.2 equal 0.3 — and should it? LOGOS spells these questions the way you would ask them out loud.
After this section you can
  • translate between symbolic and English operator spellings
  • predict the result of Int division before running it
  • use chained comparisons and the number predicates

Operators let you combine values into expressions. LOGOS supports both symbolic operators (like +) and English words (like plus).

Arithmetic

OperationSymbolEnglish
Addition+plus
Subtraction-minus
Multiplication*times
Division/divided by
Modulo%modulo

Comparisons

OperationSymbolEnglish
Less than<is less than
Greater than>is greater than
Less or equal<=is at most
Greater or equal>=is at least
Equal==equals
Not equal!=is not

Logical Operators

OperationKeywordMeaning
ANDandBoth must be true
ORorAt least one must be true
NOTnotInverts true/false
You might expect
10 / 3 is 3.333… — division produces fractions (Python 3, JavaScript).
LOGOS instead
Int division stays in the integers: 10 / 3 is 3, and `%` hands you the remainder 1. Ask for fractions explicitly — Rational when it must be exact, floats when it must be fast.
See it run ↓
You might expect
`1 <= x <= 10` is a bug: it compares a Bool to 10 (C), or needs `&&` (JavaScript).
LOGOS instead
Chained comparisons mean the mathematics: both legs must hold, exactly as written on a blackboard. `1 <= x <= 10` is the range test you meant.
See it run ↓
Code Examples8 examples
Arithmetic OperationsImperative
Expected output
Sum: 13 Difference: 7 Product: 30 Quotient: 3 Remainder: 1
Int Division Stays WholeImperative
What will this print?
ComparisonsImperative
Expected output
true false true true true
Logical OperatorsImperative
Expected output
false true false
Chained ComparisonsImperative
What will this print?
is even / is odd / is divisible by / is betweenImperative
Expected output
true true true true
Floor Division and ExponentiationImperative
Expected output
3 -4 1024
is approximately (float-tolerant equality)Imperative
Expected output
true
Now you tryFill in the blankFill the hole so the range test reads like English and prints `true`.
practices: number predicates
Now you tryPracticePrint two lines: whether 12 is even, then whether 12 is divisible by 5.
practices: number predicates
Check yourself
What do `10 / 3` and `10 % 3` print?
How does `1 <= x <= 10` differ from the same line in C?
When does `is approximately` beat `equals`?

Have questions or feedback? Join our Discord community.

Join Discord →

9.Control Flow

Thirty exam scores need letter grades, and score seventeen needs a different answer than score three. One `Show` per score can't do it — the program has to decide, and it has to repeat.
After this section you can
  • write If/Otherwise branches and While loops
  • choose between counted Repeat and for-each iteration
  • predict how many times `from 1 to 5` runs

Control flow determines which code runs and in what order. LOGOS provides conditionals and loops using natural English syntax.

Conditionals

Use If to execute code only when a condition is true. The colon (:) after the condition opens an indented block.

If/Otherwise

Use Otherwise to handle the false case.

While Loops

Use While to repeat code as long as a condition is true.

For-Each Loops

Use Repeat for to iterate over collections.

You might expect
A counted loop `from 1 to 5` runs four times — ranges exclude their end (Python's range, Rust's ..).
LOGOS instead
`Repeat for i from 1 to 5` runs five times: 1 through 5, both ends included — the same convention as LOGOS slices. One inclusive rule everywhere beats two exclusive ones somewhere.
See it run ↓
Code Examples6 examples
If/OtherwiseImperative
Expected output
It's comfortable.
While LoopImperative
Expected output
1 2 3 4 5
For-Each LoopImperative
Expected output
1 2 3 4 5
Grading ExampleImperative
Expected output
Grade: B
Counted Repeat (1-based, inclusive)Imperative
What will this print?
BreakImperative
Expected output
3
Now you tryFill in the blankFill the hole so the false branch fires and prints the lower grade.
practices: If/Otherwise
Now you tryPracticeCount down from 5 to 1 with a While loop, then print `Liftoff!`.
practices: While loop
Check yourself
While or Repeat for — which for 'until a condition changes', which for 'once per element'?
How many times does `Repeat for i from 1 to 5` run?
What does `Break.` do inside `While true:`?

Have questions or feedback? Join our Discord community.

Join Discord →

10.Functions

The midpoint formula appears three times in the finished guessing game. Change it in one place and the other two copies are already wrong. Code you can name once and call anywhere is code that cannot drift apart.
After this section you can
  • write functions with typed parameters and return values
  • trace a recursive call down to its base case
  • name the two cases every recursive function needs

Functions are reusable blocks of code. In LOGOS, you define functions using natural English headers that describe what the function does.

Defining Functions

A function definition starts with ## To followed by the function name.

Parameters

Functions can accept parameters—values passed in when the function is called. Separate

them with and in the definition (commas also parse; the guide writes the English form).

Calls use commas: add(3, 5).

Return Values

Use -> Type to specify what the function returns.

Recursion

Functions can call themselves. This is called recursion. Every recursive function needs:

  • A base case — when to stop recursing
  • A recursive case — calling itself with a "smaller" problem
Simple FunctionImperative
Expected output
Hello, Alice! Hello, Bob!
Function with ReturnImperative
Expected output
8
Recursive FactorialImperative
Expected output
120
Now you tryFill in the blankFill the hole in the signature so `double` type-checks and prints 42.
practices: function definition
Now you tryPracticeWrite `max3`, the largest of three Ints (the built-in `max` takes two), and show max3(3, 9, 5).
practices: function definition · Return
Check yourself
What two cases does every recursive function need?
How are parameters separated in a definition — and in a call?
What does `-> Int` declare in a function header?

Have questions or feedback? Join our Discord community.

Join Discord →

11.Closures and Function Values

A sort needs the rule 'compare by age'; a filter needs 'keep the positives'. Those rules are tiny functions — and they work best passed around like any other value, written right where they're needed.
After this section you can
  • write expression and block closures
  • use function-typed parameters to accept behavior as an argument
  • predict what a closure captures when its variable later changes

Functions are values. A closure is a function written inline — bind it to a name, pass it

to another function, return it.

Expression closures

(x: Int) -> x + 1 is a complete function: one parameter, one expression, the result is

the return value.

Block closures

When the body needs statements, write ->: and indent — the block form.

Functions that take functions

A parameter typed fn(Int) -> Int accepts any matching closure. This is how map/filter

pipelines and callbacks are built.

Mental model: A closure is a recipe card that also photographs the pantry:
(x: Int) -> x + n copies the value n holds at the moment the card is written.
Reassign n later and the card still cooks from the photograph, not the shelf.
The photograph is not the whole kitchen, though — parameters still arrive fresh
on every call.

*Try it: in the last example, swap the doubling closure for (n: Int) -> n * n.*

An Expression ClosureImperative
Expected output
42
A Block ClosureImperative
Expected output
positive
Passing a Closure to a FunctionImperative
Expected output
42
Now you tryFill in the blankFill the hole to finish the doubling closure.
practices: expression closures
Now you tryPracticeWrite `twice` that applies a function two times — twice(f, x) = f(f(x)) — and show twice((n: Int) -> n + 3, 10).
practices: function-typed parameters
Check yourself
A closure mentions `n`, and `n` is Set to a new value afterwards. Which value does the closure use?
When do you need `->:` instead of `->`?
What is the type of a parameter that accepts Int-to-Int functions?

Have questions or feedback? Join our Discord community.

Join Discord →

12.Collections

One player has one score — `Let` handles that. A leaderboard has a hundred scores, a shop keeps prices by item name, and a lottery draw allows no duplicates. Different shapes of 'many' want different containers.
After this section you can
  • choose the right container among Seq, Map, Set, and Tuple
  • predict what aliasing does to a list before you run it
  • write map updates and set membership checks

Collections hold multiple values. LOGOS provides four main collection types:

CollectionDescriptionIndex Type
Seq of TOrdered listInt (1-based)
Map of K to VKey-value pairsAny key type
Set of TUnique elementsN/A (membership)
TupleFixed-size, mixed typesInt (1-based)

Creating Lists

Create a list with square brackets, or create an empty list with a type.

Accessing Elements

LOGOS uses 1-based indexing. The first element is at position 1, not 0. Why? Because that's how humans count.

Modifying Collections

  • Push to add an element to the end
  • Pop to remove and get the last element
  • copy of to create a deep copy

Slicing

Extract a portion of a list with through. Slicing is inclusive on both ends.

Maps (Dictionaries)

Maps store key-value pairs. Unlike lists which use integer indexing, maps use keys of any type.

Create a map:

Let prices be a new Map of Text to Int.

Access a value by key:

Let cost be prices["iron"].

Set a value by key:

Set prices["iron"] to 100.

Maps are useful for lookups, caches, and associating data without needing a struct.

Bracket Syntax

Both lists and maps support bracket indexing as an alternative to item X of:

English StyleBracket Style
item 1 of itemsitems[1]
item "iron" of pricesprices["iron"]
Set item "key" of map to val.Set map["key"] to val.

Both compile to the same code—use whichever reads better in context.

Sets

Sets store unique elements with no duplicates. Unlike lists, sets have no order and no index.

Create a set:

Let numbers be a new Set of Int.

Add elements:

Add 5 to numbers.

Remove elements:

Remove 5 from numbers.

Check membership:

If numbers contains 5: or If 5 in numbers:

Set operations:

  • a union b — elements in either set
  • a intersection b — elements in both sets

Tuples

Tuples are fixed-size collections that can hold values of different types. Unlike lists, tuples can mix integers, text, and other types in a single collection.

Create a tuple:

Let point be (10, 20).

Let record be ("Alice", 25, true).

Access elements (1-indexed):

Let x be point[1]. or Let x be item 1 of point.

Get length:

Let size be length of record.

Tuples are useful for returning multiple values from functions or grouping related but differently-typed data without defining a struct.

You might expect
`Let b be a` gives me my own copy of the list — changing b leaves a alone.
LOGOS instead
A plain binding aliases: `a` and `b` are two names for one list until you ask for `copy of`. Predict what `a` looks like after pushing to `b` — then run the evidence.
See it run ↓
You might expect
Reading a missing key returns null, None, or a default — every map I know is forgiving.
LOGOS instead
A missing key is an error that names the key. If absence is meaningful in your domain, say so with Option — a map that invents values hides bugs instead of reporting them.
See it run ↓
Code Examples22 examples
Creating ListsImperative
Expected output
[1, 2, 3, 4, 5] [Alice, Bob, Charlie]
Accessing Elements (1-indexed)Imperative
Expected output
apple banana cherry
Push and PopImperative
Expected output
[1, 2, 3, 4, 5] 5 [1, 2, 3, 4]
Slicing (inclusive, 1-based)Imperative
Expected output
[banana, cherry]
Iterating and AccumulatingImperative
Expected output
Total: 150
Creating MapsImperative
Expected output
Iron: 10 Copper: 25 Gold: 100
Map AccessImperative
Expected output
Wood: 50
A Missing Key Is an Error (Fails on Purpose)Imperative
What error do you expect?
Map UpdateImperative
Expected output
Initial: 100 Updated: 150
Bracket SyntaxImperative
Expected output
10 5 99
Creating SetsImperative
Expected output
{1, 2, 3}
Set MembershipImperative
Expected output
3 is prime 4 is not prime
Adding and RemovingImperative
Expected output
{red, green, blue} {red, blue}
Set OperationsImperative
Expected output
Intersection: {2, 3} Union: {1, 2, 3, 4}
Creating TuplesImperative
Expected output
(10, 20) (Alice, 25, true)
Accessing Tuple ElementsImperative
Expected output
answer 42
Mixed-Type TuplesImperative
Expected output
Bob 30 5.9 3
Typed Empty ListsImperative
Expected output
[7]
Capacity HintsImperative
Expected output
[1]
Index AssignmentImperative
Expected output
[10, 99, 30]
Destructuring Map IterationImperative
Expected output
Ada is 36
copy of vs AliasingImperative
What will this print?
Now you tryFill in the blankFill the hole so the counter increments to 2.
practices: maps
Now you tryPracticeAdd 3, 1, 3, 2, 1 to a Set of Int and print how many distinct numbers it holds.
practices: sets
Check yourself
Scores by player name, unique winners, ordered history — which containers?
When do two names point at one list, and how do you break the link?
What does reading a missing map key do?

Have questions or feedback? Join our Discord community.

Join Discord →

13.User-Defined Types

A point is an x AND a y; a direction is north OR south OR east OR west. Every domain has its own ANDs and ORs — declare them, and LOGOS refuses every value that doesn't fit.
After this section you can
  • write struct definitions with `has:` and enum definitions with `is either:`
  • read and write possessive field access fluently
  • write exhaustive Inspect/When matches

Beyond primitive types and collections, LOGOS lets you define your own types to model your problem domain.

Structs

A struct (structure) groups related values together. Define one in a ## Definition block using A [TypeName] has: syntax.

Creating Instances

Use a new [Type] with [fields] to create instances.

Accessing Fields

Use 's (possessive) to access fields.

Enums

An enum (enumeration) defines a type that can be one of several variants using A [TypeName] is either: syntax.

Pattern Matching

Use Inspect to handle different enum variants with When clauses.

Code Examples4 examples
Basic StructImperative
Expected output
10 20
Person StructImperative
Expected output
Alice 25
Simple EnumImperative
Expected output
North
Pattern Matching (Inspect/When)Imperative
Expected output
Heading east
Now you tryFill in the blankFill the hole so the Green arm fires.
practices: Inspect/When pattern matching
Now you tryPracticeDefine a Pet struct with a name (Text) and an age (Int); create one named "Rex" aged 4 and print `Rex is 4`.
practices: struct definition · possessive field access
Check yourself
Struct or enum: a point with x and y? a direction that is north or south?
How do you read the age field of a struct bound to `alice`?
What happens when an Inspect misses a variant and has no Otherwise?

Have questions or feedback? Join our Discord community.

Join Discord →

14.Generics

A Box that holds an Int and a Box that holds Text are the same idea — writing the type twice would be writing the same bug twice. Write it once, with a blank where the type goes.
After this section you can
  • write a generic type with one and with two parameters
  • explain what the [T] label buys at check time

Generics let you write types and functions that work with any type, not only specific ones.

Generic Types

Define a generic type with [T] in the type name. The [T] is a placeholder that gets replaced with a real type when you use it.

Multiple Type Parameters

You can have multiple type parameters like [A] and [B].

Generic Collections

Collections are generic types. Seq of Int is a sequence of integers.

Nested Generics

You can nest generic types like Seq of (Seq of Int) for a matrix.

Mental model: Box of [T] is a labeled shipping container: [T] is the blank
on the label. Stamp it — Box of Int — and the checker refuses mismatched cargo at
the dock. The label exists for the checker alone: by the time the program runs,
there is no label left, only the value that passed inspection.
Generic BoxImperative
Expected output
42 Hello
Generic PairImperative
Expected output
1 one
Now you tryFill in the blankFill the hole with the type that matches the contents.
practices: generic types
Now you tryPracticeMake a Pair of Text and Int with first "answer" and second 42, and print both fields.
practices: generic types
Check yourself
What does `[T]` declare in a type definition?
When is `Box of Int` vs `Box of Text` enforced?
Can generic types nest?

Have questions or feedback? Join our Discord community.

Join Discord →

15.Error Handling

Divide by zero, an age of -5, a missing file: real inputs fail. The question is whether a failure becomes a crash, a silent wrong answer, or a value your program can Inspect and handle like any other.
After this section you can
  • distinguish guard-and-return from failure-as-value
  • write Result and Option handling with Inspect
  • choose Require that for conditions that must stop the program

LOGOS uses Socratic error messages—friendly, educational feedback that teaches while it corrects.

The Philosophy

Instead of cryptic compiler errors, LOGOS explains:

  • What went wrong
  • Where it happened
  • Why it's a problem
  • How to fix it

Modeling Failure

Model success and failure as an enum and pattern-match on it — the Outcome example below shows

the whole pattern. The standard library's generic Result works exactly the same way: construct

with a new Ok with value x / a new Err with error msg, match with When Ok (v): /

When Err (e):. And for "maybe a value": some x and none build an Option you can

Inspect with When Some (v): / When None:.

Error Propagation

Errors propagate naturally through return values. Handle them where appropriate.

Defensive Programming

Use assertions and guards to prevent errors before they happen.

Code Examples6 examples
Safe Division with GuardImperative
Expected output
10 / 2 = 5 Error: Cannot divide by zero Result after error: 0
Input ValidationImperative
Expected output
Age 25 valid: true Error: Age cannot be negative Age -5 valid: false
Success/Failure with Pattern MatchingImperative
Expected output
Halved: 5 Error: cannot halve a negative
The Standard Library ResultImperative
Expected output
got 5 error: divide by zero
Option: some and noneImperative
Expected output
found 4 none
Require that (Fails on Purpose)Imperative
Expected error
Assertion failed
Now you tryFill in the blankFill the hole with the failure constructor so the error path prints.
practices: stdlib Result
Now you tryPracticeInspect `some 42` and print `found 42`; then Inspect `none` and print `none`.
practices: Option some/none + Inspect
Check yourself
How does a LOGOS function report failure without crashing?
What type do `some 4` and `none` build, and what is it for?
What does `Require that` do when its condition is false?

Have questions or feedback? Join our Discord community.

Join Discord →

16.Checkpoint: The Language

After this section you can
  • combine collections, control flow, functions, and interpolation in one program
  • diagnose which Part II section to reread when a challenge stumps you

Ten sections of core language deserve proof of mastery. The four challenges below

each blend two or more sections — none can be solved by pattern-matching a single

example, and one reaches back to Part I on purpose.

If a challenge stumps you, its hints name the moves; the section to reread follows

from them.

Now you tryChallengeSum the costs [450, 1200, 300] with a loop and print exactly `total: 1950 cents`.
practices: Repeat for-each · string interpolation · list literal
Now you tryChallengeWrap the tutorial's midpoint in a function `mid(lo, hi)` and show mid(1, 100) and mid(50, 75).
practices: function definition · staged build-one-program tutorial
Now you tryChallengeBind a block closure `classify` that returns "even" or "odd", then print the classification of 1, 2, 3.
practices: block closures · number predicates
Now you tryChallengeDefine an Outcome enum (Success with an Int, Failure with a Text), halve 10 through a checked function, and print `half is 5`.
practices: failure-as-enum pattern · string interpolation
Check yourself
Which container keeps insertion order and allows duplicates?
What does `10 / 3` print, and how do you get the remainder?
How do you write 'x is at least 1 and at most 10' as one chained comparison?
A closure captured a variable that later changed. Old value or new?
Struct or enum for 'a payment is cash or card'?
Why prefer Result over printing an error and returning 0?
What is exact for money: Float, Decimal, or both?
`items 2 through 3` of a five-item list — how many elements, and why?

Have questions or feedback? Join our Discord community.

Join Discord →

Part III: Systems Programming

17.Memory and Ownership

Two functions both want the invoice: one to display it, one to file it away for good. If both held full control, whose changes would win? LOGOS makes the question unaskable — every value has exactly one owner, and the verbs say who.
After this section you can
  • distinguish Give, Show-to, and copy of by what the caller keeps
  • predict the error a use-after-Give raises
  • choose the cheapest verb that still does the job

LOGOS provides memory safety through an ownership system expressed in natural English. Instead of cryptic symbols, you use verbs that describe what you're doing with data.

The Three Verbs

VerbMeaningWhat Happens
GiveTransfer ownershipThe original variable can no longer be used
ShowTemporary read accessThe function can look but not modify
Let modifyTemporary write accessThe function can change the data

Ownership Rules

  • Single Owner: Every value has exactly one owner at a time
  • Move Semantics: Give transfers ownership—you can't use it after
  • Borrow Checking: References (Show) can't outlive the owner
  • Exclusive Mutation: Only one Let modify at a time

Common Patterns

  • Copy first, then give
  • Show multiple times (all OK — reads never conflict)
  • Sequential mutation

The copy of Expression

Use copy of to create a deep clone of a value. This lets you keep using the original while giving away the copy.

Mental model: A value is a physical object. Give hands it over — your hand is
empty afterwards, and the compiler says so if you forget. Show x to f holds the
object up to the glass: f looks, nobody keeps. copy of runs the photocopier first.
The analogy's limit: copies are honest duplicates, not originals — changing one
never touches the other.
You might expect
Passing a variable to a function shares it — I can keep using it afterwards, like every garbage-collected language.
LOGOS instead
`Give` transfers ownership: the name has nothing left to hold, and the error names both fixes — lend with `Show x to f`, or hand over `a copy of x`. One owner at a time is why data races are unrepresentable.
See it run ↓
Code Examples4 examples
Show (Borrow)Imperative
Expected output
Displaying: User Profile Data User Profile Data
Give (Move Ownership)Imperative
Expected output
Consumed: Important data Message was transferred
Use After Give (Fails on Purpose)Imperative
What error do you expect?
Copy Before GivingImperative
Expected output
Processing: Keep this Original still here: Keep this
Now you tryFill in the blankFill the hole with the ownership verb that hands the copy to the archive.
practices: Give (move)
Now you tryPracticeBroken on purpose: it Gives, then uses. Change ONE line so the function still sees the text and the last Show works — lend, don't give.
practices: Show-to (borrow)
Check yourself
Which verb transfers ownership, which lends read access, which duplicates?
What can you do with a name after Give-ing it away?
What does single ownership buy in concurrent code?

Have questions or feedback? Join our Discord community.

Join Discord →

18.The Zone System

A simulation allocates a million temporaries every frame; freeing them one at a time costs more than making them did. What if cleanup were one instruction — the whole room swept at once?
After this section you can
  • write zone blocks, sized and default
  • explain the Hotel California rule and how results leave a zone
  • choose zones for burst allocation patterns

For high-performance scenarios, LOGOS provides Zones—memory regions where allocations are fast and cleanup is instant.

Why Zones?

OperationNormal HeapZone
AllocateO(log n)O(1)
Deallocate individualO(log n)N/A
Free everythingO(n)O(1)

The Hotel California Rule

"What happens in the Zone, stays in the Zone."

References to zone-allocated data cannot escape. To get data out of a zone, make an explicit copy.

Zone Configuration

Default size: 4 KB (4096 bytes) when not specified.

Specifying size: Use of size with units:

UnitExampleBytes
Bof size 256 B256
KBof size 64 KB65,536
MBof size 2 MB2,097,152
GBof size 1 GB1,073,741,824

Zone Types

Zone TypeSyntaxAccessUse Case
HeapInside a zone called "X":Read/WriteTemporary data
Heap (sized)Inside a zone called "X" of size 2 MB:Read/WriteLarge temporary data
MappedInside a zone called "X" mapped from "file.bin":Read-onlyLarge file processing

When to Use Zones

Use zones when:

  • Processing large amounts of temporary data
  • Performance is critical (games, simulations)
  • Memory allocation patterns are predictable
  • You want instant cleanup
Mental model: The zone is a whiteboard room: allocate by writing anywhere on the
board, free by erasing the whole board on the way out — one wipe, no matter how much
was written. What happens in the zone stays in the zone: to keep a result, copy it
out before the wipe. The board is not magic memory — oversized data needs a bigger
board, asked for with of size.
Code Examples4 examples
Basic Zone (4KB default)Imperative
Expected output
[1, 2, 3, 4, 5] Zone freed!
Zone with Size (MB)Imperative
Expected output
[1, 2, 3, 4, 5]
Zone with Size (KB)Imperative
Expected output
142
Memory-Mapped ZoneImperative
Expected output
Processing: config.bin Processing: assets.bin
Now you tryFill in the blankFill the hole with the zone's size so the burst work runs in 64 KB.
practices: sized zones
Now you tryPracticeInside a 64 KB zone, sum [10, 20, 30, 40] into a total declared OUTSIDE the zone, and show it after the zone closes.
practices: zone blocks
Check yourself
What does freeing a zone cost, regardless of how much it allocated?
How does a result leave a zone?
What kind of workload does a zone beat the normal heap on?

Have questions or feedback? Join our Discord community.

Join Discord →

19.Bits, Bytes, and Machine Words

Add 1 to 4294967295 in a Word32 and you get 0 — and SHA-3, ChaCha20, and every checksum you trust are built on exactly that. The craft is knowing when wrap-around is the bug and when it is the tool.
After this section you can
  • predict wrapping arithmetic on fixed-width words
  • use xor, rotate, and shifts as cipher building blocks
  • distinguish Word types from auto-promoting Int

Word8, Word16, Word32, and Word64 are fixed-width machine words: arithmetic on

them wraps (the ring ℤ/2ⁿ) instead of promoting. That is not a limitation — it is the

substrate hashing, checksums, and ciphers are built on. The LOGOS standard library's

SHA-3, ChaCha20, and ML-KEM are written in LOGOS itself on exactly these words.

The word vocabulary

OperationForm
Constructword32(n), word64(n)
Back to IntintOfWord32(w), intOfWord64(w)
Bitwisew xor v, word_and(a, b), word_or(a, b), word_not(a)
Rotaterotl(w, n), rotr(w, n)
Shiftw shifted left by n, w shifted right by n

Wrapping is the point

word32(4294967295) + word32(1) is exactly 0 — the carry falls off the 32nd bit. Name

the behavior and it becomes a tool: rotation, mixing, and diffusion in ciphers all depend

on it.

Plain Int bit tools

Int has bit operations too: shifted left by / shifted right by, xor, and

count_ones(n) (the number of set bits).

Mental model: A word is a car odometer with a fixed number of digits: 99999 + 1
rolls to 00000 — the roll-over is the mechanism, not a malfunction. Rotation spins the
digits around the dial without losing any. The odometer's limit: it counts patterns,
not meaning — words are raw bit patterns; what they stand for is yours to assign.

*Try it: in the xorshift example, run twice — same seed, same output, every time. Change

the seed and watch the whole sequence change.*

You might expect
Integer overflow is a bug: a panic (Rust debug), undefined behavior (C), or impossible (Python).
LOGOS instead
On Word types the wrap IS the semantics — ℤ/2ⁿ ring arithmetic, the substrate ciphers and hashes are built on. Plain Int auto-promotes instead; wrapping happens exactly where you asked for it.
See it run ↓
Code Examples4 examples
Wrapping: The Carry Falls OffImperative
What will this print?
XOR and RotateImperative
Expected output
5 256
Int Bit ToolsImperative
Expected output
16 16 8
A PRNG in Four Lines (xorshift)Imperative
Expected output
8748534153485358512
Now you tryFill in the blankThe classic xorshift shifts by 13, 7, 17 — fill the first hole.
practices: xorshift PRNG
Now you tryPracticeRotate word32(1) left by 4 and print it; then print intOfWord64(word64(255)).
practices: word bitwise + rotate
Check yourself
What does `word32(4294967295) + word32(1)` print, and why is that not a bug?
Same sum on plain Int — what happens instead of wrapping?
Which three word operations does xorshift combine?

Have questions or feedback? Join our Discord community.

Join Discord →

20.Concurrency

Three workers pull jobs from one queue. In most languages the output depends on which thread won which race — today. A LOGOS run is a pure function of the program and a seed: the race you saw is the race you can replay.
After this section you can
  • write tasks, pipes, and select blocks
  • predict pipe buffering behavior before running it
  • explain why concurrent runs replay deterministically

LOGOS provides safe concurrency through structured patterns. No data races, no deadlocks.

Concurrent Patterns Overview

PatternSyntaxUse ForCompiles To
Async JoinAttempt all of the following:Wait for all I/O taskstokio::join!
Parallel CPUSimultaneously:CPU-bound computationrayon::join / threads
Spawn TaskLaunch a task to...Fire-and-forget worktokio::spawn
ChannelsPipe of TypeMessage passingtokio::mpsc
SelectAwait the first of:Race operationstokio::select!

Attempt All (Async I/O)

Use Attempt all of the following: for I/O operations that wait on external resources. All operations run concurrently, and the program waits until all complete.

Variables declared in concurrent blocks are captured and returned as a tuple.

Simultaneously (Parallel CPU)

Use Simultaneously: for CPU-intensive work. Computations run in parallel on different CPU cores.

  • 2 tasks → uses rayon::join (work-stealing thread pool)
  • 3+ tasks → uses std::thread::spawn (dedicated threads)

Tasks (Green Threads)

Use Launch a task to... to spawn a green thread that runs concurrently. For fire-and-forget work, launch and move on:

Launch a task to process(data).

To control the task later (cancel, await), capture a handle:

Let worker be Launch a task to process(data).

Stop a running task with:

Stop worker.

Channels (Pipes)

Pipes are Go-style channels for message passing between tasks.

Create a channel:

Let jobs be a new Pipe of Int.

Send into a channel (blocking):

Send value into jobs.

Receive from a channel (blocking):

Receive item from jobs.

Non-blocking variants:

Try to send value into jobs.

Try to receive item from jobs.

Select (Racing Operations)

Use Await the first of: to race multiple operations. The first one to complete wins:

Await the first of:
    Receive msg from inbox:
        Show msg.
    After 5 seconds:
        Show "timeout".

Branch types:

  • Receive var from pipe: — wait for channel message
  • After N seconds: — timeout branch

Ownership and Concurrency

The ownership system prevents data races. Multiple reads are OK, but concurrent writes are prevented.

Note: Everything above — tasks, pipes, and select — runs right here in the browser playground on the deterministic scheduler. Compiled programs run the same code on the real multi-threaded runtime.

Deterministic replay

A concurrent LOGOS run is a pure function of the program and a seed: the scheduler's every

choice derives from LOGOS_SEED, so a race you saw once is a race you can replay forever —

same seed, same interleaving, byte-identical output. Concurrency bugs stop being

heisenbugs.

Mental model: Tasks are relay runners; pipes are the baton-passing lanes between
them. The scheduler is a referee working from a script: every "who runs next" choice
is written by the seed, so re-running the race replays every hand-off exactly. The
script covers scheduling only — a wrong program replays its wrongness deterministically.
You might expect
Send blocks until someone receives — an unbuffered Go channel would deadlock right here.
LOGOS instead
Pipes buffer: a task can send several values and receive them later itself. Blocking bites at RECEIVE on an empty pipe — and `Try to receive` is the non-blocking probe.
See it run ↓
Code Examples9 examples
Async Concurrent (Attempt All)Imperative
Expected output
a = 10 b = 20 Sum: 30
Parallel CPU (Simultaneously)Imperative
Expected output
x = 100 y = 200 Product: 20000
Three Parallel TasksImperative
Expected output
Sum: 6
Concurrency in FunctionsImperative
Expected output
Result: 15
Launch TaskImperative
Expected output
Worker 1 started Worker 2 started Tasks launched
Task with HandleImperative
Expected output
Working... Task spawned Task cancelled
Pipe CommunicationImperative
What will this print?
Select with TimeoutImperative
Expected output
No message received
Non-Blocking Send and ReceiveImperative
Expected output
5
Now you tryFill in the blankFill the hole so all three values reach the pipe and the sum prints 60.
practices: Send/Receive
Now you tryPracticeSend 10, 20, 30 into a pipe, receive all three, and print `sum: 60`. Your answer is gradable because runs are reproducible.
practices: pipes (channels) · Send/Receive
Check yourself
What two things fully determine a concurrent LOGOS run?
Which end of a pipe can block — Send or Receive?
Attempt all vs Simultaneously — which for I/O waits, which for CPU work?

Have questions or feedback? Join our Discord community.

Join Discord →

21.Interoperability: Rust, Native, and WASM

Sooner or later you need something LOGOS doesn't ship: a vendor SDK, a syscall, a hand-tuned kernel. Three doors open outward — embed Rust in place, bind a native host function, or export your LOGOS to WASM callers.
After this section you can
  • name the three interop doors and what each is for
  • distinguish what runs in the playground from what needs largo build

When you need to reach outside the language, three doors open — all in compiled programs.

Escape to Rust

An Escape to Rust: block embeds raw Rust in place; largo build compiles it into the

binary as-is. The playground shows the honest boundary: it cannot interpret raw Rust, and

says so.

Native functions

## To native <name> (…) -> <Type> declares a function whose body is provided by the

host — the mechanism behind the standard library's now, read, and randomInt.

Exporting to WASM

Mark a function is exported for wasm and largo build --emit wasm produces a module

whose exports call straight into your LOGOS code — no JavaScript shims, no rustc in the

loop. The decorator parses and runs everywhere — it *matters* at largo build --emit wasm.

Escape to RustImperative
Exporting to WASM (Decorator Runs Everywhere)Imperative
Expected output
36
Check yourself
What are the three interop doors?
Why can't the playground run an Escape block?
Where does `is exported for wasm` change behavior — the playground or the build?

Have questions or feedback? Join our Discord community.

Join Discord →

22.Checkpoint: Systems Programming

After this section you can
  • combine ownership verbs, zones, and pipes in working programs
  • diagnose which Part III section to reread when a challenge stumps you

Ownership, zones, machine words, concurrency — the systems toolkit. Three challenges

below blend them, and one reaches back to Part II on purpose. The recall cards then

collect on the part's promises.

Now you tryChallengePrint word32(6) xor word32(3), then the remainder of 10 divided by 3.
practices: word bitwise + rotate · English arithmetic
Now you tryChallengeBuild "Q3" + " report" inside a zone, copy it out to a variable declared outside, Give that to an archive function, and print `done`.
practices: Give (move) · zone blocks
Now you tryChallengeSend 1 and 2 into a pipe, receive both, and print `total: 3` with interpolation.
practices: pipes (channels) · string interpolation
Check yourself
Give, Show-to, copy of — which keeps the original usable?
What is O(1) about a zone?
Which type wraps on overflow — Int or Word32 — and what does the other do?
Why is a LOGOS data race replayable?
Which pipe operation can block, and what is its non-blocking twin?
Which interop door embeds raw Rust in place?

Have questions or feedback? Join our Discord community.

Join Discord →

Part IV: Distributed Programming

23.Distributed Types (CRDTs)

Two phones edit the same shopping list in a tunnel, offline. When they reconnect, whose edits survive? With CRDTs the answer is BOTH — merge is mathematics, not mediation.
After this section you can
  • write Shared structs with CRDT fields
  • predict what Merge does to concurrent updates
  • choose the right CRDT type per field

What are CRDTs?

CRDTs (Conflict-free Replicated Data Types) are data structures that can be replicated across multiple computers and merged without coordination. No matter what order updates arrive, the final state converges to the same result.

Why CRDTs Matter

ChallengeTraditional ApproachCRDT Approach
Network partitionData loss or conflictsAutomatic merge
Concurrent editsLast-write-wins (data loss)Semantic merge
Offline supportSync conflictsSeamless reconciliation

Shared Structs

Mark a struct as Shared to enable automatic merge support. The compiler generates a merge method that combines two instances.

Built-in CRDT Types

TypeDescriptionOperations
ConvergentCountCounter that only growsIncrease
TallyCounter that grows and shrinksIncrease, Decrease
LastWriteWins of TRegister with timestamp-based conflict resolutionSet
Divergent TRegister that preserves concurrent valuesSet, Resolve
SharedSet of TSet with add/remove supportAdd, Remove, contains
SharedSequence of TOrdered list (RGA algorithm)Append, length of
CollaborativeSequence of TText-optimized sequence (YATA)Append, length of
SharedMap from K to VKey-value CRDT map[] access and assignment

ConvergentCount

A grow-only counter (G-Counter). Multiple replicas can increment independently, and when merged, the total reflects all increments. Useful for view counts, likes, or any monotonically increasing metric.

Tally

A bidirectional counter (PN-Counter) that supports both increment and decrement. Unlike ConvergentCount, values can go up and down—even negative. Useful for scores, balances, and temperatures.

LastWriteWins

A register that resolves conflicts by timestamp. The most recent write wins. Works with any type: Text, Int, Bool, etc.

Divergent

A multi-value register that preserves all concurrent writes instead of silently picking a winner. When replicas write different values concurrently, both are kept until you explicitly Resolve the conflict. Useful for collaborative editing where conflicts should be visible.

SharedSet

An observed-remove set (OR-Set) that supports both adding and removing elements. By default uses add-wins semantics: if one replica adds while another removes, the element stays.

Configuring bias:

  • SharedSet (AddWins) of T — concurrent add beats remove (default)
  • SharedSet (RemoveWins) of T — concurrent remove beats add

SharedSequence

An ordered CRDT list using the RGA (Replicated Growable Array) algorithm. Elements maintain their order across replicas. Useful for ordered lists, chat history, and document lines.

CollaborativeSequence

A text-optimized sequence using the YATA algorithm. Better conflict resolution for concurrent insertions at the same position. Ideal for collaborative text editing. Alternative syntax: SharedSequence (YATA) of T.

SharedMap

A key-value CRDT map (OR-Map). Keys can be added and removed, and values are themselves CRDTs that merge recursively. Alternative syntax: ORMap from K to V.

Merge Operations

Use Merge source into target to combine two CRDT instances. The target is updated in place with the merged state.

Persistence

CRDTs can be persisted to disk using the Persistent type modifier and Mount statement. Data is stored in append-only journal files (.lsf format) with automatic compaction.

The Persistent Type:

Persistent Counter wraps a Shared struct with journaling. All mutations are durably recorded.

The Mount Statement:

Mount [variable] at [path].

or

Let x be mounted at "path/to/data.lsf".

This loads existing state from the journal file (if present) or creates a new one. Changes are automatically persisted.

Network Synchronization

CRDTs earn their keep when synchronized across the network. Use Sync to subscribe a variable to a GossipSub topic.

The Sync Statement:

Sync [variable] on [topic].

  • variable — A mutable variable containing a Shared struct
  • topic — A string or variable naming the GossipSub topic

What Sync Does:

  • Subscribes to the topic for incoming messages
  • Spawns a background task to merge incoming updates
  • Broadcasts the full state after any mutation

Persistence + Network

For the best of both worlds, combine Persistent types with Sync. The Distributed runtime ensures:

  • Local changes are journaled before broadcast
  • Remote updates are merged and persisted
  • Data survives restarts

Note: In the playground, Sync runs in local single-node mode — the statement executes, but there are no real peers to gossip with. Mount/Persistent journaling and SharedMap need the compiled runtime; those examples are marked "(Compiled Only)".

Mental model: Replicas are notebooks kept by different reporters: syncing is the
union of FACTS — increments, adds, timestamped writes — never a fight over one pen.
Convergence is guaranteed because a union doesn't care what order the facts arrive in.
The limit: union needs facts designed to union — a plain overwrite degenerates to
LastWriteWins, where the later clock wins and the earlier write is gone.
You might expect
Two replicas both incremented the counter; merging keeps one and loses the other — someone must win.
LOGOS instead
ConvergentCount merges by combining per-replica contributions: 100 + 50 = 150, in any merge order. Every increment survives — that is the 'conflict-free' in CRDT, and it is why no coordinator is needed.
See it run ↓
Code Examples13 examples
Basic Shared StructImperative
Expected output
10
Merging ReplicasImperative
What will this print?
Last-Write-Wins RegisterImperative
Expected output
Alice 100
Synced CounterImperative
Expected output
100
Synced ProfileImperative
Expected output
1
Persistent CounterImperative
Tally (Bidirectional Counter)Imperative
Expected output
70
Divergent (Multi-Value Register)Imperative
Expected output
Draft Final
SharedSet (Add/Remove Set)Imperative
Expected output
Bob is invited 1
SharedSet with BiasImperative
Expected output
{safe} {spammer}
SharedSequence (Ordered List)Imperative
Expected output
3
CollaborativeSequence (Text)Imperative
Expected output
3
SharedMap (Key-Value CRDT)Imperative
Now you tryFill in the blankFill the hole so away's contributions fold into home — 45 survives.
practices: Merge
Now you tryPracticeHome scored 30. Away scored 20, then lost 5. Merge away into home and print home's points.
practices: Tally (PN-Counter) · Merge
Check yourself
What does the 'conflict-free' in CRDT actually guarantee?
A like-counter, a set of invited guests, a profile name — which CRDT for each?
When would you reach for Divergent instead of LastWriteWins?

Have questions or feedback? Join our Discord community.

Join Discord →

24.P2P Networking

A LAN game finds its peers with zero configuration: Listen once, and mDNS introduces every machine on the network. The whole shape of a P2P app — addresses, handshakes, gossip — fits on this one page.
After this section you can
  • name the P2P building blocks: Listen, Connect, PeerAgent, Send, Sync
  • distinguish playground single-node mode from compiled live networking

LOGOS includes built-in peer-to-peer networking primitives for building distributed applications.

Note: In the playground these examples run in offline single-node mode — Listen, Send, and mDNS setup execute locally with no real network. Connect and PeerAgent dial live peers, so they need a compiled program; those examples are marked "(Compiled Only)".

Core Concepts

ConceptDescription
Addresslibp2p multiaddr format: /ip4/127.0.0.1/tcp/8000
ListenBind to an address to accept connections
ConnectDial a peer at an address
PeerAgentA handle to a remote peer
SendTransmit a message to a peer
SyncSubscribe a CRDT to a GossipSub topic

Portable Types

Messages sent over the network must be Portable. Mark your struct with is Portable to enable network serialization.

Address Format

LOGOS uses libp2p multiaddresses:

AddressMeaning
/ip4/0.0.0.0/tcp/8000Listen on all interfaces, port 8000
/ip4/127.0.0.1/tcp/8000Localhost only, port 8000
/ip4/192.168.1.5/tcp/8000Specific IP address
/ip4/0.0.0.0/tcp/0Listen on any available port

Automatic Peer Discovery (mDNS)

When you Listen, LOGOS automatically enables mDNS (multicast DNS) for local network peer discovery. Peers on the same LAN will discover each other without manual configuration.

  • Works on WiFi networks, local development
  • No configuration required — Listen is enough
  • Peers are auto-connected when discovered

GossipSub (Pub/Sub)

The Sync statement uses GossipSub, a pub/sub protocol for broadcasting messages to topic subscribers:

  • Topics are strings (e.g., "game-scores", "player-data")
  • When you mutate a synced variable, the full state broadcasts to all subscribers
  • Incoming messages are automatically merged in the background
  • Retry with exponential backoff: 1s, 2s, 4s, 8s, 16s

File Transfer

For large file transfers, LOGOS provides FileSipper—a chunked transfer protocol:

ComponentDescription
FileSipperZero-copy file chunker (1 MB default chunks)
FileManifestDescribes file: chunk count, SHA256 hashes
FileChunkIndividual chunk with verification hash

This enables resumable transfers over unreliable networks.

Building a P2P Application

  • Define Portable message types
  • Listen on an address (server)
  • Connect to peers (client)
  • Create PeerAgent handles
  • Send messages
  • Use Sync for automatic CRDT replication
Code Examples7 examples
Server: Listen for ConnectionsImperative
Expected output
Server listening on port 8000
Client: Connect to PeerImperative
Creating a Remote HandleImperative
Sending a MessageImperative
Expected output
Sending: Hello, peer!
Persistent + SyncedImperative
Automatic Peer DiscoveryImperative
Expected output
Listening... mDNS will auto-discover peers Synced to game-session topic
File Transfer PatternImperative
Expected output
File server ready Supports resumable chunked transfers
Check yourself
What must a struct declare before it can cross the network?
What configuration does LAN peer discovery need?
Which networking statements run in the playground, and in what mode?

Have questions or feedback? Join our Discord community.

Join Discord →

25.Policy-Based Security

The intern's cleanup script deletes the CEO's document at 2 a.m. — through the code path everyone forgot to guard. A `Check that` line is the guard that cannot be optimized away, compiled from a policy you can read aloud.
After this section you can
  • write policy predicates and capabilities in English
  • distinguish Check from Assert by what survives a release build
  • predict what a denied Check does to the program

Security in Natural Language

LOGOS lets you express security policies as natural English sentences. These compile into efficient runtime checks that can never be optimized away.

Policy Blocks

Define security rules in ## Policy blocks. Policies define predicates (conditions on a single entity) and capabilities (permissions involving multiple entities).

Predicates

A predicate is a boolean condition on a subject:

A User is admin if the user's role equals "admin".

This generates a method is_admin() on the User type.

Capabilities

A capability defines what a subject can do with an object:

A User can publish the Document if the user is admin.

This generates a method can_publish(&Document) on the User type.

Check Statements

Use Check to enforce security at runtime. Unlike Assert, Check statements are mandatory and can never be optimized away.

StatementDebug BuildRelease Build
AssertRunsCan be optimized out
CheckRunsAlways runs

Policy Composition

Policies can use AND and OR to combine conditions, and can reference other predicates.

Simple PredicateImperative
Expected output
Access granted
Capability with ObjectImperative
Expected output
Edit permitted
A Denied Check (Fails on Purpose)Imperative
What error do you expect?
Now you tryFill in the blankFill the hole with the predicate so the gate opens for the admin.
practices: Check enforcement
Now you tryPracticeWrite a `can delete` capability: admins, or the document's owner. Ada (a writer) owns the doc — Check she can delete it and print `delete permitted`.
practices: capabilities · policy predicates
Check yourself
Check vs Assert — which survives a release build, and why does it matter?
Predicate vs capability — what does each involve?
What does a denied Check do?

Have questions or feedback? Join our Discord community.

Join Discord →

26.Checkpoint: Distributed Programming

After this section you can
  • combine Shared types, merges, and policies in working programs
  • diagnose which Part IV section to reread when a challenge stumps you

CRDTs, networking, and policies close the distributed story. Three challenges blend

them — two reach back to earlier parts on purpose — and the recall cards collect on

the part's promises.

Now you tryChallengeA Shared profile's name is set to "Ada", then "Grace". Print the surviving name.
practices: LastWriteWins register · possessive field access
Now you tryChallengeLocal counted 100 views; remote counted 50. Merge and print `views: 150`.
practices: ConvergentCount (G-Counter) · string interpolation
Now you tryChallengeDefine a User with a role, a policy making role "admin" an admin, Check it for an admin user, and print `granted`.
practices: Check enforcement · struct definition
Check yourself
Two replicas of a ConvergentCount hold 100 and 50. What does merging produce, and in what orders?
What decides the winner in LastWriteWins?
One replica adds a tag while another removes it. Who wins by default?
What marks a struct as network-serializable?
Why is Check the right gate for security, not Assert?
What three things does `Sync x on "topic"` set up?

Have questions or feedback? Join our Discord community.

Join Discord →

Part V: Projects and Tooling

27.Modules

One file is fine for a script; a real project wants its vocabulary split by topic — geometry here, billing there. Each file is a module, and the filename is the name.
After this section you can
  • explain how files become modules
  • name what the playground can and cannot import

Organize large programs across multiple files using the module system.

Importing Modules

Use Use to import a module.

Qualified Access

Access module contents with the possessive 's.

Creating Modules

Each .md file is a module. The filename becomes the module name.

Visibility

By default, all definitions are public. Mark fields private with no public modifier.

The playground runs single-file programs, so the example below shows the module SHAPE

in one file — definitions above, ## Main below. Multi-file Use imports are a

largo-project feature.

The Module Shape, in One FileImperative
Expected output
5 squared = 25
Check yourself
What makes a module in LOGOS?
Why does the playground not run `Use` imports?

Have questions or feedback? Join our Discord community.

Join Discord →

28.The CLI: largo

After this section you can
  • choose the right largo subcommand for check, run, build, and prove workflows
  • run a program on the interpreter for sub-second feedback

LOGOS projects are built with largo, the LOGOS build tool.

Installing

curl -fsSL https://logicaffeine.com/install.sh | sh

Windows: powershell -ExecutionPolicy Bypass -c "irm https://logicaffeine.com/install.ps1 | iex".

Prebuilt for Linux/macOS (x64 + arm64) and Windows x64, SHA-256-verified, installed to

~/.local/bin with no sudo. --full bundles Z3 static verification.

Creating a Project

CommandDescription
largo new <name>Create a new project in a new directory
largo initInitialize a project in the current directory

This creates a Largo.toml manifest and src/main.lg entry point.

Build Commands

CommandDescription
largo buildCompile the project to a native binary
largo build --releaseCompile with optimizations
largo runBuild and run
largo run --interpretRun on the interpreter—no Rust build, sub-second feedback
largo run --releaseBuild and run with optimizations
largo checkType-check without compiling
largo verifyRun Z3 static verification (Pro+ license required)
largo build --verifyBuild with verification
largo build --target wasmCross-compile to WebAssembly
largo opts <file>Report which optimizations actually fire

The Wider Verbs

CommandDescription
largo replInteractive session: imperative statements + English→FOL logic mode
largo logic "<sentence>"English → First-Order Logic (--all-readings, --format latex)
largo prove [file]Kernel-certified theorem proving (## Theory / ## Theorem blocks)
largo sat <file.cnf>The certified SAT solver (DIMACS in, DRAT proofs out)
largo fmt [--check]Format sources (the LSP's rules)
`largo emit <rust\c\wasm>`Print or write the generated code
largo docGenerate markdown docs from a project's ## blocks
largo add / remove <dep>Edit Largo.toml dependencies (format-preserving)
largo cleanRemove build artifacts
largo completions <shell>Shell tab-completion scripts

Package Registry

Publish and manage packages on the LOGOS registry:

CommandDescription
largo loginAuthenticate with the registry
largo publishPublish your package
largo publish --dry-runValidate without publishing
largo logoutLog out from the registry

Project Manifest

The Largo.toml file defines package metadata and dependencies:

[package]
name = "myproject"
version = "0.1.0"
entry = "src/main.lg"

[dependencies]
Check yourself
Which command type-checks without compiling?
Which flag runs a program with no Rust build at all?
Which command certifies Theorem blocks?

Have questions or feedback? Join our Discord community.

Join Discord →

29.Standard Library

Before reaching for a library: absolute value, min and max, lengths, formatting — the everyday verbs are already here, and the bigger modules (file, time, random, crypto) import themselves the moment you call them.
After this section you can
  • use the core builtins without any imports
  • name which stdlib modules auto-import on first call
  • distinguish playground-safe calls from host-dependent ones

LOGOS provides built-in functions for common operations.

Currently Available

These built-ins work in both the playground and compiled programs:

  • Show x. — Output values to the console
  • length of x — Get the length of a list or text
  • format(x) — Convert any value to text
  • abs(n) — Absolute value of a number
  • min(a, b) — Minimum of two integers
  • max(a, b) — Maximum of two integers

Standard Library Modules

These modules import themselves the moment you call them — no Use line needed:

ModuleFunctions
fileread(path) -> Result of Text and Text, write(path, content) -> Result of Unit and Text
timenow() -> Nat (Unix milliseconds), sleep(ms)
randomrandomInt(min, max) -> Int, randomFloat() -> Real
envget(key) -> Option of Text, args() -> Seq of Text
cryptoPost-quantum ML-KEM-768, ChaCha20, SHA-3/Keccak — written in LOGOS itself
uuidRFC 9562 UUIDs, all versions

Every stdlib definition carries literate ## Note documentation — hover over a call in the

editor, or run largo doc, to read it.

File, environment, and network access follow the host program: compiled programs get the real

operating system, while the browser playground is sandboxed, so those calls are limited here.

Standard LibraryImperative
Expected output
Built-in functions: length of nums = 5 length of text = 5 abs(-42) = 42 min(10, 3) = 3 max(10, 3) = 10
Reading a File (Result-Typed)Imperative
SHA3-256 (Written in LOGOS)Imperative
Now you tryFill in the blankFill the hole with the builtin that turns -42's minimum into 42.
practices: core builtins
Now you tryPracticePrint abs(min(-42, 7)); then clamp 15 into the range 1..10 by composing max and min.
practices: core builtins
Check yourself
Which everyday builtins need no import at all?
How do the file/time/random/crypto modules get imported?
Why is `read(path)` compiled-only?

Have questions or feedback? Join our Discord community.

Join Discord →

30.How LOGOS Runs: The Five Tiers

The same program runs five ways — interpreted in this page, JIT-compiled mid-loop, or built to a native binary. You choose the tradeoff; the meaning is proven identical, so the choice is never a risk.
After this section you can
  • name the five execution tiers and when each fires
  • use ## No and ## Tier decorators without changing meaning
  • explain what translation validation proves

One program, five ways to execute it — you choose the tradeoff, the semantics never change:

TierWhatWhen
Tree-walking interpreterruns the AST directlythis playground, largo run --interpret
Register bytecode VMcompact bytecode, Int fast pathslive sync, the browser
EXODIA JITcopy-and-patch native x86-64hot functions tier up automatically
AOT Rustlargo build — full native binaryproduction (the 11-language benchmark winner)
Direct WASMlargo build --emit wasm, no rustcmilliseconds to a .wasm module

Translation validation proves the compiled tiers faithful: the emitted Rust is checked

equivalent to the source semantics with an SMT solver — you do not have to trust the

compiler, you can check it.

Steering the optimizer

## No <Name> decorators disable a named optimization for the program (forty exist —

Memo, Tco, Inline, Unroll, …), and ## Tier opt pins the VM's tier-up policy.

Both parse and run everywhere; they *matter* to the compiled tiers. largo opts <file>

reports which optimizations actually fired.

Mental model: One recipe, five kitchens: a tasting counter (interpreter), a prep
line (VM), a flash grill (JIT), a full restaurant build (AOT Rust), and a food truck
(direct WASM). Same dish every time — translation validation is the taste test that
proves the plating identical. Only the speed changes; the menu never does.

*Try it: both examples below print the same 42 — the decorators change how it runs, never

what it means.*

Pinning the Tier PolicyImperative
Expected output
42
Disabling One OptimizationImperative
Expected output
42
Check yourself
What are the five ways one LOGOS program can execute?
What do ## No and ## Tier decorators change — and what do they never change?
What does translation validation prove?

Have questions or feedback? Join our Discord community.

Join Discord →

31.Checkpoint: Projects and Tooling

After this section you can
  • combine builtins, decorators, and earlier-part constructs in working programs
  • diagnose which Part V section to reread when a challenge stumps you

Modules, largo, the stdlib, and the five tiers — the working-programmer's toolkit.

Three challenges blend them with earlier parts, then the recall cards collect.

Now you tryChallengeDisable the Memo optimization with a decorator, then print the length of "LOGOS" — same answer, by design.
practices: execution tiers + opt decorators · core builtins
Now you tryChallengeWrite clamp(x, lo, hi) from min and max; show clamp(12, 1, 10) then clamp(0, 1, 10).
practices: core builtins · function definition
Now you tryChallengeCompute the length of "tiers" into a variable, then print `len: 5` by interpolating it.
practices: core builtins · string interpolation
Check yourself
Fast feedback loop: which two largo invocations skip rustc entirely?
What triggers a stdlib module's import?
Which tier runs in this page, and which produces a native binary?
Why can you trust largo build without trusting the compiler?
What is the unit of modularity in a largo project?
Can a ## No decorator change a program's output?

Have questions or feedback? Join our Discord community.

Join Discord →

Part VI: Logic and Verification

32.Logic Mode

Translate 'All birds fly' to logic. If you wrote ∀x(Bird(x) → Fly(x)), you are in good company — and you missed where the flying HAPPENS. Give every verb an event, and adverbs, tense, and who-did-what suddenly have a home.
After this section you can
  • translate quantified English sentences to FOL
  • read event variables and thematic roles (Agent, Theme)
  • predict the shape negative quantifiers take

LOGOS can translate English sentences into First-Order Logic (FOL). This is useful for formal verification, knowledge representation, and understanding the logical structure of natural language.

Quantifiers

EnglishSymbolOutput
All X are Y∀x(X(x) → Y(x))
Some X is Y∃x(X(x) ∧ Y(x))
No X is Y¬∃¬∃x(X(x) ∧ Y(x))

Connectives

EnglishSymbol
and
or
not¬
if...then
if and only if

Modals

EnglishSymbol
can, may, might (possibility)
must (necessity)

Tense and Aspect

  • PAST(P) — past tense
  • FUT(P) — future tense
  • PROG(P) — progressive aspect
  • PERF(P) — perfect aspect
Mental model: Every verb is an EVENT with name tags on its participants:
"John runs" becomes ∃e(Run(e) ∧ Agent(e, John)) — there exists a running, and John
is its agent. Adverbs decorate the event, tense stamps it, roles name who did what
to whom. The limit: pure states (being tall) ride the same machinery — linguists
argue about that; the parser commits.
You might expect
'All birds fly' should be ∀x(Bird(x) → Fly(x)) — that is Logic 101.
LOGOS instead
LOGOS emits neo-Davidsonian event semantics: ∀x(Birds(x) → ∃e(Fly(e) ∧ Agent(e, x))) — the flying is an event x participates in. That extra e is what lets adverbs, tense, and roles compose instead of exploding the predicate.
See it run ↓
Code Examples5 examples
Universal QuantifierLogic Mode
What FOL does this produce?
Existential QuantifierLogic Mode
Expected output
∃x((Cats(x) ∧ ∃e(Sleep(e) ∧ Agent(e, x))))
Negative QuantifierLogic Mode
Expected output
∀x(((Fish(x) ∧ Animal(x)) → ¬∃e(Fly(e) ∧ Agent(e, x))))
ConditionalLogic Mode
Expected output
∀e(∀e((HAB(Run(e) ∧ Agent(e, John)) → HAB(∃e(Walk(e) ∧ Agent(e, Mary))))))
Modal OperatorsLogic Mode
Expected output
◇_{0.5} ∃e(Swim(e) ∧ Agent(e, John))
Now you tryPracticeWrite the First-Order Logic for: All birds fly. (Run the Universal Quantifier example above to see the shape.)
practices: FOL quantifiers
Now you tryPracticeWrite the FOL for: Some birds sing.
practices: FOL quantifiers
Now you tryPracticeWrite the FOL for: No cats bark.
practices: FOL quantifiers
Check yourself
What does the `e` in ∃e(Fly(e) ∧ Agent(e, x)) stand for?
How does 'No fish fly' negate — on the fish or on the flying?
Universal pairs with →; what does the existential pair with, and why?

Have questions or feedback? Join our Discord community.

Join Discord →

33.Assertions and Trust

withdraw(50, 100) must never leave a negative balance — but 'must never' in a comment guards nothing. Assert makes the sentence executable; Trust records the reason when you can't check.
After this section you can
  • write Assert and Trust-with-because guards
  • distinguish what debug and release builds keep
  • predict what a failing Assert does to the run

LOGOS bridges imperative programming with formal verification through assertions and proof statements.

Assert

Use Assert to verify conditions at runtime. If an assertion fails, the program stops with a clear error message.

Trust with Justification

Use Trust for conditions the compiler can't verify automatically. The because clause is mandatory—it documents your reasoning.

Trust Generates Debug Assertions

In development builds, Trust becomes a debug_assert!. In release builds, it generates no code—the trust is assumed.

Auditing Trust Statements

Every Trust stays a plain Trust that ... because ... line in your source—search for Trust that to review every assumption, each carrying the because justification that documents why it holds.

Proof Blocks (Advanced)

For formal verification, use theorem blocks with proofs documented in comments.

You might expect
Assertions are plain if-statements — they always run.
LOGOS instead
Trust compiles to a debug-only assertion and vanishes in release; Assert runs and can be tuned by build settings; only security Check can NEVER be dropped. Choose the guard by what must survive shipping.
See it run ↓
Code Examples4 examples
AssertImperative
Expected output
50
A Failing Assert (Fails on Purpose)Imperative
What error do you expect?
Trust with JustificationImperative
Expected output
10
AssertionsImperative
Expected output
5
Now you tryFill in the blankFill the hole so the guard reads as an inequality and the division runs.
practices: Assert
Now you tryPracticeGuard safe_ratio with an Assert that b is not 0, then show safe_ratio(9, 3).
practices: Assert
Check yourself
What happens when an Assert's condition is false?
When is Trust the right tool instead of Assert?
How do you audit every assumption in a codebase?

Have questions or feedback? Join our Discord community.

Join Discord →

34.Z3 Static Verification

A percentage that cannot leave 0–100, a balance positive by construction — refinement types state the invariant where the type lives, and Z3 proves it before the program ever runs.
After this section you can
  • write refinement annotations with where clauses
  • predict what the playground does with a violated refinement
  • explain when Z3 actually checks (largo build --verify)

LOGOS can use the Z3 SMT solver to verify refinement types at compile time.

What is Z3?

Z3 is a theorem prover. Instead of checking constraints at runtime, Z3 proves (or disproves) them at compile time.

ApproachWhen CheckedIf Violated
Runtime assertionWhen code runsProgram crashes
Z3 verificationAt compile timeCompilation fails

Variable Tracking

Z3 tracks constraints through variable assignments.

Compound Predicates

Multiple constraints can be combined.

Function Preconditions

Z3 verifies function contracts.

Enabling Z3 Verification

Enable with largo build --verify or in Largo.toml.

What Z3 Can Prove

Constraint TypeExampleZ3 Support
Integer boundsit > 0, it < 100Full
Equalityit == 5Full
Arithmeticit * 2 < 100Full
Boolean logicit > 0 and it < 10Full

In the playground, refinements are CARRIED, not checked — the proof happens at

largo build --verify. The second example below violates its own refinement on

purpose: predict what the playground does with it before you run it.

You might expect
A violated refinement crashes at runtime — the type is a guard.
LOGOS instead
Refinements are compile-time contracts for Z3 (`largo build --verify`); the playground carries the annotation and prints -5 without complaint. The proof happens before the program exists — that is the point of static verification.
See it run ↓
Refinement TypesImperative
Expected output
5 85
A Violated Refinement in the Playground (Predict First)Imperative
What will this print?
Check yourself
When are refinement types actually proven?
What does the playground do with `Let positive: Int where it > 0 be -5.`?
Refinement vs Assert — when does each catch a violation?

Have questions or feedback? Join our Discord community.

Join Discord →

35.The Proof Engine: Theorems from English

'All men are mortal; Socrates is a man.' You already know the conclusion — now make the machine PROVE it, live, through a kernel that checks every step rather than taking the prover's word.
After this section you can
  • write Theorem blocks with Given, Prove, and Proof lines
  • predict the verdict line a successful proof produces
  • distinguish the prover (searches) from the kernel (certifies)

A ## Theorem block states premises and a goal in plain English; the proof engine finds

the derivation and the CoC kernel certifies it — not merely claims it. Run the example

below and the proof happens live, right here. The classic:

## Theorem: Socrates_Is_Mortal
Given: All men are mortal.
Given: Socrates is a man.
Prove: Socrates is mortal.
Proof: By automation.
$ largo prove syllogism.lg
Proved: Socrates_Is_Mortal — kernel-certified.

## Axiom and ## Theory blocks build larger developments — whole formal theories stated

in source text — and largo sat exposes the certified SAT solver directly (DIMACS in,

DRAT proofs out).

Mental model: The kernel is a referee, not a player: the proof engine searches
for the derivation, the kernel only CHECKS it, step by minimal step. A bug in the
search wastes time; only a bug in the tiny referee could certify a falsehood — which
is exactly why the referee stays tiny. The limit: "Not proved" means the search
failed, never that the statement is false.
The Syllogism, Proved LiveTheorem
What verdict do you expect?
Check yourself
What three line-kinds make up a Theorem block?
Why does a tiny kernel make the verdict trustworthy?
Does 'Not proved' mean the statement is false?

Have questions or feedback? Join our Discord community.

Join Discord →

36.Checkpoint: Logic and Verification

After this section you can
  • combine guards, refinements, and functions across the trust ladder
  • diagnose which Part VI section to reread when a challenge stumps you

Logic mode, assertions, refinements, proofs — the trust ladder from runtime guards to

kernel-certified theorems. Three challenges blend the rungs with earlier parts.

Now you tryChallengeWrite safe_ratio(a, b) with an Assert guard against b = 0, and show safe_ratio(9, 3).
practices: Assert · function definition
Now you tryChallengeBind pct as an Int refined to 0..=100 with value 85, then print `85%` by interpolation.
practices: Z3 refinement verification · string interpolation
Now you tryChallengeWrite half(n) that Trusts n is greater than 0 (with a because), returns n / 2, and show half(10).
practices: Trust with because · function definition
Check yourself
Order the trust ladder: where do Assert, Check, refinements, and Theorems each catch problems?
In LOGOS FOL, what does an adverb like 'quickly' attach to?
Which construct requires a written justification, and what is it for?
Your refinement is violated but the playground printed the value anyway. Bug?
What does the proof engine print when the syllogism succeeds?

Have questions or feedback? Join our Discord community.

Join Discord →

Part VII: Practice

37.Complete Examples

Watch three real programs get built the way problems are actually solved: understand, plan, build, look back. None of the code is new — the craft is in the moves.
After this section you can
  • trace a complete program from problem statement to output
  • name which section supplies each tool a build uses
  • extend a finished program without breaking its shape

Complete programs, each read the way a problem is actually solved — Polya's four moves:

UNDERSTAND the problem (what exactly should print?), PLAN (which sections supply the

tools?), CARRY OUT (the build), and LOOK BACK (what generalizes, what breaks, what to

try next).

Warm-ups: Factorial, Fibonacci, Filtering

Three classics to loosen up on — recursion, recursion + a loop, and the

iterate–guard–accumulate shape you will use weekly.

Word Count

*Understand:* given words, print each distinct word with its count, first-seen order.

*Plan:* a Map for counts (Collections), a membership guard (Control Flow), destructured

iteration to report (Collections).

Look back: the count-by-key shape generalizes to any tally — errors by type,
sales by region. What breaks if the input is empty? Nothing: zero loop turns, zero
report lines. Try counting characters instead of words.

A Tiny Ledger

*Understand:* entries with labels and amounts; print each, then the total, in cents.

*Plan:* a struct for the entry (User-Defined Types), a typed empty list plus Push

(Collections), a running total (Control Flow), interpolation to report (Text).

Look back: amounts stay integer cents to dodge float dust — the same discipline
Money automates. What breaks with a negative amount? Nothing mechanical — but should
a ledger allow it? Try a credit/debit enum per entry.

A Todo List

*Understand:* seed tasks, add one, list all, then finish (pop) the newest.

*Plan:* list literals and Push/Pop (Collections), counted report via interpolation (Text).

Look back: Pop takes the NEWEST — a stack. For first-in-first-out, a Pipe
(Concurrency) is the queue shape. Try printing the remaining count after the pop.
Code Examples6 examples
FactorialImperative
Expected output
5! = 120
FibonacciImperative
Expected output
Fibonacci sequence: 0 1 1 2 3 5 8 13 21 34
Filter Positive NumbersImperative
Expected output
Positives: [5, 8, 3, 7]
Word CountImperative
Expected output
the: 2 cat: 1 sat: 1 on: 1 mat: 1
A Tiny LedgerImperative
Expected output
coffee: 450 cents book: 1200 cents total: 1650 cents
A Todo ListImperative
Expected output
todo (3): - write guide - run tests - ship it finished: ship it
Now you tryChallengeExtend Word Count: add a third "the" to the words list and print the report — predict how the first line changes.
practices: maps · Repeat for-each
Now you tryChallengeExtend the Ledger: with coffee 450, book 1200, lamp 800, total ONLY the entries above 500 cents and print `big-ticket total: 2000 cents`.
practices: struct definition · If/Otherwise
Now you tryChallengeExtend the Todo list: after finishing the newest task, also print `remaining: 2`. Careful — `length of` doesn't parse inside braces.
practices: Push and Pop · string interpolation
Check yourself
What are Polya's four moves, as this section uses them?
Why does the ledger count integer cents instead of 4.50?
Pop takes which task — and what shape does that make the list?

Have questions or feedback? Join our Discord community.

Join Discord →

38.Recipes: How Do I…?

How do I round to two decimals? Clamp to a range? Count the passing scores? Goal-first answers — each a complete program to run, adapt, and steal the shape of.
After this section you can
  • use a recipe by adapting its domain, keeping its shape
  • name which sections each recipe combines

Goal-indexed answers — each recipe is a complete program you can run and adapt. More

recipes land with every release; if a "how do I…?" you need is missing, that is a bug in

this guide.

  • Format a number to two decimals — a format spec in the interpolation braces.

Combines: Text (format specs).

  • Keep a value in range (clamp)min and max compose.

Combines: the Standard Library (min/max) + Functions.

  • Count matching items — iterate, guard with If, accumulate.

Combines: Control Flow + Collections + Text (interpolation).

Look back: every recipe is a shape, not a snippet — clamp works on any ordered
values, count-matching on any predicate. When you adapt one, change the domain first
and the shape last.
Format to Two DecimalsImperative
Expected output
19.99
Clamp a Value to a RangeImperative
Expected output
10 1 7
Count Matching ItemsImperative
Expected output
3 of 5 passed
Check yourself
Which two builtins compose into clamp, and in what order?
What is the count-matching shape?

Have questions or feedback? Join our Discord community.

Join Discord →

Part VIII: Reference

39.Quick Reference

After this section you can
  • identify the syntax for any construct in under a minute
  • translate between English and bracket spellings at a glance

Syntax Cheat Sheet

Variables:

  • Let x be 5. — Create variable
  • Set x to 10. — Change variable
  • Let x: Int be 5. — With type annotation

Control Flow:

  • If condition: ... Otherwise: — Conditional
  • While condition: — While loop
  • Repeat for item in items: — For-each loop
  • Return value. — Return from function

Functions:

  • ## To name (param: Type) -> ReturnType: — Define function

Structs:

  • A TypeName has: ... — Define struct
  • Let x be a new TypeName with field1 value1. — Create instance
  • x's field — Access field

Enums:

  • A TypeName is either: ... — Define enum
  • Inspect x: When Variant: ... — Pattern match

Primitive Types:

TypeDescriptionExamples
IntWhole numbers5, -10, 0
BoolTrue or falsetrue, false
TextStrings"Hello", ""
RealDecimals3.14, -0.5
CharSingle characterbacktick syntax
Byte8-bit unsigned42: Byte, 255: Byte

Lists (Seq):

  • [1, 2, 3] — List literal
  • item 1 of items or items[1] — Access (1-indexed)
  • Push value to items. — Add to end
  • length of items — Get length

Maps:

  • Map of K to V — Map type (key-value pairs)
  • a new Map of Text to Int — Create empty map
  • item "key" of map or map["key"] — Get value by key
  • Set item "key" of map to val. or Set map["key"] to val. — Set value

Sets:

  • Set of T — Set type (unique elements)
  • a new Set of Int — Create empty set
  • Add x to set. — Add element
  • Remove x from set. — Remove element
  • set contains x — Check membership
  • a union b — Elements in either set
  • a intersection b — Elements in both sets

Tuples:

  • (1, "two", 3.0) — Tuple literal (mixed types allowed)
  • t[1] or item 1 of t — Access (1-indexed)
  • length of t — Get tuple size

Ownership Verbs

VerbMeaning
Give x to f.Move ownership
Show x to f.Borrow (read)
Let f modify x.Mutable borrow
copy of xClone

Zones

Basic syntax:

  • Inside a zone called "Name": — 4KB default zone
  • Inside a zone called "Name" of size 2 MB: — Sized heap zone
  • Inside a zone called "Name" mapped from "file.bin": — Memory-mapped file

Size units: B, KB, MB, GB

Concurrency

Async I/O:

  • Attempt all of the following: — Concurrent async tasks (tokio::join!)

Parallel CPU:

  • Simultaneously: — Parallel computation (rayon/threads)

Tasks:

  • Launch a task to f(args). — Fire-and-forget spawn
  • Let h be Launch a task to f(args). — Spawn with handle
  • Stop h. — Abort a running task

Channels/Pipes:

  • Let p be a new Pipe of Int. — Create bounded channel
  • Send x into p. — Blocking send
  • Receive x from p. — Blocking receive
  • Try to send/receive — Non-blocking variants

Select:

  • Await the first of: — Race multiple operations
  • Receive x from p: — Channel receive branch
  • After N seconds: — Timeout branch

Distributed Types (CRDTs)

Shared Structs:

  • A Counter is Shared and has: — CRDT-enabled struct

CRDT Field Types:

  • ConvergentCount — Grow-only counter (Increase)
  • Tally — Bidirectional counter (Increase, Decrease)
  • LastWriteWins of T — Timestamp-based register (Set)
  • Divergent T — Multi-value register (Set, Resolve)
  • SharedSet of T — Add/remove set (Add, Remove, contains)
  • SharedSet (AddWins) of T — Set where add wins conflicts
  • SharedSet (RemoveWins) of T — Set where remove wins conflicts
  • SharedSequence of T — Ordered list RGA (Append)
  • CollaborativeSequence of T — Text-optimized YATA (Append)
  • SharedSequence (YATA) of T — Alternate YATA syntax
  • SharedMap from K to V — Key-value CRDT ([] access)
  • ORMap from K to V — Alternate map syntax

CRDT Operations:

  • Increase x's field by amount. — Increment counter
  • Decrease x's field by amount. — Decrement Tally
  • Set x's field to value. — Set register value
  • Resolve x's field to value. — Resolve Divergent conflict
  • Add value to x's field. — Add to SharedSet
  • Remove value from x's field. — Remove from SharedSet
  • Append value to x's field. — Append to sequence
  • Merge source into target. — Combine two CRDT instances

Persistence (Compiled Only):

  • Persistent Counter — Type with automatic journaling
  • Let x be mounted at "data.lsf". — Load/create persistent CRDT
  • Mount x at "path". — Mount statement for persistence

Network Sync:

  • Sync mutable_var on "topic". — Subscribe to GossipSub topic for auto-sync (real peers when compiled; local single-node mode in the playground)

P2P Networking

Server/Client:

  • Listen on "/ip4/0.0.0.0/tcp/8000". — Bind to address
  • Listen on "/ip4/0.0.0.0/tcp/0". — Listen on any available port
  • Connect to addr. — Dial a peer
  • Let remote be a PeerAgent at addr. — Create remote handle
  • Send msg to remote. — Transmit message

Portable Types:

  • A Message is Portable and has: — Network-serializable struct

Automatic Discovery:

  • mDNS auto-discovers peers on local network when you Listen
  • Peers are automatically connected when discovered

GossipSub (via Sync):

  • Topics broadcast state changes to all subscribers
  • Retry with exponential backoff (1s, 2s, 4s, 8s, 16s)

File Transfer:

  • FileSipper for chunked transfers (1 MB chunks)
  • FileManifest with SHA256 hashes for verification
  • Enables resumable transfers

Security

Policy Blocks:

  • ## Policy — Define security rules
  • A User is admin if... — Define a predicate
  • A User can edit the Doc if... — Define a capability

Security Enforcement:

  • Check that user is admin. — Mandatory runtime check (never optimized out)
  • Assert that x > 0. — Debug-only assertion (can be optimized out)

Logic Mode Symbols

EnglishSymbol
All
Some
and
or
not¬
if...then
can/may
must

Have questions or feedback? Join our Discord community.

Join Discord →

40.Appendix: Every Operator, Both Spellings

After this section you can
  • identify both spellings of every operator
  • choose the spelling that reads better in context, knowing they compile identically

The complete operator vocabulary — every English form and its symbolic twin. Neither

spelling is more "real"; they compile identically.

EnglishSymbolMeaning
plus+addition / Text concat
minus-subtraction
times*multiplication
divided by/division
modulo%remainder
//floor division (floors toward −∞)
**exponentiation
equals==equality
is not!=inequality
is less than<less
is greater than>greater
is at most<=≤ (chainable: 1 <= x <= 10)
is at least>=
is between a and binclusive range test
is even / is oddparity
is divisible bydivisibility
is approximatelytolerant float equality
and / or / notlogic (short-circuit)
xor^bitwise / word XOR
shifted left by / shifted right bybit shifts
followed bysequence concatenation
item N of xsxs[N]1-based access
items A through B of xsinclusive slice
x's fieldfield access
copy of xdeep copy
length of xlength
xs contains vmembership
a union b / a intersection bset algebra

Have questions or feedback? Join our Discord community.

Join Discord →

41.Appendix: The Error Message Gallery

After this section you can
  • read a Socratic diagnostic and name the fix it suggests
  • run each failing exhibit, fix it, and watch it pass

LOGOS errors teach — each states what happened, why it matters, and what to do. These

examples fail on purpose so you can read the real messages; the test suite locks

every displayed diagnostic word-for-word, so this gallery can never drift from the

compiler.

*Try it: run each one, read the message, then fix the program and watch it pass.*

Code Examples4 examples
Undefined VariableImperative
Expected error
Undefined variable: total
Type Mismatch (the Socratic form)Imperative
Expected error
This slot is typed 'Int', but the value here is 'Text'. LOGOS holds every value to its declared type — that agreement is what the later guarantees stand on. Which side is right, the annotation or the value? Change the one that is lying.
Division by ZeroImperative
Expected error
Division by zero
Non-Exhaustive MatchImperative
Expected error
Inspect has no arm for the value and no Otherwise (matches must be exhaustive)

Have questions or feedback? Join our Discord community.

Join Discord →

42.Appendix: Playground vs Compiled

After this section you can
  • identify which examples need largo build and exactly why
  • run a compiled-only example locally with largo new and largo run

Every example in this guide carries a truth badge: it either runs right here in the

browser playground, or it says exactly why it cannot. The badge, the Run button, and the

test suite all read the same declaration — and the suite executes every example on every

change, in both directions: a playground example that stops running fails the build, and

a compiled-only example that *starts* running fails the build too, until it is promoted.

This table is the complete list of examples that need the compiled runtime. Everything

not listed here runs in the playground.

ExampleSectionWhy it needs largo build
crdt-persistentDistributed Types (CRDTs)The journal runtime (Mount/Persistent) only exists in compiled Rust
crdt-sharedmapDistributed Types (CRDTs)SharedMap (OR-Map) is the one rich CRDT the playground interpreter still defers to compiled Rust
network-connectP2P NetworkingConnect dials a live relay — a real network transport the browser doesn't have
network-peer-agentP2P NetworkingPeerAgent needs the compiled networking runtime
network-distributedP2P NetworkingMount and the live relay both need the compiled runtime
escape-to-rustInteroperabilityRaw Rust blocks compile under largo build; the playground cannot interpret them
stdlib-file-readThe Standard LibraryThe native file bindings live in compiled programs
stdlib-sha3The Standard LibraryThe crypto substrate's native kernels live in compiled programs

To run one: copy the code into a largo new project and largo run.

Have questions or feedback? Join our Discord community.

Join Discord →