Primeros pasos
Python → Synsema — la tabla de traducción
Vos (un LLM, o un dev de Python) ya sabés Python. Esta página mapea el reflejo de Python a la forma Synsema y marca exactamente dónde diverge la semántica. Es más rápido que aprender de cero, y previene el modo de falla clásico: escribir Python con keywords de Synsema.
La regla que previene la mayoría de las alucinaciones: si no lo viste en estas docs, no existe. No hay import, no hay stdlib de Python, no hay clases, no hay comprehensions, no hay decoradores, no hay with, no hay generadores, no hay sintaxis de método sobre valores (xs.append(x) → los builtins son tasks planas: append(xs, x)).
Cada afirmación semántica de abajo está asegurada por este doctest que pasa:
-- Doc example: the Python → Synsema divergences that bite the hardest.
-- Every claim in the translation-table page is asserted here (doctested).
intent: "doc example: python-to-synsema translation table"
task boom()
raise("kaput")
task reraise_error()
try
boom()
recover err
raise(err)
task bad_return()
return 5
task give_none()
give None
task iterate_map_directly()
each k in {"a": 1}
print(k)
test "assignment is let/set, not = (x = 5 is a parse error)"
let x be 1
set x to 2
assert_eq(x, 2)
test "Python 'return'/'None'/'True' PARSE but are undefined names — use give/nothing/true"
assert_error(bad_return)
assert_error(give_none)
assert_eq(nothing == nothing, true)
test "try/recover (not try/except); recover SWALLOWS unless you raise(err)"
let seen be ""
try
boom()
recover err
set seen to err
assert(contains(seen, "kaput"))
assert_error(reraise_error)
test "and/or do NOT short-circuit — guard with nested when, not with `and`"
let m be {"a": 1}
assert_error(() => contains(m, "b") and m["b"] == 1)
when contains(m, "a")
assert_eq(m["a"], 1)
test "append returns a NEW list (no mutating .append); reassign with set"
let xs be [1, 2]
let ys be append(xs, 3)
assert_eq(xs, [1, 2])
assert_eq(ys, [1, 2, 3])
test "f-string equivalent is the backtick string; quoted strings stay literal"
assert_eq(`n={1 + 1}`, "n=2")
assert(contains("n={1+1}", "{"))
test "each cannot iterate a map — go through keys()"
assert_error(iterate_map_directly)
let ks be []
each k in keys({"x": 1, "y": 2})
set ks to append(ks, k)
assert_eq(ks, ["x", "y"])
test "comprehension equivalent: apply + where; slicing is slice()"
assert_eq(apply((x) => x * 2, where([1, 2, 3], (x) => x > 1)), [4, 6])
assert_eq(slice([10, 20, 30, 40], 1, 3), [20, 30])
assert_eq(slice([10, 20, 30, 40], -2, 4), [30, 40])
test "the `in` operator is contains(); on maps it checks KEYS"
assert(contains([1, 2], 2))
assert(contains("abc", "b"))
assert(contains({"a": 1}, "a"))
test "text + number CONCATENATES (unlike Python); text * n does not exist"
assert_eq("a" + 1, "a1")
assert_error(() => "ab" * 2)
test "len/str/sorted are length/text/sort_by; d.get(k, default) does not exist"
assert_eq(length("abc"), 3)
assert_eq(text(42), "42")
assert_eq(sort_by([3, 1, 2], (x) => x), [1, 2, 3])
assert_error(() => get({"a": 1}, "a"))
assert_error(() => {"a": 1}["b"])
test "enumerate gives {index, item} maps (Python's for i, x in enumerate)"
let e be enumerate(["a", "b"])
assert_eq(e[0].index, 0)
assert_eq(e[1].item, "b")
assert_eq(length(enumerate([])), 0)Reflejos de sintaxis§
| En Python | En Synsema | ⚠️ Divergencia |
|---|---|---|
x = 5 … x = 6 | let x be 5 … set x to 6 | x = 5 → error de parseo Unexpected token: ASSIGN ('='). = existe SOLO en params con default / args nombrados: task f(x, y = 1), f(x, y = 2) |
# comentario | -- comentario | # → Unexpected character: '#' |
if / elif / else: | when / otherwise when / otherwise (sin dos puntos) | un : al final → error de parseo; elif no es una palabra |
x if c else y | when c then x otherwise y | forma expresión inline, usable en let/args |
for x in xs: | each x in xs | for → error de parseo |
for k in un_dict: | each k in keys(m) | each no puede iterar un map: Cannot iterate over map. Pasá por keys(m)/values(m) |
for i, x in enumerate(xs): | each e in enumerate(xs) … e.index / e.item | enumerate(list) → [{index, item}, …] |
while c: | while c | mismo keyword, sin dos puntos; un loop desbocado corta con Loop exceeded maximum iterations |
def f(x): return v | task f(x) … give v | def → error de parseo. return PARSEA como un nombre y falla en runtime: Undefined variable: 'return' — la palabra es give |
lambda x: x + 1 | (x) => x + 1 | — |
None / True / False | nothing / true / false | las formas con mayúscula parsean y después fallan: Undefined variable: 'None' (igual True/False) |
x is None | x == nothing | no hay operador is de identidad (is es de match) |
f"n={n}" | ` n={n} ` (string con backticks) | f"..." → error de parseo. Las strings con comillas "..." NO interpolan ("{n}" queda literal) y un newline literal adentro es Unterminated string — los backticks hacen ambas cosas |
"""multilínea""" | ` multilínea ` | los backticks permiten newlines reales + {expr} |
[f(x) for x in xs if p(x)] | apply(f, where(xs, p)) | la sintaxis de comprehension → error de parseo |
xs[1:3], xs[-2:] | slice(xs, 1, 3), slice(xs, -2, length(xs)) | [1:3] → error de parseo; slice acepta negativos estilo Python, funciona en listas/text/bytes |
x in xs (operador) | contains(xs, x) | in solo vale dentro de each. En maps contains chequea CLAVES |
try/except E as e: | try … recover err | except → error de parseo. err es el TEXTO del mensaje (no hay tipos/jerarquía de excepciones). recover TRAGA por defecto — re-propagá con raise(err) |
raise ValueError("x") | raise("x") (o la sentencia raise "x") | un solo tipo de error; en engine ≤ v0.5.1 usá la forma con paréntesis |
import json, import requests | nada que importar — los builtins son globales | import x parsea como un nombre y falla: Undefined variable: 'import'. JSON/HTTP/etc. son builtins gateados por capabilities (abajo) |
from mimodulo import f | use "./mimodulo.syn" as m … m.f() | solo módulos .syn locales; los exports necesitan export — Módulos |
class Person: | type Person (campos) + tasks planas | sin métodos/herencia/self; construí Person("Alice", 30), accedé p.name / name of p / p["name"] |
match/case | match … is patrón | los brazos usan is, el default es otherwise — Sintaxis |
Además: las palabras LLM reason / decide / analyze / generate son reservadas en todas partes (incluso como nombres de member/param) — let reason be 1 → 'reason' is a reserved word in Synsema. Nombrá las cosas resolve, why, etc.
Equivalentes de builtins (los métodos son tasks planas)§
| En Python | En Synsema |
|---|---|
len(x) | length(x) (text/list/map/bytes/array) |
str(x) / int(s) / float(s) | text(x) / number(s) (siempre float; floor() para entero) |
xs.append(x) (muta) | append(xs, x) → devuelve una lista NUEVA; reasigná: set xs to append(xs, x) |
s.upper() / s.lower() / s.strip() | upper(s) / lower(s) / trim(s) |
s.split(",") / ",".join(xs) | split(s, ",") / join(xs, ",") |
s.startswith(p) / s.replace(a, b) | starts_with(s, p) / replace_text(s, a, b) |
sorted(xs, key=f) / reverse=True | sort_by(xs, f) / sort_by(xs, (x) => 0 - x) (no hay sort pelado) |
sum(xs) / min(xs) / max(xs) | sum(xs) / min(xs) / max(xs) (también variádico max(a, b, c)) |
map(f, xs) / filter(p, xs) | apply(f, xs) / where(xs, p) — ambos aceptan cualquiera de los dos órdenes |
functools.reduce(f, xs, init) | reduce(xs, f, init) |
xs.index(v) (lanza) / v in xs | index_of(xs, v) → nothing si no está (no -1, no error) |
d.get(k, default) | no existe — when contains(m, "k") y después indexá (when anidado, ver trampas) |
d.keys() / d.values() / d.items() | keys(m) / values(m) / no hay items — iterá keys(m) e indexá |
json.dumps(x) / json.loads(s) | json_encode(x) / json_decode(s) (puros, sin import) — JSON |
range(n) | range(n) → una lista real (también range(a, b, paso)) |
print(...) | print(...) (bufferea bajo run hasta salir — flush() para salida en vivo) |
re.fullmatch / re.findall | matches(s, pat) (match COMPLETO) / find_all(s, pat) — Builtins |
open(p).read() / requests.get(url) | read_file(p) + require file(...) / fetch(url) + require net(host) — Archivos, HTTP |
Trampas semánticas — parece Python, se comporta distinto§
| Parece que | Lo que pasa en realidad (doctesteado arriba) |
|---|---|
a and b cortocircuita | NO cortocircuita — ambos lados se evalúan SIEMPRE. contains(m, "k") and m["k"] == 1 igual falla con Map has no key 'k' cuando la clave no está. Guardá con when anidado |
xs.append muta in place | append (y familia) devuelven valores nuevos; el original queda intacto. Reasigná con set |
d["missing"] → KeyError que atrapás por tipo | Map has no key 'missing' — atrapable solo como try/recover (texto del mensaje) |
"a" + 1 → TypeError | Concatena: "a" + 1 → "a1" (text + number coerciona). Pero "ab" * 2 y 1 + true SÍ son errores — no hay repetición ni aritmética de bools |
except: deja el programa muriendo | recover traga el error entero (la task termina normal). Para fallar hacia arriba, raise(err) dentro de recover |
| iterar un dict da las claves | each sobre un map es ERROR — usá keys(m) |
Más trampas (también verificadas contra el engine): Contra tus instintos y Errores y exit codes.
Dónde tu intuición de Python SÍ es segura (verificado — confiá)§
- La división siempre devuelve float (
10 / 3→3.33…), como Python 3. División entera:floor(a / b). round()es redondeo banker's, igual que Python:round(2.5)→2,round(3.5)→4.- Truthiness:
nothing/false/0/""/[]/{}son falsy, todo lo demás truthy. [1] + [2]→[1, 2](concatenación de listas),sliceacepta índices negativos.- Los literales de map
{"k": v}y de lista se ven y anidan como dicts/listas. - La indentación define bloques (4 espacios), comentarios hasta fin de línea,
and/or/notson palabras.
Sin equivalente en Python — leé la página del tema antes de usar§
- Capabilities: la I/O es deny-by-default; declará
require net("host")/file(...)/
db(...) / serve(PUERTO) / llm arriba o las llamadas fallan → Capabilities
- Ops LLM como keywords:
decide between [...] given x,generate,analyze,reason→ Primitivas LLM - Agentes/concurrencia:
agent/spawn/share/observe/signal/wait_for,
parallel_map → Multi-agente
- Servidor HTTP como sintaxis:
serve on 8080+ bloquesroute "GET /x"→ Serve - Secretos: valores
secret("KEY")que jamás se imprimen/serializan → Secretos - Humano en el loop:
approve/confirm/ask/show→ Humano en el loop - Tests en el archivo: bloques
test "nombre"+assert_eq, corridos porsynsema test→ CLI