Paradigma Lógico · UNAL 2026-1
Programación lógico-funcional con tipos estáticos,
modos e inferencia de determinismo.
01 — Primeros Pasos
Mercury es un lenguaje de programación lógico-funcional diseñado para aplicaciones de alta confiabilidad. Combina la expresividad de Prolog con un sistema de tipos estático, análisis de modos e inferencia de determinismo. A diferencia de Prolog, Mercury está compilado y produce código de máquina eficiente.
Fue desarrollado en la Universidad de Melbourne a mediados de los 90 y sigue en desarrollo activo. Es ideal para sistemas donde la corrección es crítica: compiladores, verificación formal, inteligencia artificial simbólica.
Basado en predicados de primer orden. Cada predicado describe una relación, no una instrucción imperativa.
Sistema de tipos fuerte y estático. Los errores de tipo se detectan en compilación, nunca en ejecución.
Compila a C, Java o C#. Rendimiento comparable a lenguajes imperativos compilados como C.
El compilador verifica si un predicado siempre tiene solución, puede fallar, o puede producir múltiples resultados.
Mercury está disponible para Linux (recomendado), macOS y Windows. En Ubuntu/Debian:
sudo apt install wget ca-certificates
cd /tmp && wget https://paul.bone.id.au/paul.asc
sudo cp paul.asc /etc/apt/trusted.gpg.d/paulbone.asc
# Reemplazar "focal" con tu distro: jammy, bookworm, etc.
echo "deb http://dl.mercurylang.org/deb/ focal main" | sudo tee -a /etc/apt/sources.list
sudo apt update && sudo apt install mercury-recommended
# Compilar (genera ejecutable + archivos intermedios)
mmc hello.m && ./hello
# Compilar limpio (recomendado, solo el ejecutable)
mmc --make hello && ./hello
¿Sin instalación? Usa Glot.io — un editor Mercury online gratuito. Cada ejemplo de este tutorial tiene un botón directo.
02 — Tour del Lenguaje
Mercury tiene una sintaxis basada en Prolog, pero con adiciones fundamentales para tipos, modos y determinismo. Todo programa es una colección de módulos.
:- module mi_modulo. % 1. Nombre del módulo (coincide con el archivo .m)
:- interface. % 2. INTERFAZ: lo que es público
:- import_module io. % Importar módulo de E/S
:- pred main(io::di, io::uo) is det. % Declarar predicado principal
:- implementation. % 3. IMPLEMENTACIÓN: código privado
main(!IO) :-
io.write_string("Hola desde Mercury!\n", !IO).
| Tipo | Descripción | Ejemplo |
|---|---|---|
| int | Entero de precisión nativa | 42, -7, 0 |
| float | Punto flotante doble | 3.14, -2.5e10 |
| string | Cadena de caracteres UTF-8 | "hola mundo" |
| char | Carácter Unicode | 'a', 'ñ', '\n' |
| bool | Booleano | yes, no |
| list(T) | Lista polimórfica homogénea | [1,2,3], ["a","b"] |
Cada predicado declara su comportamiento. El compilador verifica que la declaración sea correcta — esto elimina errores en tiempo de ejecución:
| Declaración | Soluciones posibles | ¿Puede fallar? | Uso típico |
|---|---|---|---|
| det | Exactamente 1 | No | Funciones, main |
| semidet | 0 ó 1 | Sí | Búsquedas, checks |
| multi | 1 ó más | No | Generadores |
| nondet | 0 ó más | Sí | Backtracking general |
| failure | Siempre 0 | Siempre | Casos imposibles |
| erroneous | Nunca retorna | — | error/1, throw |
% in = ya instanciado (entrada)
% out = no instanciado (salida)
% di = estado destruible (E/S entrada)
% uo = estado único (E/S salida)
:- pred suma(int::in, int::in, int::out) is det.
suma(X, Y, Z) :- Z = X + Y.
% Azúcar sintáctico: func declara in/in -> out automáticamente
:- func suma_f(int, int) = int.
suma_f(X, Y) = X + Y.
% Predicado semidet: puede fallar si X no está en la lista
:- pred miembro(T::in, list(T)::in) is semidet.
miembro(X, [X|_]).
miembro(X, [_|T]) :- miembro(X, T).
03 — Particularidades
Mercury comparte la filosofía declarativa de Prolog, pero añade garantías estáticas que lo hacen apto para software de producción de alta integridad.
Los predicados son puros por defecto. Los efectos secundarios son explícitos: se modela como paso de estado único de E/S.
Mercury no tiene el operador cut de Prolog. El control de flujo se maneja con el sistema de determinismo — más predecible y seguro.
Gestión automática de memoria con GC de Boehm. Sin manejo manual de memoria.
Sistema de módulos con interfaces separadas. Facilita encapsulación y desarrollo de librerías reutilizables.
Compila a C, C#, Java o Erlang. Un mismo código, múltiples plataformas y entornos.
El compilador genera HTML de documentación a partir de comentarios especiales (%>) en el código.
La E/S en Mercury es pura y explícita. En lugar de efectos secundarios ocultos, se pasa un token de estado !IO que garantiza la secuencialidad y la pureza referencial:
% !IO es azucar sintactico para IO0::di, IO::uo
% Cada llamada consume el estado anterior y produce uno nuevo.
% El compilador garantiza que no se use el mismo estado dos veces.
main(!IO) :-
io.write_string("Linea 1\n", !IO), % consume IO0, produce IO1
io.write_string("Linea 2\n", !IO), % consume IO1, produce IO2
io.write_string("Linea 3\n", !IO). % consume IO2, produce IO3
% Nota: !IO expandido es:
% main(IO0, IO3) :-
% io.write_string("Linea 1\n", IO0, IO1),
% io.write_string("Linea 2\n", IO1, IO2),
% io.write_string("Linea 3\n", IO2, IO3).
% Tipo algebraico: arbol binario polimórfico
:- type arbol(T)
---> hoja
; nodo(arbol(T), T, arbol(T)).
% Pattern matching exhaustivo (el compilador verifica)
:- func altura(arbol(T)) = int.
altura(hoja) = 0.
altura(nodo(Izq, _, Der)) =
1 + int.max(altura(Izq), altura(Der)).
:- func contar(arbol(T)) = int.
contar(hoja) = 0.
contar(nodo(Izq, _, Der)) =
1 + contar(Izq) + contar(Der).
04 — Ejemplos Básicos
Los ejemplos básicos cubren los conceptos fundamentales. Haz clic en ▶ Glot.io para ejecutarlos directamente en el navegador.
El programa más simple posible. Observa la estructura obligatoria: módulo → interfaz → implementación.
:- module hello.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
main(!IO) :-
io.write_string("Hello, World!\n", !IO).
Función factorial recursiva usando func. El compilador verifica que los casos sean exhaustivos y el determinismo sea det.
:- module factorial.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int.
:- func factorial(int) = int.
factorial(0) = 1.
factorial(N) = N * factorial(N - 1) :- N > 0.
main(!IO) :-
io.format("factorial(0) = %d\n", [i(factorial(0))], !IO),
io.format("factorial(5) = %d\n", [i(factorial(5))], !IO),
io.format("factorial(10) = %d\n", [i(factorial(10))], !IO).
La secuencia de Fibonacci con múltiples cláusulas. Nota la simetría con la definición matemática.
:- module fibonacci.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int.
:- func fib(int) = int.
fib(0) = 0.
fib(1) = 1.
fib(N) = fib(N - 1) + fib(N - 2) :- N > 1.
main(!IO) :-
io.format("fib(0) = %d\n", [i(fib(0))], !IO),
io.format("fib(1) = %d\n", [i(fib(1))], !IO),
io.format("fib(5) = %d\n", [i(fib(5))], !IO),
io.format("fib(10) = %d\n", [i(fib(10))], !IO),
io.format("fib(15) = %d\n", [i(fib(15))], !IO).
Mercury infiere el tipo de cada variable desde su primer uso — no puede cambiar ni reasignarse. Este ejemplo muestra los cuatro tipos primitivos fundamentales: int, float, string y list.
:- module variables.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int, float, string, list.
main(!IO) :-
% Declaración de variables de distintos tipos.
% El tipo se infiere del primer uso — no puede cambiar.
IntVar = 42,
FloatVar = 3.14,
StringVar = "Hola, Mercury",
ListVar = [1, 2, 3, 4, 5],
io.write_string("Valor de IntVar: ", !IO),
io.write_int(IntVar, !IO),
io.nl(!IO),
io.write_string("Valor de FloatVar: ", !IO),
io.write_float(FloatVar, !IO),
io.nl(!IO),
io.write_string("Valor de StringVar: ", !IO),
io.write_string(StringVar, !IO),
io.nl(!IO),
io.write_string("Valores de ListVar: ", !IO),
io.write_list(ListVar, ", ", io.write_int, !IO),
io.nl(!IO).
Mercury tiene un sistema de tipos fuerte y estático. No existe conversión implícita. Para combinar un int con una string se debe usar string.from_int/1 explícitamente — el compilador rechaza el código si los tipos no coinciden.
:- module tipos.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module string.
:- pred ejemplo_predicado(int::in, string::out) is det.
ejemplo_predicado(X, Y) :-
% Esto sería un error de tipo en Mercury:
% Y = X + "hola".
% Forma correcta con conversión explícita:
Y = string.from_int(X) ++ " hola".
main(!IO) :-
T = 1,
% No se puede reasignar ni cambiar el tipo de una variable:
% T = "hola",
% T = 2,
_ = T,
ejemplo_predicado(42, Resultado),
io.write_string(Resultado, !IO),
io.nl(!IO).
La tabla de determinismo de la sección 02 cobra vida aquí. add/3 es det — produce siempre exactamente un resultado. is_even/1 y my_member/2 son semidet — pueden fallar, por eso se usan dentro de if-then-else.
:- module det_semidet.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int, list.
% Predicado determinista: siempre produce exactamente un resultado.
:- pred add(int::in, int::in, int::out) is det.
add(X, Y, Z) :- Z = X + Y.
% Predicado semideterminista: puede fallar (si X es impar).
:- pred is_even(int::in) is semidet.
is_even(X) :- X mod 2 = 0.
% Predicado semideterminista: puede fallar (si X no está en la lista).
:- pred my_member(int::in, list(int)::in) is semidet.
my_member(X, [X | _]).
my_member(X, [_ | Tail]) :- my_member(X, Tail).
main(!IO) :-
add(3, 4, Sum),
io.write_string("La suma es: ", !IO),
io.write_int(Sum, !IO),
io.nl(!IO),
( if is_even(Sum) then
io.write_string("La suma es par\n", !IO)
else
io.write_string("La suma es impar\n", !IO)
),
MyList = [1, 2, 3, 4],
( if list.member(3, MyList) then
io.write_string("3 está en la lista\n", !IO)
else
io.write_string("3 no está en la lista\n", !IO)
).
05 — Ejemplos Intermedios
Los ejemplos intermedios exploran el trabajo con listas, algoritmos recursivos y tipos de datos algebraicos — el núcleo de la programación lógico-funcional.
Implementación manual de operaciones comunes: longitud, inversión y concatenación usando recursión estructural.
:- module listas.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int, list.
:- func mi_longitud(list(T)) = int.
mi_longitud([]) = 0.
mi_longitud([_|T]) = 1 + mi_longitud(T).
% Inversión con acumulador (eficiente O(n))
:- func invertir(list(T)) = list(T).
invertir(L) = aux(L, []).
:- func aux(list(T), list(T)) = list(T).
aux([], Acc) = Acc.
aux([H|T], Acc) = aux(T, [H|Acc]).
:- func concatenar(list(T), list(T)) = list(T).
concatenar([], L) = L.
concatenar([H|T], L) = [H | concatenar(T, L)].
main(!IO) :-
Lista = [1, 2, 3, 4, 5],
io.format("Lista: %s\n", [s(string(Lista))], !IO),
io.format("Longitud: %d\n", [i(mi_longitud(Lista))], !IO),
io.format("Invertida: %s\n", [s(string(invertir(Lista)))], !IO),
io.format("Concat [6..8]:%s\n",[s(string(concatenar(Lista,[6,7,8])))], !IO).
Algoritmo de ordenamiento clásico que muestra cómo los predicados tienen múltiples cláusulas con guardas de condición.
:- module ordenamiento.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int, list.
:- pred insertar(int::in, list(int)::in, list(int)::out) is det.
insertar(X, [], [X]).
insertar(X, [H|T], [X,H|T]) :- X =< H.
insertar(X, [H|T], [H|T1]) :- X > H, insertar(X, T, T1).
:- pred insertion_sort(list(int)::in, list(int)::out) is det.
insertion_sort([], []).
insertion_sort([H|T], Sorted) :-
insertion_sort(T, ST),
insertar(H, ST, Sorted).
main(!IO) :-
Lista = [64, 25, 12, 22, 11, 90, 3],
insertion_sort(Lista, Ordenada),
io.format("Original: %s\n", [s(string(Lista))], !IO),
io.format("Ordenada: %s\n", [s(string(Ordenada))], !IO).
Figuras geométricas modeladas con un tipo algebraico. El compilador verifica que todos los casos sean cubiertos en el pattern matching.
:- module figuras.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module float, math.
:- type figura
---> circulo(float)
; rectangulo(float, float)
; triangulo(float, float, float).
:- func area(figura) = float.
area(circulo(R)) = math.pi * R * R.
area(rectangulo(B, H)) = B * H.
area(triangulo(A, B, C)) = sqrt(S * (S-A) * (S-B) * (S-C)) :-
S = (A + B + C) / 2.0.
:- func nombre(figura) = string.
nombre(circulo(_)) = "Circulo".
nombre(rectangulo(_, _)) = "Rectangulo".
nombre(triangulo(_, _, _)) = "Triangulo".
:- pred mostrar(figura::in, io::di, io::uo) is det.
mostrar(F, !IO) :-
io.format("%s: area = %.4f\n", [s(nombre(F)), f(area(F))], !IO).
main(!IO) :-
mostrar(circulo(5.0), !IO),
mostrar(rectangulo(4.0, 6.0), !IO),
mostrar(triangulo(3.0, 4.0, 5.0), !IO).
Un predicado puede declarar el mismo argumento como in u out según el modo de llamada. mother/2 funciona hacia adelante (dado el hijo, busca la madre) y también puede usarse hacia atrás (dada la madre, busca sus hijos). El compilador verifica cada modo por separado.
:- module mother.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- type persona ---> laura ; rafael ; james.
% El mismo predicado, dos modos distintos:
% dado el hijo (in) → busca la madre (out): semidet
% dada la madre (out) → busca hijos (in): nondet
:- pred mother(persona, persona).
:- mode mother(in, out) is semidet.
:- mode mother(out, in) is nondet.
mother(rafael, laura).
mother(james, laura).
main(!IO) :-
( if mother(rafael, X) then
io.write_string("La madre de rafael es ", !IO),
io.write(X, !IO),
io.nl(!IO)
else
io.write_string("Rafael no tiene madre registrada.\n", !IO)
).
solutions/2Un árbol genealógico con cuatro predicados relacionales (parent, father, mother, grandparent), cada uno con múltiples declaraciones de modo. solutions/2 recolecta todas las respuestas de un predicado nondet en una lista — similar a findall en Prolog pero con verificación de tipos.
:- module family.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module list, solutions.
:- type person ---> ada ; bob ; dan ; ema ; fay ; joe.
:- pred female(person).
:- mode female(in) is semidet.
:- mode female(out) is multi.
female(ada). female(ema). female(fay).
:- pred male(person).
:- mode male(in) is semidet.
:- mode male(out) is multi.
male(bob). male(dan). male(joe).
:- pred parent(person, person).
:- mode parent(in, in) is semidet.
:- mode parent(in, out) is nondet.
:- mode parent(out, in) is nondet.
:- mode parent(out, out) is multi.
parent(ada, dan). parent(bob, dan).
parent(dan, fay). parent(ema, fay).
parent(dan, joe). parent(ema, joe).
:- pred grandparent(person, person).
:- mode grandparent(in, in) is semidet.
:- mode grandparent(in, out) is nondet.
:- mode grandparent(out, in) is nondet.
:- mode grandparent(out, out) is nondet.
grandparent(PP, C) :- parent(PP, P), parent(P, C).
main(!IO) :-
% solutions/2 recolecta todas las soluciones en una lista.
solutions(
(pred(PP::out) is nondet :- grandparent(PP, fay)),
GPs),
(
GPs = [],
io.write_string("fay no tiene abuelos registrados.\n", !IO)
;
GPs = [_ | _],
io.write_string("Abuelos de fay: ", !IO),
io.write(GPs, !IO),
io.nl(!IO)
).
io.read_line_as_string/3 devuelve un resultado que puede ser ok(String), eof o error(Error). El disjunctive pattern matching sobre ese tipo garantiza que todos los casos sean manejados — el compilador rechaza código que omita algún constructor.
:- module suma.
:- interface.
:- import_module io.
:- pred suma(int::in, int::in, int::out) is det.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int, string, list.
suma(X, Y, Resultado) :- Resultado = X + Y.
main(!IO) :-
io.read_line_as_string(Result1, !IO),
(
Result1 = ok(Line1),
( if string.to_int(string.strip(Line1), X) then
io.read_line_as_string(Result2, !IO),
(
Result2 = ok(Line2),
( if string.to_int(string.strip(Line2), Y) then
suma(X, Y, Resultado),
io.format("La suma de %d y %d es %d\n",
[i(X), i(Y), i(Resultado)], !IO)
else
io.write_string(
"Error: el segundo valor no es un número válido.\n", !IO)
)
;
Result2 = eof,
io.write_string(
"Error: fin de entrada al leer el segundo número.\n", !IO)
;
Result2 = error(Err2),
io.format("Error al leer el segundo número: %s\n",
[s(io.error_message(Err2))], !IO)
)
else
io.write_string(
"Error: el primer valor no es un número válido.\n", !IO)
)
;
Result1 = eof,
io.write_string(
"Error: fin de entrada al leer el primer número.\n", !IO)
;
Result1 = error(Err1),
io.format("Error al leer el primer número: %s\n",
[s(io.error_message(Err1))], !IO)
).
06 — Ejemplos Avanzados
Capacidades avanzadas de Mercury: predicados de orden superior, gramáticas de cláusulas definidas y manejo de estado mediante recursión.
Mercury soporta funciones de orden superior: funciones que reciben otras funciones como argumento, similar a map, filter y fold en lenguajes funcionales.
:- module orden_superior.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int, list.
% mi_map: aplica F a cada elemento de la lista
:- func mi_map(func(T) = U, list(T)) = list(U).
mi_map(_, []) = [].
mi_map(F, [H|T]) = [F(H) | mi_map(F, T)].
% mi_foldl: reduce la lista acumulando con F
:- func mi_foldl(func(T, U) = U, list(T), U) = U.
mi_foldl(_, [], Acc) = Acc.
mi_foldl(F, [H|T], Acc) = mi_foldl(F, T, F(H, Acc)).
% Funciones concretas para pasar como argumento
:- func doblar(int) = int.
doblar(X) = X * 2.
:- func cuadrado(int) = int.
cuadrado(X) = X * X.
:- func sumar(int, int) = int.
sumar(X, Acc) = X + Acc.
main(!IO) :-
Lista = [1, 2, 3, 4, 5],
Dobles = mi_map(doblar, Lista),
Cuadrs = mi_map(cuadrado, Lista),
Suma = mi_foldl(sumar, Lista, 0),
io.format("Original: %s\n", [s(string(Lista))], !IO),
io.format("Dobles: %s\n", [s(string(Dobles))], !IO),
io.format("Cuadrados: %s\n", [s(string(Cuadrs))], !IO),
io.format("Suma total: %d\n", [i(Suma)], !IO).
Las DCG permiten definir gramáticas formales y parsers de forma elegante. Mercury transforma las reglas DCG en predicados con argumentos de diferencia de listas automáticamente.
:- module dcg_parser.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module list, char, string, int.
% Gramática: oracion --> frase_nominal, frase_verbal
% frase_nominal --> sustantivo
% frase_verbal --> verbo | verbo, frase_nominal
:- pred sustantivo(list(string)::in, list(string)::out) is semidet.
sustantivo --> ["mercury"].
sustantivo --> ["lenguaje"].
sustantivo --> ["programador"].
:- pred verbo(list(string)::in, list(string)::out) is semidet.
verbo --> ["es"].
verbo --> ["usa"].
:- pred frase_nominal(list(string)::in, list(string)::out) is semidet.
frase_nominal --> sustantivo.
:- pred frase_verbal(list(string)::in, list(string)::out) is semidet.
frase_verbal --> verbo.
frase_verbal --> verbo, frase_nominal.
:- pred oracion(list(string)::in, list(string)::out) is semidet.
oracion --> frase_nominal, frase_verbal.
:- pred probar(string::in, list(string)::in, io::di, io::uo) is det.
probar(Desc, Tokens, !IO) :-
( oracion(Tokens, []) ->
io.format("VALIDA | %s\n", [s(Desc)], !IO)
;
io.format("INVALIDA| %s\n", [s(Desc)], !IO)
).
main(!IO) :-
probar("mercury es", ["mercury", "es"], !IO),
probar("programador usa mercury",["programador","usa","mercury"], !IO),
probar("es mercury", ["es", "mercury"], !IO).
En Mercury, el estado mutable se modela pasando el estado como argumento. Este patrón — llamado state threading — garantiza la pureza referencial sin sacrificar la expresividad.
:- module estado.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int, list.
% Cuenta regresiva: el estado (N) se pasa por recursión
:- pred cuenta_regresiva(int::in, io::di, io::uo) is det.
cuenta_regresiva(0, !IO) :-
io.write_string("Despegue!\n", !IO).
cuenta_regresiva(N, !IO) :-
N > 0,
io.format(" %d...\n", [i(N)], !IO),
cuenta_regresiva(N - 1, !IO).
% Suma acumulativa: el acumulador es el estado
:- pred sumar_lista(list(int)::in, int::in, int::out) is det.
sumar_lista([], Acc, Acc).
sumar_lista([H|T], Acc, Total) :-
NuevoAcc = Acc + H,
sumar_lista(T, NuevoAcc, Total).
% Producto con acumulador
:- pred producto_lista(list(int)::in, int::in, int::out) is det.
producto_lista([], Acc, Acc).
producto_lista([H|T], Acc, Prod) :-
producto_lista(T, Acc * H, Prod).
main(!IO) :-
io.write_string("=== Cuenta regresiva ===\n", !IO),
cuenta_regresiva(5, !IO),
io.nl(!IO),
Lista = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
sumar_lista(Lista, 0, Suma),
producto_lista(Lista, 1, Prod),
io.format("=== Lista 1..10 ===\n", [], !IO),
io.format("Suma: %d\n", [i(Suma)], !IO),
io.format("Producto: %d\n", [i(Prod)], !IO).
cc_multiUn sistema de inferencia simple donde las propiedades se derivan de hechos y reglas. cc_multi (committed choice multi) indica que el predicado produce al menos una solución pero sólo se compromete con la primera — equivalente a un corte implícito, pero verificado por el compilador.
/* ngtrks es pequeño y verde.
pgvdrk es un marciano saltarín.
Todas las criaturas saltarinas son verdes.
Todas las criaturas pequeñas y saltarinas son marcianas.
Todas las criaturas verdes y marcianas son inteligentes.
¿Cuál de los dos es inteligente? */
:- module martians.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is cc_multi.
:- implementation.
:- type marciano ---> ngtrks ; pgvdrk.
:- pred small(marciano::out) is det.
:- pred green(marciano::out) is multi.
:- pred martian(marciano::out) is multi.
:- pred jumping(marciano::out) is det.
:- pred intelligent(marciano::out) is nondet.
small(ngtrks).
green(ngtrks).
martian(pgvdrk).
jumping(pgvdrk).
green(X) :- jumping(X).
martian(X) :- small(X), jumping(X).
intelligent(X) :- green(X), martian(X).
main(!IO) :-
io.write_string("¿Qué marciano es inteligente?: ", !IO),
( if intelligent(X) then
io.write(X, !IO),
io.write_string(" es inteligente.\n", !IO)
else
io.write_string("No se puede determinar.\n", !IO)
).
Lectura carácter a carácter con io.read_char/3 en un bucle recursivo que termina al alcanzar eof. El cifrado ROT13 se implementa como una función de pattern matching exhaustivo sobre char — cada letra del alfabeto se mapea a su par rotado 13 posiciones.
:- module rot13.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module char, list, string.
main(!IO) :-
io.read_char(Result, !IO),
(
Result = ok(Char),
io.write_char(rot13(Char), !IO),
main(!IO)
;
Result = eof
;
Result = error(ErrorCode),
io.format("%s\n", [s(io.error_message(ErrorCode))], !IO)
).
:- func rot13(char) = char.
rot13(Char) = (
if Char = 'a' then 'n' else if Char = 'b' then 'o'
else if Char = 'c' then 'p' else if Char = 'd' then 'q'
else if Char = 'e' then 'r' else if Char = 'f' then 's'
else if Char = 'g' then 't' else if Char = 'h' then 'u'
else if Char = 'i' then 'v' else if Char = 'j' then 'w'
else if Char = 'k' then 'x' else if Char = 'l' then 'y'
else if Char = 'm' then 'z' else if Char = 'n' then 'a'
else if Char = 'o' then 'b' else if Char = 'p' then 'c'
else if Char = 'q' then 'd' else if Char = 'r' then 'e'
else if Char = 's' then 'f' else if Char = 't' then 'g'
else if Char = 'u' then 'h' else if Char = 'v' then 'i'
else if Char = 'w' then 'j' else if Char = 'x' then 'k'
else if Char = 'y' then 'l' else if Char = 'z' then 'm'
else Char
).
Mercury usa el GC de Boehm. Este ejemplo crea y descarta estructuras de datos sin liberar memoria manualmente: DataList e IntList se vuelven inalcanzables al final del predicado y el GC las libera automáticamente.
:- module trash.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module list, int.
:- type my_data ---> data(int).
:- func create_data_list(int) = list(my_data).
create_data_list(N) =
( if N =< 0 then [] else [data(N) | create_data_list(N - 1)] ).
:- func process_data_list(list(my_data)) = list(int).
process_data_list([]) = [].
process_data_list([data(X) | Xs]) = [X | process_data_list(Xs)].
main(!IO) :-
DataList = create_data_list(10),
IntList = process_data_list(DataList),
io.write_list(IntList, ", ", io.write_int, !IO),
io.nl(!IO),
% DataList e IntList ya no son referenciadas.
% El recolector de basura (Boehm GC) libera su memoria automáticamente.
io.write_string(
"Memoria gestionada automáticamente por el recolector de basura.\n",
!IO).
foreign_procpragma foreign_proc permite implementar un predicado directamente en C (o Java, C#). Las anotaciones le indican al compilador qué garantías ofrece el código externo: will_not_call_mercury (no reingresa en Mercury), promise_pure (es referencialmente transparente) y thread_safe (seguro para concurrencia).
:- module addc.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module string, int.
:- pred add(int::in, int::in, int::out) is det.
:- pragma foreign_proc("C",
add(A::in, B::in, Result::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
Result = A + B;
").
main(!IO) :-
io.read_line_as_string(ResultA, !IO),
(
ResultA = ok(LineA),
( if string.to_int(string.strip(LineA), A) then
io.read_line_as_string(ResultB, !IO),
(
ResultB = ok(LineB),
( if string.to_int(string.strip(LineB), B) then
add(A, B, Sum),
io.write_string("La suma es: ", !IO),
io.write_int(Sum, !IO),
io.nl(!IO)
else
io.write_string("Error: B no es un entero válido.\n", !IO)
)
;
ResultB = eof,
io.write_string("Error: fin de entrada al leer B.\n", !IO)
;
ResultB = error(ErrB),
io.format("Error al leer B: %s\n",
[s(io.error_message(ErrB))], !IO)
)
else
io.write_string("Error: A no es un entero válido.\n", !IO)
)
;
ResultA = eof,
io.write_string("Error: fin de entrada al leer A.\n", !IO)
;
ResultA = error(ErrA),
io.format("Error al leer A: %s\n",
[s(io.error_message(ErrA))], !IO)
).
Tres casas con colores, nacionalidades y mascotas distintas. Cada restricción se expresa como un predicado puro. solutions/2 busca todas las asignaciones que satisfacen todas las restricciones simultáneamente — el motor de backtracking de Mercury hace el resto.
:- module puzzle.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module list, string, solutions.
% El inglés vive en la casa roja.
% El jaguar es la mascota de la familia española.
% Los japoneses viven a la derecha del cuidador de caracoles.
% El cuidador de caracoles vive a la izquierda de la casa azul.
:- type origenes ---> ingles ; japones ; espanol.
:- type colores ---> rojo ; azul ; verde.
:- type mascotas ---> jaguar ; caracol ; cebra.
:- type casa ---> casa(origen::origenes, color::colores, mascota::mascotas).
:- pred distinct(casa::in, casa::in) is semidet.
distinct(casa(O1,C1,M1), casa(O2,C2,M2)) :-
not (O1 = O2 ; C1 = C2 ; M1 = M2).
:- pred fila(list(casa)::out) is nondet.
fila([X, Y, Z]) :-
casa(X), casa(Y), casa(Z),
( X^mascota = caracol <=> Y^origen = japones ),
( Y^mascota = caracol <=> Z^origen = japones ),
( Z^color = azul <=> Y^mascota = caracol ),
( Y^color = azul <=> X^mascota = caracol ),
not X^origen = japones,
not Z^mascota = caracol,
distinct(X, Y), distinct(Y, Z), distinct(X, Z).
:- pred casa(casa::out) is nondet.
casa(casa(O, C, M)) :-
origen(O), color(C), mascota(M),
( O = ingles <=> C = rojo ),
( O = espanol <=> M = jaguar ),
not (O = japones, M = caracol),
not (M = caracol, C = azul).
:- pred origen(origenes::out) is multi.
origen(ingles). origen(japones). origen(espanol).
:- pred color(colores).
:- mode color(out) is multi.
:- mode color(in) is det.
color(rojo). color(azul). color(verde).
:- pred mascota(mascotas::out) is multi.
mascota(jaguar). mascota(caracol). mascota(cebra).
main(!IO) :-
solutions(fila, Soluciones),
( if Soluciones = [] then
io.write_string("Sin solución.\n", !IO)
else
list.foldl(
(pred(L::in, !.IO::di, !:IO::uo) is det :-
io.print(L, !IO), io.nl(!IO)),
Soluciones, !IO)
).
El principio del casillero (pigeonhole principle) expresado como un predicado Mercury puro. Si hay más palomas que casilleros, algún casillero alberga más de una. Sin bucles, sin estado mutable — sólo lógica declarativa.
:- module palomar.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int, string, bool.
:- pred pigeonhole(int::in, int::in, bool::out) is det.
pigeonhole(N, M, Result) :-
( if N > M then Result = yes else Result = no ).
:- pred print_result(int::in, int::in, bool::in, io::di, io::uo) is det.
print_result(N, M, Result, !IO) :-
( if Result = yes then
io.format(
"%d palomas en %d casilleros: al menos uno tendrá más de una.\n",
[i(N), i(M)], !IO)
else
io.format(
"%d palomas en %d casilleros: ninguno tendrá más de una.\n",
[i(N), i(M)], !IO)
).
main(!IO) :-
pigeonhole(10, 9, R1), print_result(10, 9, R1, !IO),
pigeonhole(5, 5, R2), print_result(5, 5, R2, !IO),
pigeonhole(3, 7, R3), print_result(3, 7, R3, !IO).
07 — Recursos y PDF
El tutorial anterior del curso incluye un PDF con desarrollo profundo de cuatro temas: Modos, Predicados, Determinismo y Tipos. Es un complemento directo a las secciones 02 y 03 de este tutorial.
Modos · Predicados · Determinismo · Tipos
Curso de Mercury en profundidad: módulos, tipos avanzados, E/S, concurrencia y más. Por Paul Bone.
mercury-in.space/crash.html →Mercury está basado en Prolog. Este tutorial UNAL cubre los fundamentos de programación lógica necesarios como base.
Tutorial Prolog UNAL →Referencia completa del lenguaje: toda la sintaxis, módulos estándar, modos, determinismo y directivas del compilador.
reference_manual.pdf →Cómo compilar, depurar, generar documentación y usar el sistema de módulos en proyectos reales.
user_guide.pdf →08 — Créditos
Este tutorial integra el trabajo de dos generaciones de estudiantes del curso de Paradigmas de Programación de la Universidad Nacional de Colombia.
Diseño, contenido, ejemplos e integración del tutorial actualizado.
Los ejemplos, explicaciones y estructura del tutorial anterior fueron la base sobre la que se construyó esta versión actualizada.
Juan Castelblanco · Ivan Cepeda · Camilo Rodriguez · Stevan Valbuena · Samuel Salgado · Diego Bulla
Simón Aparicio · Javier Toro · Juliana De Castro · William García