cv.cm

Programación · Lenguajes de programación

Tutorial de OCaml para principiantes

7 min de lectura

Respuesta corta

OCaml es un lenguaje funcional de tipado estático de la familia ML. Su compilador infiere los tipos por ti, detecta muchos errores antes de ejecutar el programa y produce código nativo rápido. Los programas se construyen con valores inmutables, variantes (uniones etiquetadas), coincidencia de patrones y módulos. Instálalo con opam, ejecuta un archivo con ocaml hello.ml o compílalo con ocamlopt.

Qué es OCaml y quién lo usa

OCaml se creó en Inria, en Francia, y hoy lo desarrolla una comunidad de código abierto que incluye empresas como Jane Street y Tarides. Combina la programación funcional con características imperativas opcionales (referencias mutables, arreglos, bucles) y un sistema de objetos que la mayor parte del código no necesita. OCaml 5 añadió paralelismo de memoria compartida mediante dominios y manejadores de efectos.

OCaml se usa en compiladores y herramientas de análisis de programas, asistentes de demostración como Rocq (antes Coq), sistemas de trading (Jane Street es el usuario industrial más conocido) y herramientas para desarrolladores como Flow e Infer de Meta. También es un lenguaje habitual para enseñar programación funcional y sistemas de tipos.

Instala OCaml y ejecuta Hola, mundo

La forma estándar de instalar OCaml es opam, el gestor de paquetes de OCaml. Tras instalar opam, ejecuta opam init una vez; configura un compilador. Después instala la herramienta de compilación dune y el REPL más amigable utop con opam install dune utop.

(* hello.ml *)
let () = print_endline "Hello, world!"
ocaml hello.ml            # run with the interpreter
ocamlopt -o hello hello.ml
./hello                   # native executable
# Hello, world!

let () = ... es la forma habitual de escribir el punto de entrada: evalúa la expresión y comprueba que devuelve unit (sin valor). En el REPL (utop u ocaml), termina cada frase con ;;; en los archivos de código fuente no necesitas ;;. Para proyectos reales, dune init project myapp crea un proyecto y dune build lo compila.

Valores y tipos básicos

TipoEjemploNotas
int4263 bits en sistemas de 64 bits; operadores + - * / mod
float3.14Tiene sus propios operadores: +. -. *. /.
string"hello"Bytes inmutables; se concatenan con ^
char'a'Un solo byte
booltrue, false&&, ||, not
unit()El valor de las expresiones sin resultado
let name = "Ana"
let age = 30
let height = 1.68 *. 100.0
let () = Printf.printf "%s is %d, %.0f cm\n" name age height
(* Ana is 30, 168 cm *)
let count = ref 0
let () = incr count; Printf.printf "count = %d\n" !count

Los enlaces son inmutables. Un ref es una celda mutable: !r la lee y r := v la escribe. OCaml nunca convierte automáticamente entre int y float; usa float_of_int e int_of_float.

Control de flujo y funciones

if es una expresión. Las funciones se definen con let; las recursivas necesitan let rec. Los argumentos se separan con espacios y todas las funciones están currificadas, así que la aplicación parcial es gratuita.

let sign n =
  if n > 0 then "positive"
  else if n < 0 then "negative"
  else "zero"
let rec factorial n = if n <= 1 then 1 else n * factorial (n - 1)
let add x y = x + y          (* int -> int -> int *)
let add5 = add 5
let () =
  for i = 1 to 3 do
    Printf.printf "%d squared is %d\n" i (i * i)
  done;
  Printf.printf "%s %d %d\n" (sign (-2)) (factorial 5) (add5 10)
(* negative 120 15 *)

Fíjate en los paréntesis de sign (-2): sin ellos OCaml lee sign - 2 como una resta. Usa ; para encadenar expresiones que devuelven unit.

Listas, arreglos, tuplas y registros

EstructuraLiteralNotas
Lista[1; 2; 3]Lista simplemente enlazada inmutable; x :: xs añade por el frente
Arreglo[| 1; 2; 3 |]Tamaño fijo, mutable, indexación con a.(0)
Tupla("Ana", 30)Grupo fijo de valores de distintos tipos
Registro{ name = "Ana"; age = 30 }Campos con nombre, el tipo se declara antes
Hashtbl / MapHashtbl.create 16Tabla hash mutable / mapa de árbol balanceado inmutable
type person = { name : string; age : int }
let people = [ { name = "Ana"; age = 30 }; { name = "Ben"; age = 17 } ]
let () =
  people
  |> List.filter (fun p -> p.age >= 18)
  |> List.map (fun p -> p.name)
  |> List.iter print_endline
(* Ana *)
let older = { (List.hd people) with age = 31 }

El operador de pipeline x |> f significa f x. El módulo List ofrece map, filter, fold_left, find_opt y muchas más.

Características distintivas: variantes, coincidencia de patrones y módulos

Un tipo variante enumera todos los casos posibles de un valor. La coincidencia de patrones descompone un valor por casos, y el compilador avisa cuando falta alguno, lo que hace seguro el refactorizado.

type shape =
  | Circle of float
  | Rectangle of float * float
let area = function
  | Circle r -> Float.pi *. r *. r
  | Rectangle (w, h) -> w *. h
let () =
  List.iter (fun s -> Printf.printf "%.2f\n" (area s))
    [ Circle 1.0; Rectangle (2.0, 3.0) ]
(* 3.14 then 6.00 *)
module StringMap = Map.Make (String)
let ages = StringMap.(empty |> add "Ana" 30 |> add "Ben" 17)
let () = Printf.printf "%d\n" (StringMap.find "Ana" ages)   (* 30 *)

Los módulos agrupan tipos y funciones, y cada archivo es un módulo (list.ml sería List). Un functor es una función de módulos a módulos: Map.Make (String) construye un módulo de mapa cuyas claves son cadenas. Las firmas (archivos .mli) ocultan los detalles de implementación.

Manejo de errores

OCaml tiene tres herramientas: option para un valor que puede faltar, result para una operación que puede fallar con información y las excepciones para situaciones excepcionales.

let parse_age s =
  match int_of_string_opt s with
  | Some n when n >= 0 -> Ok n
  | Some _ -> Error "Age must not be negative"
  | None -> Error "Not a number"
let () =
  match parse_age "abc" with
  | Ok n -> Printf.printf "Age %d\n" n
  | Error msg -> print_endline ("Error: " ^ msg)
(* Error: Not a number *)
exception Empty_input
let first = function [] -> raise Empty_input | x :: _ -> x
let () =
  try print_int (first []) with Empty_input -> print_endline "empty list"

Un programa pequeño y completo: frecuencias de palabras

let () =
  let text = "the cat and the hat and the bat" in
  let counts = Hashtbl.create 16 in
  String.split_on_char ' ' text
  |> List.iter (fun w ->
         let n = Option.value (Hashtbl.find_opt counts w) ~default:0 in
         Hashtbl.replace counts w (n + 1));
  Hashtbl.to_seq counts
  |> List.of_seq
  |> List.sort (fun (_, a) (_, b) -> compare b a)
  |> List.filteri (fun i _ -> i < 2)
  |> List.iter (fun (w, n) -> Printf.printf "%s %d\n" w n)
(* the 3
   and 2 *)

El programa cuenta las palabras en una tabla hash mutable, convierte la tabla en una lista de pares (word, count), ordena por cantidad en orden descendente e imprime los dos primeros. ~default:0 es un argumento con etiqueta.

Siguientes pasos

  • ocaml.org: la guía de instalación, «A Tour of OCaml» y los demás tutoriales de la sección Docs.
  • El manual de OCaml, para la referencia completa del lenguaje y de la biblioteca estándar.
  • Aprende dune para compilar proyectos y opam para instalar bibliotecas.
  • Prueba el libro gratuito en línea «Real World OCaml» para ver ejemplos más grandes y prácticos.

Preguntas frecuentes

¿OCaml es difícil de aprender?

La sintaxis es pequeña, pero pensar en tipos, recursión y coincidencia de patrones requiere práctica si vienes de lenguajes imperativos. Los mensajes de error del verificador de tipos se vuelven pronto más fáciles de leer.

¿Por qué OCaml tiene operadores distintos para los flotantes?

OCaml no sobrecarga los operadores aritméticos, así que + funciona solo con int y +. solo con float. Esto mantiene la inferencia de tipos simple y predecible.

¿Cuál es la diferencia entre OCaml y F#?

F# deriva de OCaml y comparte gran parte de su sintaxis básica, pero F# se ejecuta en .NET y usa sangría significativa, mientras que OCaml compila a código nativo o bytecode y tiene un sistema de módulos más potente con functores.

¿Cuándo necesito ;; en OCaml?

Solo en el toplevel interactivo (ocaml o utop), donde indica al REPL que evalúe lo que escribiste. En los archivos de código fuente .ml normalmente se omite.

Todos los tutoriales de programación