C5 — REPL¶
Pre-requisitos: C4 — CLI esencial
terminado. Conocés run, check, fmt, lint, dev.
Objetivo: usar fitz repl como laboratorio interactivo
con dominio completo de: prompt + auto-print, multi-line por
balanced brackets, los 6 comandos especiales (:help, :quit,
:env, :reset, :type, :load), history persistente, todos
los atajos de teclado de rustyline, y los límites conocidos
del MVP.
Por qué importa: el REPL es la herramienta exploratoria.
Es donde aprendés sintaxis nueva sin commitear nada, donde
validás "¿qué devuelve esta expresión?" sin escribir un archivo
entero, y donde debuggeás fns de tu proyecto cargándolas vivas.
Análogo a python / node / irb / iex — si venís de
cualquiera de esos, ya sabés el patrón.
Mapa del REPL¶
flowchart TD
A[fitz repl] --> B[Prompt vacío<br/>esperando input]
B --> C{¿Qué tipeás?}
C -->|Expresión| D[Evaluar + auto-print<br/>= valor]
C -->|Statement (let, fn, etc.)| E[Evaluar + persistir<br/>en el scope]
C -->|:comando| F[Comando especial<br/>:help / :env / :type / ...]
C -->|Bracket abierto sin cerrar| G[Prompt ... <br/>multi-line]
D --> B
E --> B
F --> B
G --> B
B -->|Ctrl+D o :quit| H[👋 hasta luego!]
Paso 1 — Arrancar el REPL¶
Desde cualquier carpeta (no necesita proyecto):
Sin args, no hay flags. Una sola variante:
| Comando | Qué hace |
|---|---|
fitz repl |
Arranca el REPL con scope limpio |
fitz repl --help |
Imprime ayuda |
No hay flag para precargar archivos al arrancar. Para eso usá
:load <archivo>adentro (Paso 7).
Para salir¶
Tres formas equivalentes:
| Forma | Tecla / comando |
|---|---|
| Ctrl+D | Manda EOF al stdin (Linux/macOS estándar) |
| Ctrl+Z + Enter | Equivalente en Windows |
:quit o :q |
Comando especial |
Imprime 👋 hasta luego! y termina.
Paso 2 — Expresiones y "auto-print"¶
Tipeá un literal o expresión:
El = indica valor de la expresión (REPL convention). Si el
result es Null (statements o print()), no muestra nada.
Tabla de qué imprime cada tipo¶
| Tipeás | Resultado | Por qué |
|---|---|---|
42 |
= 42 |
Int literal |
3.14 |
= 3.14 |
Float literal |
"hola" |
= hola |
Str — Display sin comillas |
true |
= true |
Bool |
null |
(nada) | Null no auto-prints |
[1, 2, 3] |
= [1, 2, 3] |
List — Display estructurado |
{"k": 1} |
= {"k": 1} |
Map — strings de keys CON comillas |
0..5 |
= 0..5 |
Range |
Ok(42) |
= Ok(42) |
Result variant |
nombre.upper() |
= PATAGONIA |
Llama el método y muestra |
print("X") |
X (sin =) |
print() es side-effect, devuelve Null |
Notá la asimetría: el REPL imprime strings sin comillas porque usa Display (igual que
print(...)). Pero ADENTRO de listas/mapas SÍ aparecen con comillas porque ahí el contexto es estructural.
Múltiples expresiones por línea¶
Cada ; es separador de statements. Solo el último valor
imprime (el primero se calcula y se descarta).
Paso 3 — Bindings persistentes en el scope¶
Cada let o fn queda en el scope hasta que cierres el
REPL o uses :reset:
(sin output — let no es expresión)
Definir fns¶
Funciones persisten igual que vars. Las podés usar después, redefinir (la nueva pisa la vieja), o combinar.
Recursividad funciona¶
Reasignación¶
Paso 4 — Multi-line automático (balanced brackets)¶
Si abrís un {, ( o [ y no lo cerrás en la misma línea, el
prompt cambia a ... y el REPL espera el cierre:
Cuando cerrás el }, el REPL procesa todo el bloque:
Heurística¶
| Carácter | Abre / cierra | Notas |
|---|---|---|
{ ... } |
Bloque o struct literal o map | Cuenta balanceada |
( ... ) |
Args de fn o agrupación | Cuenta balanceada |
[ ... ] |
List literal o indexing | Cuenta balanceada |
"..." |
String | Brackets adentro de string NO cuentan |
// ... |
Comment | Brackets adentro de comment NO cuentan |
Cancelar input multi-line¶
Si te equivocaste a mitad de multi-line y querés empezar de nuevo:
| Acción | Tecla |
|---|---|
| Cancelar línea actual (sin salir) | Ctrl+C |
| Salir del REPL entero | Ctrl+D o :quit |
Ctrl+C te deja en un prompt limpio con el scope intacto.
Limitaciones¶
| Caso | Funciona en REPL multi-line? |
|---|---|
Bloque fn { ... } |
✅ |
| Struct literal multi-línea | ✅ |
| List literal multi-línea | ✅ |
String interpolada con {} adentro |
⚠️ Cuenta brackets — strings raras pueden confundir |
String multi-línea con \n literal |
✅ |
| Comentario multi-line | ❌ (Fitz no tiene /* */) |
Paso 5 — :help y los 6 comandos especiales¶
:help o :h lista todo:
Comandos del REPL:
:help, :h — esta ayuda
:quit, :q — salir (también Ctrl+D)
:env — listar variables y fns definidas en el scope
:reset — limpiar el scope (perdés todo)
:type <expr> — mostrar el tipo de una expresión
:load <archivo> — evaluar un .fitz en el scope actual
Tabla detallada:
| Comando | Alias | Argumento | Qué hace |
|---|---|---|---|
:help |
:h |
— | Lista comandos |
:quit |
:q |
— | Sale del REPL |
:env |
— | — | Lista bindings del scope |
:reset |
— | — | Resetea scope (pierde user vars y fns) |
:type <expr> |
— | expresión | Muestra el tipo de <expr> sin evaluarla |
:load <archivo> |
— | path | Carga y evalúa un .fitz en el scope actual |
Paso 6 — :type <expr> (inspeccionar tipo)¶
Quizás el comando más útil del REPL: mostrar el tipo de una expresión sin evaluarla.
Notá que no hace falta anotar x: Int: el checker propaga el
tipo del receptor List<Int> al param del callback automáticamente
(inferencia bidireccional). Cubre tres casos canónicos:
-
Callbacks de métodos built-in con templates paramétricos (
.map/.filter/.find/.any/.all/etc. sobreList<T>): -
Args FnExpr a fns user-defined con param
Fn(...) -> ...: -
RHS FnExpr de
letcon anotaciónFn(...) -> ...:
Si el param tiene anotación explícita incompatible con el hint, la anotación gana y el checker emite el error correspondiente.
Tipos compuestos¶
Limitación conocida — :type sobre vars del REPL¶
:type corre el checker sobre un programa sintético
(no es 100% scope-aware con las vars del REPL):
Esperabas Int. El checker dentro del REPL no sabe del binding
edad. Workaround: escribilo en un .fitz:
let edad = 200
let _peek = edad // hover sobre _peek en VSCode dice Any aún, pero hover sobre edad sí dice Int
O usá fitz check archivo.fitz y leé el output.
Esta deuda está documentada — refinar
:typescope-aware es mejora prevista del REPL.
Paso 7 — :env (ver bindings actuales)¶
Listar todo lo que definiste:
Definido en el scope:
bytes = <builtin bytes> // Function
db = <module db> // Module
double = <function> // Function
nombre = "Patagonia" // Str
spawn = <builtin spawn> // Function
x = 21 // Int
Qué incluye¶
| Tipo de binding | Aparece en :env? |
|---|---|
Variables del usuario (let x = ...) |
✅ |
Funciones del usuario (fn foo()) |
✅ |
Built-ins de fn (spawn, bytes) |
✅ |
Módulos built-in (db, hash, jwt) |
✅ |
Tipos built-in (Int, Str, ...) |
❌ (no son bindings) |
Keywords (let, fn, ...) |
❌ |
El listado mezcla tus bindings con los built-ins. Ordenado alfabético. No hay flag para filtrar solo lo del usuario hoy — convención: prefijo
_para vars "internas" si querés diferenciar.
Paso 8 — :load <archivo> (cargar código del proyecto)¶
Esto es la diferencia entre el REPL como juguete y el REPL como
herramienta real. :load evalúa un .fitz adentro del scope
actual — vos podés cargar fns de tu proyecto y probarlas en
vivo.
Demo¶
Crealo en src/helpers.fitz (dentro del proyecto donde arrancaste
fitz repl):
// src/helpers.fitz
let pi = 3.14159
fn area(r: Float) -> Float => pi * r * r
fn perimetro(r: Float) -> Float => 2.0 * pi * r
Desde el REPL:
Tip cross-OS: usar paths relativos al directorio donde arrancaste el REPL (
src/helpers.fitz) es la forma más portable. Paths absolutos estilo Unix (/tmp/...) no funcionan en Windows — ahí resuelven aD:/tmp/...que casi nunca existe.
Notá que :load también ejecuta los stmts top-level (en
este caso, el let pi se evaluó). Las fns quedan disponibles:
Definido en el scope:
area = <function> // Function
bytes = <builtin bytes> // Function
db = <module db> // Module
perimetro = <function> // Function
pi = 3.14159 // Float
spawn = <builtin spawn> // Function
Reglas de path¶
| Path | Resuelve relativo a |
|---|---|
helpers.fitz |
Directorio donde arrancaste fitz repl |
./helpers.fitz |
Idem (relativo al cwd del arranque) |
/tmp/helpers.fitz (Linux/macOS) |
Absoluto |
D:/proyecto/helpers.fitz o D:\proyecto\helpers.fitz (Windows) |
Absoluto |
../otro/file.fitz |
Relativo (sube de carpeta) |
El REPL NO tiene cwd interno —
cdno funciona como comando. El path siempre se resuelve contra el directorio donde arrancastefitz repl.
Si el archivo tiene errores¶
El scope NO se modifica si hubo error (load es atómica).
Paso 9 — :reset (empezar de cero)¶
Si tu scope se ensució con experimentos y querés volver al estado inicial sin salir del REPL:
Después:
Definido en el scope:
bytes = <builtin bytes> // Function
db = <module db> // Module
spawn = <builtin spawn> // Function
Solo built-ins. Todas las vars y fns del usuario desaparecen.
Si intentás usar algo viejo:
:resetes irrecuperable — no hay undo. Si querés recuperar un script de experimentos, usáhistory(Paso 11) o lo más prolijo: escribilo en un.fitzy usá:load.
Paso 10 — History persistente¶
El REPL guarda lo que tipeás:
| OS | Path del history |
|---|---|
| Linux | ~/.fitz/history |
| macOS | ~/.fitz/history |
| Windows | %USERPROFILE%\.fitz\history (típicamente C:\Users\<tu-user>\.fitz\history) |
Persistente entre sesiones — cuando volvés a arrancar
fitz repl, tenés todo lo del REPL anterior en el history.
Atajos sobre el history¶
| Acción | Tecla |
|---|---|
| Línea anterior del history | ↑ (flecha arriba) |
| Línea siguiente del history | ↓ (flecha abajo) |
| Búsqueda incremental hacia atrás | Ctrl+R |
| Búsqueda incremental hacia adelante | Ctrl+S |
| Aceptar match y editar | Enter |
| Cancelar búsqueda | Ctrl+G |
Editar líneas (atajos rustyline / Emacs-style)¶
| Acción | Tecla |
|---|---|
| Mover al inicio de línea | Ctrl+A o Home |
| Mover al fin de línea | Ctrl+E o End |
| Avanzar una palabra | Alt+F o Ctrl+→ |
| Retroceder una palabra | Alt+B o Ctrl+← |
| Borrar hasta fin de línea | Ctrl+K |
| Borrar hasta inicio de línea | Ctrl+U |
| Borrar palabra hacia atrás | Ctrl+W |
| Limpiar pantalla (preserva scope) | Ctrl+L |
| Cancelar línea actual | Ctrl+C |
Los atajos vienen de rustyline (la lib que usa el REPL por
debajo). Si usaste bash/zsh/psql, son los mismos.
Borrar el history¶
Paso 11 — Async funciona en el REPL¶
Como el REPL corre sobre tokio, podés usar .await y sleep
directo:
(Hay pausa de 500ms, después prompt nuevo.)
(Future pendiente bindeado a f.)
(Pausa de ~1s, después prompt.)
Esto es útil para experimentar con async sin escribir un archivo
#[tokio::main]. M5 cubre async en detalle.
Paso 12 — Aplicarlo a mi-saludos¶
Volvé al proyecto y arrancá el REPL desde la raíz:
Cargá tu src/main.fitz:
(Ejecuta los print() del archivo y deja las fns/vars en
scope.)
Probá cosas vivas:
Editás tu src/main.fitz afuera, recargás:
(Re-ejecuta el archivo entero, sobreescribiendo bindings con mismo nombre.)
Workflow típico¶
flowchart LR
A[Escribir en src/main.fitz] --> B[fitz repl]
B --> C[:load src/main.fitz]
C --> D[Probar fns vivas]
D -->|Bug encontrado| A
D -->|Sospecha resuelta| E[escribir tests con @test<br/>cubriendo el caso]
Paso 13 — Limitaciones MVP del REPL (honestas)¶
| Feature | Estado |
|---|---|
| Auto-print de expresiones | ✅ |
| Multi-line por balanced brackets | ✅ |
| History persistente + búsqueda | ✅ |
:type |
✅ (limitado en scope-awareness) |
:load |
✅ |
:env |
✅ |
:reset |
✅ |
| Async | ✅ |
| Tab completion | ❌ |
| Highlighting (colores) en input | ❌ |
Manifest mode (:load relativo al proyecto) |
⚠️ Manual con path absoluto |
Importar deps desde adentro (from foo import X) |
❌ |
:reload (re-ejecutar todo el history) |
❌ |
:save <archivo> para guardar la sesión |
❌ |
Si una de estas te limita, la convención hoy es:
escribilo en un .fitz y combiná fitz repl + :load con
ese archivo.
Validación¶
-
fitz replarranca y mostrás el prompt sin errores. - Definís una var (
let x = 42) y al pedirlexte devuelve= 42. -
:type "hola"te dice:: Str. -
:loadsobre un.fitzcon una fn cualquiera te deja esa fn disponible en el scope. -
:resetborra tus bindings (al re-pedirxda error). - Multi-line: al abrir
{el prompt cambia a...y cuando cerrás procesa el bloque.
Troubleshooting¶
:load me dice "no se pudo leer el archivo"¶
- El path es relativo al directorio donde arrancaste
fitz repl, no al cwd actual (no hay cwd actual en el REPL). Si tenés dudas, usá ruta absoluta. - Verificá que el archivo exista con
lsdesde otra terminal.
:type me devuelve Any y esperaba algo concreto¶
- Limitación documentada del MVP (Paso 6). Workaround: escribilo
en un
.fitzyfitz checkahí.
Salir con Ctrl+D no funciona en Windows¶
- Usá
Ctrl+Zy después Enter. O:quit/:q.
El REPL no detecta una fn que cargué con :load¶
- Verificá que el
:loadhaya impreso✓ cargado X.fitz. Si no, hubo un error de parse/check y el archivo no se evaluó.
Multi-line se queda esperando y no procesa¶
- Probablemente hay un bracket sin cerrar que el REPL espera.
Apretá
Ctrl+Cpara cancelar y empezar de nuevo.
History no se guarda entre sesiones¶
- Verificá permisos de escritura en
~/.fitz/(o el equivalente Windows). - Si la carpeta no existe, el REPL la crea — chequeá que tenga permiso para crearla.
El REPL muestra rastros raros al tipear¶
- Algunos terminales emulados (typical:
git-bashen Windows con configs raras) no manejan bien rawmode de rustyline. Probá un terminal nativo: PowerShell, Windows Terminal, alacritty.
Lo que viene en C6¶
Para cerrar M1 nos falta una cosa: dejar de interpretar
nuestro programa y empezar a compilarlo a binario nativo.
Eso es fitz build — el diferencial de Fitz frente a
Python/JS/Ruby. Después del C6 tenés el M1 entero cerrado y un
binario standalone listo para distribuir.