Primeros pasos
Contra tus instintos
Ningún LLM fue entrenado en Synsema, así que un modelo que escribe Synsema recae en hábitos de otros lenguajes. Estas son las diferencias que muerden. Cada afirmación acá está verificada contra el engine (las de abajo son un doctest que pasa):
¿Venís de Python en particular? Empezá por Python → Synsema — la tabla de traducción: mapea los reflejos de Python (def/return, f-strings, comprehensions,try/except,d.get) a las formas Synsema en un solo lugar. Esta página junta el resto de los gotchas verificados.
-- 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")Más gotchas (verificados)§
loges una sentencia, no una función. Escribílog "mensaje", nolog("mensaje")—
la forma de función parsea con check pero revienta en runtime (Undefined variable: log).
random()/random_int()necesitanrequire randomincluso bajorun— no se
auto-otorga (random es para tokens/nonces, así que es deny-by-default).
- El orden de
applyya no es una trampa — la familia intencional (apply/where/
transform/reduce/sort_by/group_by/find_first/every/some/count_where/ zip_with) acepta ambos órdenes (fn, lista, …) y (lista, fn, …); dos tasks o dos listas donde se esperaba una-y-una es error explícito, jamás una adivinanza. index_of devuelve nothing si no está (no -1 — chequeá when idx != nothing).
a of b.cse lee comoa of (b.c)— para(a of b).c, bindeá primero: `let x be
a of b, después x.c (el error de runtime ahora trae este hint). Y request/query/ params` existen solo dentro de los route handlers — pasalos a las tasks auxiliares como parámetros.
- La memoria del agente se DECLARA —
remember/recall/reglas/progreso fallan con
Capability not granted: memory salvo que el programa tenga require memory("<name>") al tope (no se auto-otorga, ni bajo run: escribe archivos). El nombre declarado — no el del archivo — keyea el .db; ver Memoria y estado.
recall(...)es más-nuevo-primero —[0]es la entrada más reciente — y trunca a 200
resultados (pasá un 5º argumento para subir el límite). Dentro de un agent, recall() lee solo las entradas propias de ese agente por defecto — cruzá namespaces con recall(from = "other") o from = "*".
- Las operaciones LLM llevan un sujeto:
reason about X,decide between [...] given X,
analyze X for "...", generate "..." given X. El reason "literal" pelado recién es válido tras un fix reciente.
- Una ruta
:paramse come un sufijo.md/.json(es negociación de contenido). Para
detectar el sufijo, leé path of request — el param ya viene sin él.
- Las rutas declaradas siempre ganan sobre los
staticmounts. Con rutas comodín como
GET /:lang/:version, un static "/assets" nunca se alcanza — serví los assets con una ruta declarada GET /assets/*path.
- La división es siempre float:
1 / 2es0.5, ytype_of(1 / 2)es"number". printbufferea bajorun— para un REPL en vivo usáflush()/read_line. Bajo
serve, print/log llegan a la terminal con prefijo [serve].
- El lexer rechaza el BOM UTF-8, y un
use "..."no puede escapar su directorio
(../x.syn se deniega).
Si un modelo insiste en generar mal alguno de estos, copiá esta página a su contexto — es exactamente el instinto que hay que corregir.