Fal#

Un lenguaje de programación completo escrito íntegramente en español y sin un solo símbolo raro: 42 palabras, 93 funciones, y todo se lee en voz alta.

Se reparte como un único ejecutable que no necesita nada instalado. Para ponerlo en marcha, mira LEEME.md.

tipo Circulo hereda de Figura con radio
    funcion area
        devuelve redondea con (pi por radio de mi por radio de mi) y 2
    fin
fin

figuras es lista con (nuevo Circulo con 3) y (nuevo Cuadrado con 6)
escribe ordena con figuras y funcion area_de

Cómo usarlo#

fal ejemplos/avanzado.fal

Sin argumentos abre la consola interactiva:

fal

Para comprobar que todo sigue bien después de tocar algo:

fal --probar

Y para saber qué versión tienes delante, que es lo primero que hace falta cuando algo no va como dice la documentación:

fal --version

Qué hay en la carpeta#

dist/El ejecutable, uno por sistema. Es todo lo que necesitas.
LEEME.mdCómo instalarlo y usarlo
pruebas/14 pruebas con su salida esperada. fal --probar
ejemplos/Programas de ejemplo, de lo básico a lo avanzado
editor/Resaltado de sintaxis para VS Code. fal --editor
*.goEl código fuente del intérprete

Ejemplos#

ArchivoQué enseña
hola.falLo mínimo: variables, condiciones, repetir
dibujo.falLa tortuga: un bucle que en vez de escribir, dibuja
mueve.falMoverse por un tablero con las flechas, usando tecla
listas.falListas, bucles, funciones
adivina.falUn juego con pregunta y mientras
completo.falRecorrido por lo básico del lenguaje
agenda.falPrograma real: objetos + archivos + menú
avanzado.falClausuras, herencia, JSON, fechas, patrones, internet
snake.falEl juego de la serpiente: tecla, limpia, comparte, listas
gastos.falPrograma de verdad: CSV, diccionarios y dinero exacto
cifrado.falCifrado Cesar, y como se rompe probando las 25 claves
romanos.falNumeros romanos en los dos sentidos, con autocomprobacion
hanoi.falTorres de Hanoi: una funcion que se llama a si misma

Las 8 reglas de diseño#

  1. Todo en español. Con tildes o sin ellas, mayúsculas o minúsculas: FUNCIÓN, funcion y Función son la misma palabra.
  2. Cero símbolos. No hay ; {} == != && + > .. Solo palabras y paréntesis.
  3. Una sola forma de hacer cada cosa.
  4. Todo se llama igual. nombre de dato o nombre con dato1 y dato2. Da igual si es una función tuya, una integrada o el campo de un objeto.
  5. Los bloques cierran con fin. La sangría es decorativa.
  6. Una línea, una instrucción.
  7. Se cuenta desde el 1. El primero es el 1, el último es el menos 1.
  8. Los errores enseñan. Dicen la línea, el camino que siguió el programa, y cómo se arregla.

Lo que cuesta la regla 2#

Quitar los símbolos no sale gratis y conviene decirlo. En cuanto una expresión crece, escribirla con palabras es más largo y más ruidoso:

3 mas 4 por 5        frente a        3 + 4 * 5

La apuesta es consciente: se gana en las primeras horas y se paga después. El = contra el == se ha llevado por delante a muchísima gente que empezaba, y las llaves y el punto y coma otro tanto. A cambio, quien ya sabe programar escribe más de lo que escribiría en otro sitio.

Y hay una segunda pega, que es real: estos símbolos son convención en casi todos los lenguajes, en la documentación y en las matemáticas, así que aquí no se practican. Por eso Fal está pensado para dejarlo atrás y no para quedarse, y por eso el manual acaba con una tabla que traduce cada cosa a Python y JavaScript. Ver Si vienes de otro lenguaje.


Lo básico#

Guardar valores#

edad es 25
nombre es "Ana"
precio es 19.99
mayor es verdadero
sin_datos es nada

Los valores son: números, textos, verdadero/falso, nada, listas, diccionarios, conjuntos, objetos y funciones.

Escribir y preguntar#

escribe "Hola mundo"
escribe "Cargando" sin salto      # se queda en la misma línea
escribe                            # una línea en blanco

nombre es pregunta "¿Cómo te llamas?"
edad es numero de pregunta "¿Cuántos años tienes?"

pregunta siempre devuelve texto; conviértelo con numero de.

Cuentas#

EscribesSignifica
3 mas 47
10 menos 46
3 por 412
10 entre 25
17 resto 52 (lo que sobra)

Los decimales son exactos. Esta es la trampa clásica de la que Fal se libra:

escribe 0.1 mas 0.2              → 0.3          (en Python: 0.30000000000000004)
escribe (0.1 mas 0.2) es 0.3     → verdadero    (en Python: False)
escribe 19.99 por 3              → 59.97        exacto, sirve para dinero

mas también une textos, listas y diccionarios. Los paréntesis funcionan como en el colegio: (2 mas 3) por 4 da 20.

Comparar#

EscribesSignifica
a es bson iguales
a no es igual a bson distintos
a es mayor que ba > b
a es menor o igual que ba <= b
a esta en ba está dentro de b
a no esta en ba no está dentro de b

Para combinar: y, o, no.

Comentarios#

# El programa ignora todo lo que va después de #

Controlar el programa#

si edad es mayor o igual que 18
    escribe "Puedes pasar"
si no si edad es mayor que 15
    escribe "Casi"
si no
    escribe "Todavía no"
fin
repite 3 veces ... fin
para cada n desde 1 hasta 10 ... fin
para cada n desde 10 hasta 1 de 2 ... fin      # hacia atrás, de 2 en 2
para cada x en coleccion ... fin               # listas, textos, diccionarios, conjuntos
mientras condicion ... fin

Dentro de un bucle: detente lo corta, continua salta a la vuelta siguiente.


Datos#

Listas#

frutas es lista con "manzana" y "pera" y "uva"
vacia es lista vacia

escribe largo de frutas
escribe elemento 1 de frutas
escribe elemento menos 1 de frutas    # el último
escribe primero de frutas
escribe ultimo de frutas

agrega "kiwi" a frutas
elemento 2 de frutas es "plátano"
quita 3 de frutas

Diccionarios#

edades es diccionario vacio
elemento "ana" de edades es 25

escribe elemento "ana" de edades
escribe claves de edades
escribe valores de edades
quita "ana" de edades

precios es diccionario con "pan" y 1.20 y "leche" y 0.99    # por parejas

Recorrer un diccionario te da las claves.

Conjuntos#

Una bolsa sin repetidos y sin orden.

a es conjunto con 1 y 2 y 3 y 2      # queda {1, 2, 3}
b es conjunto con 3 y 4
vacio es conjunto vacio
distintas es conjunto de una_lista   # quita los repetidos de una lista

escribe union con a y b              # {1, 2, 3, 4}
escribe interseccion con a y b       # {3}
escribe diferencia con a y b         # {1, 2}
agrega 9 a a

Funciones#

funcion sumar con a y b
    devuelve a mas b
fin

escribe sumar con 3 y 4
escribe sumar de 3 con 4      # lo mismo

Las funciones son datos#

Se pueden guardar, pasar y devolver como cualquier otro valor.

f es funcion sumar                    # una que ya existe
g es funcion mayusculas               # también las integradas

triple es funcion con n               # una sin nombre, escrita ahí mismo
    devuelve n por 3
fin
escribe triple con 7

Y una función puede recordar dónde nació (una clausura):

funcion contador_desde con inicio
    cuenta es inicio
    devuelve funcion
        comparte cuenta
        cuenta es cuenta mas 1
        devuelve cuenta
    fin
fin

siguiente es contador_desde con 100
escribe siguiente     → 101
escribe siguiente     → 102

La regla: un nombre a secas que apunta a una función sin datos se entiende como "llámala". Si quieres la función en sí, escribe funcion <nombre>.

Trabajar con listas de golpe#

escribe mapa con numeros y funcion con n
    devuelve n por n
fin

escribe filtra con numeros y funcion con n
    devuelve n resto 2 es 0
fin

escribe reduce con numeros y funcion con a y b
    devuelve a mas b
fin

escribe ordena con gente y funcion edad_de        # ordena por lo que diga la función
escribe cuenta_si con numeros y funcion es_par
escribe alguno con numeros y funcion es_par
escribe todos con numeros y funcion es_par

Ámbito: las funciones no pisan tus variables#

Lo que guardas dentro de una función se queda dentro:

total es "esto no se toca"
funcion suma con l
    total es 0            # crea SU total, no toca el de fuera
    ...
fin

Si de verdad quieres tocar la de fuera, hay que pedirlo:

funcion sube
    comparte contador
    contador es contador mas 1
fin

comparte vale tanto para una variable de arriba del todo como para la de la función que te envuelve. En otros lenguajes hacen falta dos palabras (global y nonlocal); aquí una.


Objetos#

tipo Perro con nombre y edad
    funcion ladra
        escribe nombre de mi mas " dice guau"
    fin
fin

toby es nuevo Perro con "Toby" y 3
escribe nombre de toby         # leer un campo
nombre de toby es "Tobías"     # cambiar un campo
ladra de toby                  # llamar a una función suya

Dentro de un tipo, mi es el propio objeto: nombre de mi.

Herencia#

tipo Animal con nombre
    funcion habla
        devuelve "..."
    fin
    funcion presenta
        devuelve nombre de mi mas " dice " mas habla de mi
    fin
fin

tipo Perro hereda de Animal
    funcion habla
        devuelve "guau"
    fin
fin

presenta de un_perro usa la versión heredada, pero llama al habla del perro.

padre llama a la versión del tipo del que heredas:

funcion presenta
    devuelve (presenta de padre) mas " y es verde"
fin

Dos funciones con nombre especial#

crea es el constructor: si existe, manda ella sobre cómo se rellena el objeto.

tipo Loro hereda de Animal con colores
    funcion crea con n y c
        nombre de mi es n
        colores de mi es c
    fin
fin

texto decide cómo se ve el objeto al escribirlo.

funcion texto
    devuelve "el loro " mas nombre de mi
fin

Cuando algo falla#

intenta
    edad es numero de pregunta "¿Tu edad?"
si falla de valor
    escribe "Eso no era un número"
si falla de matematica o archivo
    escribe "Otro problema: " mas error
si falla
    escribe "Cualquier otra cosa"
finalmente
    escribe "esto se hace siempre, falle o no"
fin

error se puede escribir tal cual (sale el mensaje) o mirarle las piezas: mensaje de error, clase de error, linea de error, pista de error.

relanza vuelve a lanzar el mismo error para que lo cace alguien de fuera.

Para provocar uno tú: falla "no se puede dividir entre cero".

Las 9 clases de error#

ClaseCuándo
matematicadividir entre cero, raíz de un negativo
valorun dato con la forma equivocada
tiposumar un texto con un número
nombreusar algo que no existe
archivono se pudo leer o escribir
redfallo de internet
programalos que lanzas tú con falla
sintaxisel programa está mal escrito
limitedemasiadas llamadas anidadas

Los errores dicen por dónde pasaron#

  X  No se puede dividir entre cero.

     linea 5 |  devuelve x entre 0

     Se llego aqui asi:
       dentro de b, llamada en la linea 2  |  devuelve b con x
       dentro de a, llamada en la linea 7  |  escribe a con 10

     Pista: Comprueba el valor antes de dividir.

Archivos#

graba con "notas.txt" y "hola\nadiós"
anexa con "notas.txt" y "\notra línea"
escribe lee de "notas.txt"
para cada l en lineas de "notas.txt" ... fin
escribe existe de "notas.txt"
borra de "notas.txt"
escribe archivos de "."

Las rutas se buscan al lado de tu programa.


Varios archivos#

usa "utiles.fal"                  # mezcla todo con lo tuyo
usa "matematicas.fal" como mate   # lo deja en su propia caja

Con como, nada choca: dos módulos pueden tener una función doble y tú otra distinta. Se usan igual que un objeto: doble de mate con 21.


Todo lo que trae puesto (93 funciones)#

Todas se llaman igual: nombre de dato o nombre con dato1 y dato2.

Textos#

largo · mayusculas · minusculas · capitaliza · recorta · parte · une · reemplaza · posicion · trozo · invierte · empieza · acaba · repetido · contiene · cuenta · numerico · formato

parte de "a,b,c" con ","                     → [a, b, c]
une de lista con " | "
reemplaza de frase con "viejo" y "nuevo"
trozo de "abcdefg" con 2 y 4                 → bcd   (ambos incluidos)
formato de "Hola {}, tienes {}" con nombre y edad

Números#

numero · texto · redondea · arriba · abajo · absoluto · raiz · potencia · minimo · maximo · azar · seno · coseno · tangente · logaritmo

redondea de pi con 4        → 3.1416
azar                        → decimal entre 0 y 1
azar de 6                   → entero de 1 a 6
azar entre 10 y 20
potencia con 10 y (menos 2) → 0.01   exacto, no aproximado

Constantes: pi y e.

Ángulos y logaritmos#

Los ángulos van en grados, igual que en gira: una vuelta son 360. Así la trigonometría y la tortuga hablan el mismo idioma y no hay que convertir nada.

escribe seno de 30           → 0.5
escribe coseno de 60         → 0.5
escribe tangente de 45       → 1
escribe coseno de 45         → 0.7071067811865476

Donde el resultado es un número redondo sale exacto; donde es irracional sale aproximado, igual que pasa con raiz. La tangente de 90 no existe y te lo dice.

Con seno y coseno ya se puede dibujar un círculo poniendo los puntos uno a uno, o una onda:

para cada g desde 0 hasta 360
    x es g
    y es 50 por seno de g
fin

logaritmo es en base 10, el del colegio. Para otra base se le dice cuál:

escribe logaritmo de 1000    → 3
escribe logaritmo con 8 y 2  → 3
escribe logaritmo con 100 y e   → el logaritmo natural

Listas#

largo · primero · ultimo · contiene · posicion · cuenta · trozo · ordena · invierte · unicos · copia · inserta · suma · promedio · elige · mezcla · rango · mapa · filtra · reduce · cuenta_si · alguno · todos

Diccionarios y conjuntos#

largo · claves · valores · contiene · copia · conjunto · union · interseccion · diferencia

Archivos#

lee · lineas · graba · anexa · existe · borra · archivos

JSON#

texto es a_json de dato          # a texto JSON
texto es a_json con dato y verdadero   # bonito, con saltos de línea
dato es de_json de texto         # de vuelta

Fechas#

Son textos con la forma "2026-07-31".

ahora · hoy · anio · mes · dia · nombremes · diasemana · dias · avanza · fecha

escribe diasemana de "2026-07-31"        → viernes
escribe dias con "2026-01-01" y hoy      → cuántos días han pasado
escribe avanza con hoy y 45              → la fecha 45 días después
escribe fecha con 2026 y 2 y 14          → 2026-02-14

Patrones#

coincide · busca · cambia

coincide con correo y "^[a-z]+@[a-z]+\.[a-z]+$"
busca con texto y "[0-9]{9}"              → todos los que encajan
cambia con texto y "[0-9]{4}" y "AAAA"

Tablas (CSV)#

filas es tabla de texto_csv       # lista de listas
escribe a_csv de filas

Internet#

pagina es pide de "example.com"
pagina es pide con url y 5        # con 5 segundos de espera máxima

Tiempo y sistema#

reloj · espera · argumentos · termina

Pantalla y azar reproducible#

FunciónQué hace
limpiaborra la pantalla, para poder repintar en el sitio
semilla de nfija el azar: el programa hará siempre lo mismo

limpia es lo que permite hacer juegos que se redibujan, como ejemplos/snake.fal.

semilla sirve para dos cosas muy prácticas: repetir una partida exactamente igual mientras arreglas algo, y poder probar un programa que usa azar. Sin ella, cada ejecución sale distinta y no hay forma de comprobar nada:

semilla de 7
escribe azar entre 1 y 100      # siempre sale lo mismo

Teclas#

FunciónQué hace
teclala tecla que se acaba de pulsar, o "" si ninguna

tecla no espera a nadie: mira si hay algo pulsado, contesta y el programa sigue. Eso es justo lo que hace falta para un juego, donde el mundo tiene que moverse aunque tú no toques nada. pregunta es lo contrario: se para hasta que escribes una línea entera y le das a Enter. Una sirve para pedir datos, la otra para jugar.

Las letras llegan tal cual y en minúscula. Las demás teclas tienen nombre en castellano: arriba, abajo, izquierda, derecha, escape, intro y espacio.

mientras jugando
    t es tecla
    si t es "arriba"
        fila es fila menos 1
    si no si t es "escape"
        jugando es falso
    fin
    espera de 0.05
fin

Esa espera del final no es un adorno. Sin ella el programa da vueltas a lo tonto comiéndose el procesador, y en el navegador además es el momento en el que entran las teclas que has pulsado.

Está en ejemplos/mueve.fal y, ya en serio, en ejemplos/snake.fal.

Una partida escrita de antemano#

Cuando el programa no tiene una terminal delante (porque le llega el texto por una tubería, o porque lo está ejecutando una prueba), tecla saca las pulsaciones de ese mismo texto, una por letra. Así una partida se puede escribir antes y repetirla igual:

fal ejemplos/snake.fal < partida.txt

Eso es lo que permite probar un juego, que si no habría que jugarlo a mano cada vez. Junto con semilla, una partida entera se vuelve repetible al detalle.

Dibujar: la tortuga#

La tortuga es un lápiz que se arrastra por la pantalla y deja raya. Empieza en el centro mirando hacia arriba.

FunciónQué hace
camina de navanza n pasos hacia donde mira, dejando raya
gira de ntuerce n grados a la derecha
levantalevanta el lápiz: se mueve sin pintar
apoyavuelve a apoyarlo
color de "azul"cambia el color de lo que venga después

Los colores son negro, blanco, gris, rojo, naranja, amarillo, verde, azul, cian, morado, rosa y marron.

No hay palabras para retroceder ni para girar a la izquierda: se hace con un número negativo, que entre paréntesis porque detrás de un de no cabe un menos n suelto.

camina de (menos 40)      # hacia atrás
gira de (menos 90)        # a la izquierda

Y aquí está la gracia: es el mismo bucle de siempre, pero dibujando.

repite 36 veces
    camina de 100
    gira de 170
fin

Eso son cuatro líneas y sale una estrella de 36 puntas. En el navegador el dibujo aparece debajo del texto; desde la terminal se guarda en un .svg con el mismo nombre que tu programa. El tamaño se ajusta solo a lo que hayas dibujado, así que da igual que uses números grandes o pequeños.

Está en ejemplos/dibujo.fal.


Palabras reservadas (42)#

agrega como comparte con continua de detente devuelve entre es escribe esta falla falso fin finalmente funcion hereda igual intenta mas menos mi mientras nada no nuevo o padre para por pregunta quita relanza repite resto retorna si sino tipo usa verdadero

Y nueve palabras del lenguaje que puedes usar como variables#

y veces hasta mayor menor que cada en desde

Son palabras del lenguaje, pero no están prohibidas como nombres. Y no hay que elegir: la misma palabra puede ser las dos cosas en la misma línea.

veces es 2
repite veces veces ... fin           # repite <la variable veces> veces

hasta es 3
para cada n desde 1 hasta hasta      # hasta <la variable hasta>

mayor es 5
si 9 es mayor que mayor ... fin

en es lista con 3
escribe 3 esta en en

para cada cada en lista ... fin

y es 5
escribe y por 2                      # 10
tipo Punto con x y y                 # campos x e y, sin renombrar nada
escribe lista con y y y              # [valor de y, valor de y]

Funciona porque las dos cosas nunca caen en el mismo sitio. Como palabra del lenguaje van siempre detrás de algo (repite 3 veces, es mayor que, desde 1 hasta 10); como nombre, una variable va siempre donde se espera un valor. El armador las distingue por la posición, así que no hace falta prohibirlas.

Esto importa más de lo que parece: veces, mayor y hasta son nombres de variable normalísimos, y en casi cualquier lenguaje tendrías que renombrarlos por culpa de la gramática.

Solo hay un sitio donde tienes que echar una mano. si x es mayor se puede entender de dos formas, y gana la comparación:

si x es mayor            # entiende "x es mayor que...", y falta el que
si x es (mayor)          # con paréntesis, compara x con la variable mayor

Pasa únicamente con mayor y menor, y el error te lo recuerda.

Las 93 funciones integradas tampoco son palabras reservadas: puedes tener una variable llamada numero, lista o suma sin problema.


La única regla rara#

Cuando encadenas varios de, el con se lo queda el primero, que es como se lee en voz alta:

parte de recorta de frase con " "

"parte, de (recorta de frase), con espacio". Correcto.

Si querías lo contrario, hacen falta paréntesis:

primero de (parte de "a,b,c" con ",")

Si te equivocas, el error te enseña dónde ponerlos.


Si vienes de otro lenguaje#

En otros lenguajesEn Fal
x = 5 / x == 5x es 5
x != 5x no es igual a 5
a + b / a % ba mas b / a resto b
a && b / `a \\b / !a`a y b / a o b / no a
x in listax esta en lista
print(x) / input()escribe x / pregunta
for i in range(1,11)para cada i desde 1 hasta 10
break / continuedetente / continua
def f(a, b): / return xfuncion f con a y b / devuelve x
lambda n: n*2funcion con nfin
map / filter / sorted(key=)mapa / filtra / ordena con
global / nonlocalcomparte (las dos)
lista[0] / lista[-1]elemento 1 de lista / elemento menos 1 de lista
len(x) / x.append(y)largo de x / agrega y a x
dic["k"] = velemento "k" de dic es v
obj.campo / obj.metodo(a)campo de obj / metodo de obj con a
class C(P): / self / super()tipo C hereda de P / mi / padre
__init__ / __str__funcion crea / funcion texto
try/except/finally / raiseintenta / si falla / finalmente / falla
import m / import m as nusa "m.fal" / usa "m.fal" como n
set() / `\ / & / -`conjunto / union / interseccion / diferencia
json.dumps / json.loadsa_json / de_json
re.search / re.findall / re.subcoincide / busca / cambia
} o indentaciónfin

Cómo está hecho por dentro#

Fal está escrito en Go, sin ninguna librería de terceros, y se reparte como un único ejecutable: no hace falta instalar nada para usarlo. Por dentro son cuatro partes:

  1. El lector (lector.go): parte el texto en piezas.
  2. El armador (armador*.go): junta las piezas en un árbol de instrucciones.
  3. El ejecutor (interprete.go, ejecutar.go, evaluar.go): recorre el árbol y hace lo que dice.
  4. La consola (main.go): ejecuta archivos y presenta los errores.

Los números exactos viven aparte, en numero.go: un número es un entero rápido mientras quepa, una fracción exacta cuando tiene decimales, y solo se vuelve aproximado si no queda más remedio (una raíz cuadrada, por ejemplo).

Para añadir una función al lenguaje basta una entrada en la tabla de biblioteca.go. No hay que tocar ni el lector ni el armador:

integrada("dobla", 1, 1, func(in *Interprete, a []Valor, ln int) (Valor, *ErrorFal) {
    n, err := pideNum(a[0], ln, "dobla")
    if err != nil {
        return nil, err
    }
    return multiplicaNum(n, Entero(2)), nil
})

Y ya puedes escribir escribe dobla de 21. Después, fal --editor actualiza el coloreado del editor solo.

Para añadir una instrucción (una palabra que va al principio de una línea) hay que tocar dos sitios: Armador.instruccion en armador_ins.go y Interprete.ejecutar en ejecutar.go.


Lo que sigue faltando#

Para ser honestos, esto es lo que Fal todavía no tiene frente a Python:

unas 8,6 veces más lento que Python nativo descontando el arranque (antes eran 55). Bajar de ahí es compilar a bytecode, y es el problema que menos molesta en la práctica.

y no necesita nada instalado; lo que no sabe todavía es meter tu .fal dentro de uno para que lo puedas repartir.

Dicho eso, esa lista compara Fal con Python, que no es a lo que aspira. Lo que de verdad importa es otra cosa: ser el escalón que falta entre Scratch y un lenguaje de adulto. Medido con esa vara, lo que queda por hacer es esto:

sí haría falta, y no es lo mismo que un depurador con puntos de parada.