Apéndice C · Tabla de operadores y precedencia
Todos los operadores del lenguaje, con su precedencia y
asociatividad. Es referencia rápida: cuando dudas cómo parsea
a | f |> g, lo consultas y sigues.
La precedencia está numerada de más alta (1, aprieta primero) a más baja (8, aprieta último). Cada nivel se resuelve contra el nivel inmediatamente superior.
C.1 Tabla principal
| Nivel | Operadores | Asociatividad |
|---|---|---|
| 1 | llamada f(args), campo ., índice [i] | Postfija |
| 2 | unario -, not | Prefija |
| 3 | *, /, //, % | Izquierda |
| 4 | +, - (binarios), ++ (concat de strings) | Izquierda |
| 5 | ?? (default de Option) | Derecha |
| 6 | ==, !=, <, >, <=, >= | No-asociativa |
| 7 | and | Izquierda (corto) |
| 8 | or | Izquierda (corto) |
| 9 | |>, |, ||, |?, |! | Izquierda |
Detalles por nivel
- Nivel 1 (postfijos). Se asocian por izquierda:
a.b.csignifica(a.b).c;f(x)(y)significa(f(x))(y). - Nivel 2 (prefijos). Unario
-xynot x. Aplican antes que cualquier operador binario. - Nivel 3 (multiplicativos).
*y/sobreIntyReal,//es división entera,%es resto. Asociación izquierda:a / b / ces(a / b) / c. - Nivel 4 (aditivos).
+y-binarios sobre números,++para concatenar strings. - Nivel 5 (
??). El default de unOption:o ?? des el valor que traeocuando esSome, ydcuando esNone. Asocia a derecha, así quea ?? b ?? cesa ?? (b ?? c), y el lado derecho corre solo en el casoNone. - Nivel 6 (comparaciones). No se encadenan.
a < b < ces error de sintaxis; se escribea < b and b < c. Así desaparece la clase de bugs donde1 == 1 == trueparsea inesperadamente. - Nivel 7 (
and) y 8 (or). Lógicos con corto-circuito.andaprieta más fuerte queor, así quea or b and cesa or (b and c). - Nivel 9 (pipes). Cinco operadores al mismo nivel:
|>(apply),|(map),||(flat-map),|?(filter) y|!(each). Todos asocian a izquierda:xs | f |> ges(xs | f) |> g.|!devuelveUnit, así que cierra el pipeline y ningún pipe puede seguirlo.
C.2 Operadores que no son operadores
Algunas formas que en otros lenguajes parecen operadores son, en kaikai, construcciones de sintaxis aparte:
=(asignación delet): parte de la sintaxis delet, no es un operador.let x = expres una declaración.:=(escritura a celda mutable): parte de la sintaxis devar.nombre := expres la forma azucarada deState.set(nombre, expr). No participa en la tabla de precedencia.&expry@r(celdasRef[T]):&varma una celda convadentro y@rla lee. Son prefijos, no participan en la tabla. La celda de unvares otra cosa: se lee pelada, por su nombre.;(separador de expresiones): separa dos expresiones escritas en la misma línea. El salto de línea hace lo mismo, así que casi nunca lo necesitas; al final de una línea es inofensivo y no descarta el valor de la expresión (§3.7).->(enmatch, en lambdas, enhandle): separador sintáctico, no operador./(en firma de efecto): separa el tipo de retorno de la fila de efectos:fn f() : Int / Log. Solo aparece en posición de firma.!(propagación deOption/Result): postfijo sobre expresiones que devuelvenOption[T]oResult[E, T], hace propagación temprana del error o elNone. Equivale más o menos al?de Rust. No es un operador binario.?y?nombre(holes): marcadores de agujero, no operadores....(spread): aparece en literales de record y de lista ([h, ...t],Punto { ...p, x: 10 }); también en patrones. No es operador.<>(anotación de unidad de medida):1.50<USD>,Real<EUR>. La unidad va entre los paréntesis angulares; no son los operadores<y>.
C.3 Equivalencias útiles
Algunos operadores son azúcar para llamadas a funciones del stdlib. Las equivalencias te ayudan cuando el operador no parece funcionar o cuando quieres ver el mecanismo de abajo.
| Operador | Equivale a |
|---|---|
| `x | > f` |
| `x | > f(_, b)` |
| `xs | f` |
| `xs | |
| `xs | ? p` |
| `xs | ! f` |
a ++ b | string_concat(a, b) (cuando son String) |
a[i] | Mutable.array_get(a, i) |
a[i] := v | Mutable.array_set(a, i, v) |
&v / @r | celda Ref[T]: armar y leer |
nombre := v | State.set(nombre, v) (con var) |
C.4 Recordatorio: comparación no asociativa
La regla del nivel 5 merece énfasis aparte: es la que más sorprende si vienes de otros lenguajes.
En la mayoría de los lenguajes:
1 == 1 == 1 # Python: false (1 == 1 → true, true == 1 → false)
1 < 2 < 3 # C: 1 (1 < 2 → 1, 1 < 3 → 1; no es lo que parece)
En kaikai, ninguna de las dos compila. El compilador exige que escribas la intención explícitamente:
1 == 1 and 1 == 1 # claramente: dos comparaciones
1 < 2 and 2 < 3 # rango: cada lado se compara explícito
Así desaparece una clase pequeña pero molesta de bugs. Si vienes de Python y te molesta escribir más, la regla es la misma de Pascal, Eiffel y SQL. No es una rareza de kaikai.