kaikai
  • Empezar
  • Ejemplos
  • El nombre
  • El libro
  • Ecosistema
  • Comunidad
EN

Quickstart

Ejemplos

Los programas del quickstart, en orden de lectura. Cada uno cabe en una pantalla y muestra una idea del lenguaje.

01 Hola, mundo 02 FizzBuzz 03 Calculadora 04 Efectos 05 Concurrencia 06 Pipes 07 Unidades de medida 08 Kinds 09 Contratos
01

Hola, mundo

El punto de entrada y el efecto de salida estándar, con handler por defecto.

01_hello.kai
fn main() {
  println("Hello, kaikai")
}
Salida
$ kai run 01_hello.kaiHello, kaikai
02

FizzBuzz

Tipos suma, guardas en el match y un pipeline sobre un literal de rango.

02_fizzbuzz.kai
type Tag
  = Ambos
  | Fizz
  | Buzz
  | Otro(Int)

# Las guardas permiten que cada rama lleve su propia condición, así la
# cascada de `if / else if` se colapsa dentro del match.
fn clasificar(n: Int) : Tag = match n {
  n if n % 15 == 0 -> Ambos
  n if n % 3 == 0  -> Fizz
  n if n % 5 == 0  -> Buzz
  n                -> Otro(n)
}

fn etiqueta(c: Tag) : String = match c {
  Ambos    -> "FizzBuzz"
  Fizz     -> "Fizz"
  Buzz     -> "Buzz"
  # Interpolación de strings: `#{expr}` renderiza cualquier valor
  # mostrable.
  Otro(n)  -> "#{n}"
}

fn main() {
  [1..15]
    | clasificar              # map: [Int] -> [Tag]
    | etiqueta                # map: [Tag] -> [String]
    |> list.foreach(println)  # apply: imprime cada línea
}
Salida
$ kai run 02_fizzbuzz.kai1
2
Fizz
4
Buzz
...
FizzBuzz
03

Calculadora

Un tipo suma recursivo como AST, recorrido con calce de patrones.

03_calculator.kai
type Expr
  = Lit(Int)
  | Suma(Expr, Expr)
  | Mult(Expr, Expr)
  | Neg(Expr)

# Una función cuyo cuerpo entero es una sola expresión usa `=`: sin
# llaves y sin `return`.
fn eval(e: Expr) : Int = match e {
  Lit(n)     -> n
  Suma(l, r) -> eval(l) + eval(r)
  Mult(l, r) -> eval(l) * eval(r)
  Neg(x)     -> -eval(x)
}

fn mostrar(e: Expr) : String = match e {
  Lit(n)     -> "#{n}"
  Suma(l, r) -> "#{mostrar(l)} + #{mostrar(r)}"
  Mult(l, r) -> "#{mostrar(l)} * #{mostrar(r)}"
  Neg(x)     -> "-#{mostrar(x)}"
}

fn main() {
  let e = Suma(Lit(2), Mult(Lit(3), Lit(4)))
  println("#{mostrar(e)} = #{eval(e)}")
}
Salida
$ kai run 03_calculator.kai2 + 3 * 4 = 14
04

Efectos

Un efecto propio: la función declara qué usa, quien la llama decide cómo se cumple.

04_effect.kai
effect Log {
  log(msg: String) : Unit
}

fn saludar(nombre: String) : Unit / Log {
  # `#{...}` interpola dentro del string — sin concatenación manual.
  Log.log("hello, #{nombre}")
}

fn main() {
  handle {
    saludar("kaikai")
    saludar("world")
  } with Log {
    log(msg, resume) -> {
      println("[INFO] #{msg}")
      resume(())
    }
  }
}
Salida
$ kai run 04_effect.kai[INFO] hello, kaikai
[INFO] hello, world
05

Concurrencia

Dos fibras cooperativas que ceden el control en puntos explícitos.

05_concurrent.kai
import spawn
import loop

fn trabajador(tag: String, n: Int) : Unit / Stdout + Spawn {
  loop.repeat(n) {
    println(tag)
    spawn.yield()
  }
}

fn main() {
  # Forma con lambda final: `spawn.spawn { ... }` en vez de
  # `spawn.spawn(() => ...)`.
  let f = spawn.spawn { trabajador("B", 3) }
  trabajador("A", 3)
  spawn.await(f)
}
Salida
$ KAI_THREADS=1 kai run 05_concurrent.kaiA
B
A
B
A
B
06

Pipes

Cuatro operadores para cuatro intenciones: aplicar, mapear, aplanar y filtrar.

06_pipes.kai
fn cuadrado(n: Int) : Int = n * n

fn divisores(n: Int) : [Int] = [1, n]

fn es_par(n: Int) : Bool = n % 2 == 0

fn main() {
  # `[1..4]` es un literal de rango — sin acumulador escrito a mano.
  # `[1..10..2]` agrega un paso.
  let total = [1..4]                  # [1, 2, 3, 4]
    | cuadrado                        # [1, 4, 9, 16]      (map)
    || divisores                      # [1, 1, 1, 4, 1, 9, 1, 16] (flat-map)
    |? es_par                         # [4, 16]            (filter)
    |> list.sum                       # 20                 (apply)

  println("total=#{total}")
}
Salida
$ kai run 06_pipes.kaitotal=20
07

Unidades de medida

Las unidades viven en el tipo, así que el compilador no deja mezclar monedas.

07_uom.kai
unit USD
unit EUR

fn convertir_a_usd(monto: Real<EUR>, tasa: Real<USD/EUR>) : Real<USD>
  = monto * tasa

fn main() {
  let salario   : Real<USD> = 1000.0<USD>
  let mercado   : Real<USD> =  250.0<USD>
  let comision  : Real<USD> =    5.0<USD>
  let reembolso : Real<EUR> =   91.0<EUR>
  let tasa      : Real<USD/EUR> = 1.10<USD/EUR>

  let balance = salario - mercado - comision + convertir_a_usd(reembolso, tasa)

  # 1000 - 250 - 5 + 100.1 = 845.1 USD
  println("balance=#{balance}")
}
Salida
$ kai run 07_uom.kaibalance=845.1 USD
08

Kinds

Por qué las unidades no son un caso especial, y cómo declarar un kind propio.

09_kinds.kai
unit m

# `Measure` está declarado sobre `AbelianGroup`, que tiene clausura
# multiplicativa: metros por metros es una cantidad real, así que
# `u^2` es una unidad que puedes nombrar. `[u: Measure]` es un
# parámetro de tipo común que recorre unidades en vez de tipos.
fn area[u: Measure](w: Real<u>, h: Real<u>) : Real<u^2> = w * h

# Como las unidades son solo un kind, la maquinaria queda abierta:
# declara el tuyo. Los puntos de experiencia se suman, pero `xp^2` no
# significa nada, así que este kind se declara sobre `Module`: un
# grupo aditivo SIN clausura multiplicativa. Una cantidad es escalar o
# lleva exactamente un habitante, y `fn sq(a: Real<xp>) : Real<xp^2>`
# se rechaza justo donde se escribe la unidad:
#
#   error: unit `xp^2` does not exist: `Points` habitants have no
#          products or powers
#
# Misma maquinaria que `Measure`, otra teoría, otra álgebra.
kind Points : Module with points
points xp

fn ganar(a: Real<xp>, b: Real<xp>) : Real<xp> = a + b

fn main() {
  let piso    = area(3.0<m>, 4.0<m>)
  let puntaje = ganar(120.0<xp>, 45.0<xp>)

  # Habitantes de kinds distintos tampoco unifican nunca: `3.0<m> +
  # 4.0<xp>` es un error de tipo. Tu kind no puede filtrarse en el de
  # nadie más.
  println("piso=#{piso}")
  println("puntaje=#{puntaje}")
}
Salida
$ kai run 09_kinds.kaipiso=12 m^2
puntaje=165 xp
09

Contratos

Precondiciones y postcondiciones que viven en la firma de la función.

08_contracts.kai
fn dividir(a: Int, b: Int) : Int
  requires b != 0
  ensures  result * b == a
{
  a / b
}

# Valor absoluto: la postcondición garantiza un resultado no negativo
# y de la misma magnitud que el argumento.
fn abs(n: Int) : Int
  ensures result >= 0
  ensures result == n or result == -n
{
  if n >= 0 { n } else { -n }
}

fn main() {
  println("#{dividir(10, 2)}")   # 5
  println("#{abs(-3)}")          # 3
}
Salida
$ kai run 08_contracts.kai5
3
kaikai

Lenguaje de programación.

Por Eduardo Díaz.

  • Empezar
  • Ejemplos
  • El nombre
  • El libro
  • Ecosistema
  • Comunidad
  • GitHub · kaikai
  • GitHub · kaikai-book
  • lnds.net