---
slug: 01-counter-your-priors
title: Counter your priors
description: The places where Synsema differs from what an LLM (or a developer) assumes by default — each one verified against the engine.
example_ids: [priors]
---

# Counter your priors

No LLM was trained on Synsema, so a model writing Synsema falls back on habits from other
languages. These are the differences that bite. **Every claim here is verified against the
engine** (the ones below are a passing doctest):

> Coming from **Python** specifically? Start with
> [Python → Synsema — the translation table](/en/0.6.x/01a-python-to-synsema): it maps the
> Python reflexes (`def`/`return`, f-strings, comprehensions, `try/except`, `d.get`) to the
> Synsema forms in one place. This page collects the rest of the engine-verified gotchas.

```synsema
-- Doc example: places where Synsema differs from what a fresh LLM assumes.
-- Every claim below is a passing assertion against the engine (doctested).
intent: "doc example: counter-your-priors gotchas"
require secret("API_KEY")

print("1 / 2 = " + text(1 / 2) + "   (division is always float)")

test "division is ALWAYS float (1/2 is 0.5, not 0)"
    assert_eq(1 / 2, 0.5)
    assert_eq(type_of(1 / 2), "number")

test "there is no has(); use contains() for map keys"
    let m be {"a": 1, "b": 2}
    assert(contains(m, "a"))
    assert(not contains(m, "z"))

test "concatenating text + secret redacts the WHOLE string (prefix absorbed)"
    let k be secret("API_KEY", "sk-real-123")
    assert_eq(type_of("Bearer " + k), "secret")
    assert_eq(text("Bearer " + k), "secret(API_KEY)")

test "property access uses `of` (or dot)"
    let cfg be {"host": "localhost", "port": 8080}
    assert_eq(host of cfg, "localhost")
```

## More gotchas (verified)

- **`log` is a statement, not a function.** Write `log "message"`, not `log("message")` —
  the function form parses with `check` but crashes at runtime (`Undefined variable: log`).
- **`random()` / `random_int()` need `require random`** even under `run` — it is **not**
  auto-granted (random is for tokens/nonces, so it's deny-by-default).
- **`apply` order is not a trap anymore** — the intentional family (`apply`/`where`/
  `transform`/`reduce`/`sort_by`/`group_by`/`find_first`/`every`/`some`/`count_where`/
  `zip_with`) accepts **both** `(fn, list, …)` and `(list, fn, …)`; two tasks or two
  lists where one-and-one is expected is an explicit error, never a guess. `index_of`
  returns **`nothing`** when absent (not -1 — check `when idx != nothing`).
- **`a of b.c` reads as `a of (b.c)`** — for `(a of b).c`, bind first: `let x be a of b`,
  then `x.c` (the runtime error carries this hint now). And `request`/`query`/`params`
  exist only inside route handlers — pass them to helper tasks as parameters.
- **Agent memory must be DECLARED** — `remember`/`recall`/rules/progress fail with
  `Capability not granted: memory` unless the program has `require memory("<name>")` at the
  top (not auto-granted, even under `run`: it writes files). The declared name — not the
  filename — keys the `.db`; see [Memory & state](/en/0.6.x/61-memory).
- **`recall(...)` is newest-first** — `[0]` is the most recent entry — and truncates to 200
  results (pass a 5th argument to raise the limit). Inside an `agent`, `recall()` reads only
  that agent's own entries by default — cross namespaces with `recall(from = "other")` or
  `from = "*"`.
- **LLM ops take a subject keyword:** `reason about X`, `decide between [...] given X`,
  `analyze X for "..."`, `generate "..." given X`. A bare `reason "literal"` only became
  valid in a recent fix.
- **A `:param` route silently drops a `.md` / `.json` suffix** (it's content negotiation).
  To detect the suffix, read `path of request` — the param itself is already stripped.
- **Declared routes always beat `static` mounts.** With wildcard routes like
  `GET /:lang/:version`, a `static "/assets"` mount is never reached — serve assets with a
  declared `GET /assets/*path` route instead.
- **Division is always float:** `1 / 2` is `0.5`, and `type_of(1 / 2)` is `"number"`.
- **`print` buffers under `run`** — for a live REPL use `flush()` / `read_line`. Under
  `serve`, `print`/`log` reach the terminal with a `[serve]` prefix.
- **The lexer rejects a UTF-8 BOM**, and a `use "..."` import **cannot escape its directory**
  (`../x.syn` is denied).

> If a model keeps generating one of these wrong, copy this page into its context — it's
> exactly the prior that needs correcting.
