Your First Program
This is a ten-minute, hands-on walkthrough. You will create a Sema project, statically check it, run it, then add one neurosymbolic construct and check again. Everything here runs on the default build — no models to download.
If you have not built the toolchain yet, do Installation first.
What a Sema project is
Section titled “What a Sema project is”A Sema project is a directory with a src/ folder. The entry point is
src/main.sema, and execution begins at its main() function. That is the whole
convention:
hello/└── src/ └── main.sema # defines main()A sema.toml manifest at the project root is optional — with no manifest you get
sensible defaults (see Project Layout). We will start
without one and add it later.
Step 1 — create the project
Section titled “Step 1 — create the project”Make the directory and the source file:
mkdir -p hello/srcNow put this in hello/src/main.sema. It is small but real — a struct, a bit
of functional style (immutable bindings, a comprehension), an f-string, and a
main that carries the deterministic effect row !{}:
"""A first Sema program — greet a few users, deterministically."""
struct User: name: str admin: bool
def greeting(u: User) -> str !{}: role = "admin" if u.admin else "member" return f"Hello, {u.name} ({role})!"
def main() -> str !{}: users = [ User(name="Ada", admin=True), User(name="Grace", admin=False), User(name="Linus", admin=False), ] lines = [greeting(u) for u in users] out = "\n".join(lines) print(out) return outA few things to notice, because they are load-bearing:
struct, notclass. Sema has no classes and no inheritance; you model data withstructandenum, and shared behavior with traits.!{}on every function. That effect row is a proof thatgreetingandmainperform no model calls and no I/O — the deterministic core.- Bindings are immutable (
role,users,lines,out). Usemut x = …when you need to rebind. if … elseis an expression ("admin" if u.admin else "member"), and[greeting(u) for u in users]is a comprehension.
Step 2 — check it
Section titled “Step 2 — check it”sema check runs the static checks — it parses, verifies arity and struct
fields, enforces the effect-row discipline, and flags any directive it does not
recognize. Run it after every edit:
sema check helloA clean run reports no diagnostics. If you mistype a field name or call a function
with the wrong number of arguments, sema check catches it here, before anything
executes.
Step 3 — run it
Section titled “Step 3 — run it”sema run executes main():
sema run helloYou should see:
Hello, Ada (admin)!Hello, Grace (member)!Hello, Linus (member)!Step 4 — add a neurosymbolic touch
Section titled “Step 4 — add a neurosymbolic touch”Now for the part that makes Sema Sema. semantic.dedup is a built-in semantic
operation: it removes near-duplicate items using calibrated similarity, not exact
string matching. On the default build it runs on the deterministic hash embedder,
so the result is reproducible.
Edit hello/src/main.sema to collapse a list of roughly-duplicate tags before
greeting. Add a helper and call it from main:
"""A first Sema program — greet a few users, then de-duplicate their interests."""
struct User: name: str admin: bool
def greeting(u: User) -> str !{}: role = "admin" if u.admin else "member" return f"Hello, {u.name} ({role})!"
def unique_tags(tags: list[str]) -> list[str] !{}: """Collapse near-duplicate tags via calibrated similarity.""" return semantic.dedup(tags, 0.9)
def main() -> str !{}: users = [ User(name="Ada", admin=True), User(name="Grace", admin=False), User(name="Linus", admin=False), ] lines = [greeting(u) for u in users]
tags = ["databases", "databases", "compilers"] interests = unique_tags(tags)
out = "\n".join(lines) + f"\nInterests: {interests}" print(out) return outsemantic.dedup(tags, 0.9) takes the list and a similarity threshold. The two
"databases" entries collapse to one, and "compilers" stays — so interests
is ["databases", "compilers"].
Note that unique_tags is still !{}. Semantic operations on the built-in
engine are part of the deterministic core; the effect row only gains
model.invoke when you cross into simulate def or a real model call.
Step 5 — check and run again
Section titled “Step 5 — check and run again”sema check hellosema run helloThe output now ends with:
Interests: ['databases', 'compilers']That is the loop you will use for everything: edit → sema check → sema run
(with SEMA_STRICT=1 while verifying). When you are ready to add contracts and
tests, sema assure grades the verification — see the toolchain page.
Where to go next
Section titled “Where to go next”- Toolchain — every command, including
sema assurefor verification andsema docfor reflected documentation. - Language Overview — the full tour of types, effects, traits, enums, generics, and pattern matching.
- Project Layout — splitting into modules, imports and
visibility,
sema.toml, and dependencies.