Skip to content

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.

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.

Make the directory and the source file:

Terminal window
mkdir -p hello/src

Now 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 out

A few things to notice, because they are load-bearing:

  • struct, not class. Sema has no classes and no inheritance; you model data with struct and enum, and shared behavior with traits.
  • !{} on every function. That effect row is a proof that greeting and main perform no model calls and no I/O — the deterministic core.
  • Bindings are immutable (role, users, lines, out). Use mut x = … when you need to rebind.
  • if … else is an expression ("admin" if u.admin else "member"), and [greeting(u) for u in users] is a comprehension.

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:

Terminal window
sema check hello

A 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.

sema run executes main():

Terminal window
sema run hello

You should see:

Hello, Ada (admin)!
Hello, Grace (member)!
Hello, Linus (member)!

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 out

semantic.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.

Terminal window
sema check hello
sema run hello

The output now ends with:

Interests: ['databases', 'compilers']

That is the loop you will use for everything: edit → sema checksema 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.

  • Toolchain — every command, including sema assure for verification and sema doc for 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.