Your First Twin

In this chapter you will write the smallest Osmol program that does anything: two twins, one fact, one gap, and a single flow between them. Along the way you meet most of the moving parts of the language, and you notice also the one part that never appears.

If you have not installed the interpreter yet, start with Installation.

A program with no main

An Osmol program has no entry point, because nothing in it executes. Axiom II of the specification says it plainly: programs are claims, not commands. Every statement is a declarative speech act: an assertion, a gap, a commitment, a decision, an expression, or a permission. There are no loops, no sequencing, no control flow, and the order of declarations never matters.

This takes a moment to trust. In every language you have used, line 3 runs before line 4. In Osmol, line 3 and line 4 are both simply true, the way two rows of a database are both true. You can shuffle every declaration in the program of this chapter and the result will not change by one character. The program does not run; it settles. The mesh solves the declared state for equilibrium, the way water finds one level across connected tanks.

So instead of asking "what does this program do?", we ask the Osmol question: what does each participant claim, and what follows from those claims together?

The smallest mesh that flows

Save this as first.osmol:

-- first.osmol — the smallest mesh that flows
twin ana {
  hold weather(berlin) = rainy
  bond ben { trust high }

  membrane {
    weather(*) -> family: exact
  }
}

twin ben {
  seek weather(berlin)
  bond ana { trust high }
}

Five constructs, each with a full reference page in Part II:

  • twin blocks are the compilation unit, one per participant. Everything a twin declares is about itself; the grammar cannot say anything about the insides of anyone else.
  • hold is an assertion: ana claims she possesses a value for the fact weather(berlin). Assertions are the mesh's supply side.
  • seek is a gap: a first-class record that ben lacks this value and has standing interest in it. Gaps are the demand side, and the only thing that can attract flow. This one has no deadline, so it simply stands.
  • membrane is ana's permission layer. The rule weather(*) -> family: exact says: facts matching weather(*) may reach my family circle at full precision. In v0.1, family means any party the sender bonds with trust high, which is why ana bonds ben.
  • bond declares trust. Ben's bond toward ana does two things on his side: it sets the trust weight of anything arriving from her, and it classifies her as family for his attention threshold (default 0.3, see The Pressure Function).

Notice what ben did not write. He never named ana as the source of the weather. He declared an absence, and that is all.

Run it

$ python3 -m osmol first.osmol
OSMOL v0.1 - reference interpreter
parsed first.osmol: 2 twins, 0 sends (no such construct exists)
initial gapcount: 1   (Theorem 1 bound: equilibrium in <= 1 flows)
t1  ana.weather(berlin) --exact--> ben   P=1.00 (R1.0*U1.0*T1.0*M1.0) > theta[family]=0.3   [silent]

EQUILIBRIUM after 1 flows (Theorem 1 bound 1: respected)
messages composed by humans: 0 (the grammar has no verb for it)
interruptions: 0
open gaps: 0 — silence is a fixpoint

Read the trace from the top. The header counts one open gap in the whole mesh, and Theorem 1 (machine-checked) guarantees equilibrium within that many flows. The interpreter asserts this bound at runtime.

The t1 line is a flow firing, with its full pressure arithmetic printed: relevance R, urgency U, trust T, and membrane cast M multiply to a pressure of 1.00, which clears ben's family-class threshold of 0.3. The membrane cast was exact, so ben now holds weather(berlin) = rainy at full precision. The placement is [silent]: ben's twin simply knows now; nothing renders, nothing buzzes. Reading the Trace walks every field in detail.

Then the footer: equilibrium, zero human messages, zero interruptions, zero open gaps. Silence is a fixpoint.

Remove the membrane rule

Delete ana's membrane block entirely and run again:

initial gapcount: 1   (Theorem 1 bound: equilibrium in <= 1 flows)

EQUILIBRIUM after 0 flows (Theorem 1 bound 1: respected)
messages composed by humans: 0 (the grammar has no verb for it)
interruptions: 0
open gap [ben]: weather(berlin)

No flow. Ben's gap stays open, and the mesh reports it honestly at the footer.

This is the protective default of v0.1, visible in the source of the interpreter: when no membrane rule matches a fact, membrane_cast returns None, and no rule means no flow. Nothing ana holds can leave her twin unless a rule of hers grants passage, and nothing arrives at ben that no permission produced. Silence is not an error state here; it is the correct equilibrium of these particular claims. If you want the flow back, ana must say so in her own grammar.

What you did not write

Look back at first.osmol and count the absences.

You wrote no send. There is no such verb; try one and you get error[O-000] before anything else is even parsed.

Also no address. Ben never routed a request to ana; he declared a gap, and the mesh found the holder who was willing.

And no schedule: no polling loop, no retry logic, no delivery callback. The pressure function evaluated, one flow fired, and the mesh settled.

Two twins declared what was true about themselves, and communication happened as a consequence, that is, as the resolution of pressure between declared states. That is the whole language, in miniature. The next chapter, The Dinner Mesh, scales this up to the canonical two-twin program with deadlines, commitments, a human decision, and the human lane.