un lenguaje lógico funcional con restricciones1

268
T OY : Un lenguaje l´ogico funcional con restricciones 1 Autor: JaimeS´anchezHern´andez Director: Francisco J. L´opez Fraguas Trabajo de Tercer Ciclo umero de cr´ editos solicitados: 7 Departamento Sistemas Inform´aticos y Programaci´on Escuela Superior de Inform´atica Univ. Complutense de Madrid Septiembre 1998 1 Este trabajo ha sido parcialmente financiado por el proyecto nacional TIC 95-0433-C03-01 ”CPD” (Combinaci´ on de Paradigmas de Programaci´on Declarativa) y el ESPRIT Working Group 22457 (CCL-II)

Upload: ngothien

Post on 06-Jan-2017

238 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Un lenguaje lógico funcional con restricciones1

T OY :

Un lenguaje logico funcional con restricciones1

Autor: Jaime Sanchez HernandezDirector: Francisco J. Lopez Fraguas

Trabajo de Tercer CicloNumero de creditos solicitados: 7

Departamento Sistemas Informaticos y ProgramacionEscuela Superior de InformaticaUniv. Complutense de Madrid

Septiembre 1998

1Este trabajo ha sido parcialmente financiado por el proyecto nacional TIC 95-0433-C03-01”CPD” (Combinacion de Paradigmas de Programacion Declarativa) y el ESPRIT Working Group22457 (CCL-II)

Page 2: Un lenguaje lógico funcional con restricciones1

Agradecimientos

El sistema T OY del que trata este trabajo ha sido implementado por Francisco J.Lopez Fraguas, Rafael Caballero Roldan y el que escribe, y no puedo dejar pasar la ocasionsin agradecer a ellos dos el esfuerzo que han dedicado al desarrollo del compilador. Perosobre todo quiero darles las gracias por la paciencia y el buen humor con el que hanafrontado los problemas que han surgido (y no han sido pocos). Creo que esta actitudha contribuido a mantener viva la ilusion en una empresa larga y, en algunas ocasiones,compleja. Tambien quiero mencionar el buen hacer y la perseverancia de Francisco antelas dificultades con las que hemos topado en el estudio de la semantica del lenguaje. Enesta parte de nuestro trabajo se produce el “efecto mariposa”: una nimia modificacionen alguna de las reglas del calculo tiene un efecto caotico (varias paginas despues) en losteoremas de correccion o completitud. Conseguir una formulacion consistente ha sido unatarea ardua, pero creo que ha merecido la pena.

Debo expresar tambien mi agradecimiento a los integrantes de nuestro grupo de Pro-gramacion Declarativa, puesto que la concepcion de T OY es fruto de sus investigaciones.De hecho, en este grupo ya se habıan implementado otros sistemas como BABLOG, que esel antecesor de T OY. Debo recalcar que todos los miembros han manifestado un espıritude coloboracion pleno en el desarrollo del nuevo sistema. Personalmente he tenido la opor-tunidad de mantener largas y provechosas conversaciones con muchos ellos para resolverlas dificultades que han ido apareciendo. T OY es un sistema “mimado” en el sentido deque se ha cuidado minuciosamente cada uno de los detalles de la implementacion, contras-tando opiniones con otros miembros del grupo para buscar la mejor opcion posible en cadacaso. Gracias tambien a Juan Carlos Gonzalez Moreno, que se ha ocupado de facilitar elacceso electronico a la distribucion del sistema (y ha discutido mucho conmigo).

Y por ultimo quiero dar las gracias a mi amiga y companera Ana, que ademas dediscutir mucho, es para mı un apoyo incondicional.

Page 3: Un lenguaje lógico funcional con restricciones1

Indice general

1. Introduccion 71.1. El sistema T OY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.2. Programacion logico funcional. Generalidades. . . . . . . . . . . . . . . . . . 91.3. Restricciones aritmeticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111.4. Funciones indeterministas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121.5. Organizacion del trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2. El sistema T OY 132.1. Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.2. El entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.2.1. Arrancando el sistema . . . . . . . . . . . . . . . . . . . . . . . . . . 132.2.2. Comandos basicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142.2.3. Compilando y ejecutando programas T OY . . . . . . . . . . . . . . 152.2.4. Informacion sobre funciones . . . . . . . . . . . . . . . . . . . . . . . 162.2.5. Salvaguarda de sesiones . . . . . . . . . . . . . . . . . . . . . . . . . 172.2.6. Activacion de las restricciones sobre los numeros reales . . . . . . . . 172.2.7. Definicion de nuevos comandos para T OY . . . . . . . . . . . . . . . 18

2.3. El lenguaje T OY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192.3.1. Definicion de tipos de datos . . . . . . . . . . . . . . . . . . . . . . . 192.3.2. Tipos predefinidos del sistema . . . . . . . . . . . . . . . . . . . . . . 212.3.3. Alias o sinonimos de tipo . . . . . . . . . . . . . . . . . . . . . . . . 222.3.4. Definicion de funciones . . . . . . . . . . . . . . . . . . . . . . . . . . 232.3.5. Tipos de las funciones . . . . . . . . . . . . . . . . . . . . . . . . . . 252.3.6. Funciones de orden superior y estructuras infinitas . . . . . . . . . . 272.3.7. Funciones indeterministas . . . . . . . . . . . . . . . . . . . . . . . . 282.3.8. Restricciones de igualdad y desigualdad . . . . . . . . . . . . . . . . 292.3.9. Restricciones sobre los numeros reales . . . . . . . . . . . . . . . . . 312.3.10. Definicion de predicados . . . . . . . . . . . . . . . . . . . . . . . . . 322.3.11. Operadores infijos y secciones . . . . . . . . . . . . . . . . . . . . . . 332.3.12. Inclusion de archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . 342.3.13. Regla de indentacion . . . . . . . . . . . . . . . . . . . . . . . . . . . 342.3.14. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352.3.15. Funciones primitivas . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

2.4. Ejemplo 1. Regiones en el plano . . . . . . . . . . . . . . . . . . . . . . . . . 422.5. Ejemplo 2. Puzle aritmetico . . . . . . . . . . . . . . . . . . . . . . . . . . . 532.6. Comparacion con otros estilos de programacion . . . . . . . . . . . . . . . . 56

2.6.1. Ordenacion de listas . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

3

Page 4: Un lenguaje lógico funcional con restricciones1

4 INDICE GENERAL

2.6.2. El problema del laberinto revisitado . . . . . . . . . . . . . . . . . . 58

3. Mecanismo de computo 613.1. Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613.2. Vision general del proceso de compilacion y ejecucion de objetivos . . . . . 623.3. Preliminares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643.4. Los objetos sintacticos de T OY y su representacion Prolog . . . . . . . . . 643.5. Orden superior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

3.5.1. Reglas de apply . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683.5.2. Informacion sobre constructoras y funciones . . . . . . . . . . . . . . 70

3.6. El sharing o comparticion de variables . . . . . . . . . . . . . . . . . . . . . 723.7. El codigo intermedio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733.8. Las restricciones de desigualdad estricta . . . . . . . . . . . . . . . . . . . . 74

3.8.1. Gestion de las desigualdades . . . . . . . . . . . . . . . . . . . . . . 763.9. Computo de formas normales de cabeza (hnf) . . . . . . . . . . . . . . . . . 793.10. Generacion de codigo para las funciones . . . . . . . . . . . . . . . . . . . . 82

3.10.1. Preliminares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843.10.2. Construccion del arbol definicional . . . . . . . . . . . . . . . . . . . 853.10.3. Generacion de codigo para las funciones. Primera aproximacion . . . 903.10.4. Incorporacion de desigualdades en la traduccion de funciones . . . . 933.10.5. Optimizaciones de codigo . . . . . . . . . . . . . . . . . . . . . . . . 973.10.6. Codigo para las funciones primitivas y apply . . . . . . . . . . . . . 102

3.11. Igualdad estricta (==) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1043.11.1. El occurs-check . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1053.11.2. El estudio de la frontera . . . . . . . . . . . . . . . . . . . . . . . . . 1063.11.3. Ligadura de variables a f.n.c.’s (binding) . . . . . . . . . . . . . . . . 1093.11.4. Igualdad estricta entre formas normales de cabeza (equalHnf) . . . 1103.11.5. El predicado equal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

3.12. Restricciones de desigualdad (notEqual) . . . . . . . . . . . . . . . . . . . . 1133.12.1. Restricciones de desigualdad entre formas normales . . . . . . . . . . 118

3.13. La funcion igualdad (eqFun) . . . . . . . . . . . . . . . . . . . . . . . . . . 1193.14. La funcion desigualdad (notEqFun) . . . . . . . . . . . . . . . . . . . . . . 1223.15. Las restricciones sobre los numeros reales . . . . . . . . . . . . . . . . . . . 123

4. De la semantica 1294.1. Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1294.2. Preliminares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

4.2.1. Signaturas, terminos y c-terminos . . . . . . . . . . . . . . . . . . . . 1304.2.2. Sustituciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

4.3. El calculo de pruebas GORC6= . . . . . . . . . . . . . . . . . . . . . . . . . 1314.3.1. Caracterizacion de == en funcion de → . . . . . . . . . . . . . . . . 1374.3.2. Sustituciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

4.4. Calculo de resolucion de objetivos . . . . . . . . . . . . . . . . . . . . . . . 1444.4.1. El calculo LNC6= . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1454.4.2. Correccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614.4.3. Completitud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

4.5. Estrategia DDS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1694.5.1. Transformacion de programas . . . . . . . . . . . . . . . . . . . . . . 171

Page 5: Un lenguaje lógico funcional con restricciones1

INDICE GENERAL 5

4.5.2. Algoritmo de transformacion . . . . . . . . . . . . . . . . . . . . . . 175

5. Conclusiones y trabajo futuro 179

A. Gramatica de T OY . 181

B. Declaracion de primitivas (archivo basic.toy) 187

C. Funciones de uso comun (archivo misc.toy) 189

D. Archivo toycomm.pl 197

E. Primitivas sin restricciones aritmeticas (archivo primitives.pl) 211

F. Primitivas con restricciones aritmeticas (archivo primitivesClpr.pl) 219

G. Construccion del arbol definicional 229

H. Generacion de codigo 241

I. Salida de respuestas 255

Page 6: Un lenguaje lógico funcional con restricciones1

6 INDICE GENERAL

Page 7: Un lenguaje lógico funcional con restricciones1

Capıtulo 1

Introduccion

El objetivo fundamental de los lenguajes de programacion declarativa en sentido am-plio, es proporcionar un alto nivel de abstraccion, de forma que la especificacion de unproblema sea un programa capaz de resolver el problema. En definitiva se intenta liberaral programador de describir detalladamente la secuencia de acciones que debe realizar lamaquina para obtener el resultado buscado, como es habitual en programacion impera-tiva. Los lenguajes declarativos estan basados en formalismos matematicos que permitenhacer un estudio riguroso y preciso de los aspectos semanticos que subyacen. Se abstraenlos detalles concretos del hardware y, en general, los programas son mas breves y massencillos de mantener.

Sin embargo, la programacion declarativa no tiene un paradigma representativo unico.Los dos mas importantes, el logico y el funcional han evolucionado de forma independiente,pero manteniendo como prioridad comun la expresividad. Como resultado se han forjadodos estilos de programacion distintos que intentan aprovechar las ventajas que ofreceuno u otro enfoque. El potencial, en el caso de la programacion funcional, viene dadofundamentalmente por la evaluacion perezosa, el orden superior y los tipos, mientras queen programacion logica las variables logicas, los modos multiples de uso de los predicadosy el indeterminismo suponen la principal aportacion.

Como amalgama de estas dos vertientes surgen los lenguajes logico funcionales ([Han94]),en los que se pretende reunir las principales ventajas de ambos paradigmas en uno nue-vo. Esta fue la motivacion de lenguajes como K-LEAF ([BCM89]) y BABEL ([MR89]).El mecanismo operacional de estas propuestas es el resultado de combinar los que uti-lizan ambos tipos de lenguajes. En general, el mecanismo operacional de los lenguajeslogicos se apoya en la unificacion y la resolucion, mientras que en los lenguajes funcio-nales es la reescritura la que juega este papel. Para los lenguajes logico-funcionales elmecanismo mas estudiado y extendido es la “reescritura con unificacion” o, mas comun-mente, narrowing (estrechamiento). Este metodo tiene sus orıgenes en Reddy ([Red85]) yen [GM86] se prueba que es completo para la resolucion de sistemas de ecuaciones con-fluentes y terminantes. El narrowing es tambien utilizado por lenguajes como BABLOG([AG94]) o CURRY([He97, HKM95]). Existen, no obstante otros como ESCHER ([Llo95])que se basan en reescritura.

El narrowing presenta un alto grado de indeterminismo debido a la eleccion del redex(exprersion a reducir en un paso de computo) y de la regla de reescritura a utilizar, loque supone que el espacio de busqueda generado puede ser muy grande. Para reducireste espacio se utilizan estrategias de estrechamiento con el fin de conseguir computos“mas” deterministas, y en consecuencia, mas eficientes. En particular, es posible guiar

7

Page 8: Un lenguaje lógico funcional con restricciones1

8 CAPITULO 1. INTRODUCCION

la evaluacion de una funcion estudiando la demanda de patrones de las reglas que ladefinen, y en este sentido surge la Estrategia Guiada por la Demanda ([LLR93]). Estaestrategia responde a la idea del estrechamiento perezoso ([Red85]) y su funcionamientoconsiste en retrasar la evaluacion de los argumentos de la funcion de llamada, mientras nosean necesarios para continuar el computo. El lenguaje BABLOG ya implementaba dichaestrategia.

Otro de los aspectos que ha evolucionado notablemente en programacion logica es laintroduccion de restricciones ([JM94, Coh90]), que enriquecen el poder expresivo de estoslenguajes. En programacion logico funcional tambien es posible incorporar restricciones([L94, LR91]). En particular, las restricciones de desigualdad ([AGL94, L94]) suponen unrecurso expresivo importante que ya incluıa el lenguaje BABLOG.

Otro tipo de restricciones ampliamente estudiadas en programacion logica son lasarimeticas, que tienen extenso rango de aplicacion y se han incluido en lenguajes comoCLP (R) ([JMS+92a, JMS+92b, HJM+91]). Tambien este tipo de restricciones puede in-corporarse a los lenguajes logico funcionales ([AHL+96, HLS+97]).

1.1. El sistema T OYEn este trabajo se presenta el sistema T OY ([CLS97]), una implementacion de un

lenguaje logico-funcional que incorpora restricciones sobre los reales. Esta implementacionha sido desarrollada por Rafael Caballero Roldan, Francisco J. Lopez Fraguas y el autorde este documento.

Toda la informacion contenida en este trabajo se refiere a la version 2.0 del sistema,que es la unica disponible al publico en la actualidad y que esta disenada para plataformasUNIX1. Esta version esta disponible en la direccion:

http://mozart.sip.ucm.es/incoming/comprimidos/toy.tar.gz

En lo sucesivo utilizaremos la palabra ‘T OY’ para referirnos tanto al sistema como allenguaje que implementa.

En T OY se combinan los dos estilos de programacion logica y funcional tomandoProlog ([SS86, O’K90]) como representante logico y Haskell ([HFP97, PH97, JJ97]) comorepresentante funcional. Las caracterısticas mas destacables del lenguaje son:

sintaxis funcional (inspirada en Haskell),

programacion logica pura mediante definicion de predicados al estilo Prolog,

programacion funcional con:

• tipos polimorficos,

• funciones de orden superior y computos logicos de orden superior (bajo deter-minadas condiciones)

• evaluacion perezosa y estructuras de datos infinitas,

restricciones de igualdad estricta,1Hay versiones experimentales no distribuidas que corren sobre MS-DOS y Windows95. Tambien hay

una version que incorpora corte dinamico ([LW91, LW95]) y algunas pruebas de entrada/salida e introduc-cion de restricciones de dominio finito.

Page 9: Un lenguaje lógico funcional con restricciones1

1.2. PROGRAMACION LOGICO FUNCIONAL. GENERALIDADES. 9

restricciones de desigualdad estricta,

funciones indeterministas,

restricciones sobre los numeros reales.

T OY, al igual que su predecesor BABLOG, lleva a cabo una traduccion a Prolog de losprogramas fuente ([Che90, CF93]) de acuerdo con la Estrategia Guiada por la Demandaque se propone en [LLR93]. Esta estrategia esta inspirada en la construccion de arbolesdefinicionales ([Ant92]). Las restricciones sobre reales tambien se transforman de acuerdocon la estrategia en otras mas simples que son procesadas por un resolutor de restricciones([Hol95]).

Las principales aportaciones de T OY a la programacion logico funcional son la intro-duccion de restricciones sobre los numeros reales y las funciones indeterministas. Tambienadmite variables logicas de orden superior e incorpora muchas optimizacciones en la tra-duccion de las funciones, ası como en la resolucion de igualdades y desigualdades. El interesde nuestro lenguaje en el ambito de la programacion declarativa viene avalado por el hechode esta presente en la pagina web mas importante sobre programacion logico funcional:

http : //www − i2.informatik.rwth− aachen.de/˜hanus/FLP

Tambien ha sido mencionado y descrito en el boletın de noticias (Febrero de 1998) de laAssociation for Logic Programming, que es la asociacion sobre Programacion Logica masrelevante a nivel internacional. La referencia es accesible electronicamente en:

http : //www − lp.doc.ic.ac.uk : 80/alp/news/free− langs/flps/toy.html

T OY soporta el desarrollo de programas no triviales, ası como metodologıas de pro-gramacion interesantes ([CR98]). Ha contribuido notablemente al desarrollo de otras inves-tigaciones en el grupo de Programacion Declarativa del Depto. de Sistemas Informaticosy Programacion de la UCM y ha sido utilizado por otros investigadores como sistema pa-ra realizar pruebas sobre evaluacion parcial ([AAF+98]). Por otro lado, T OY constituyeel sistema de partida para el proyecto TREND (Tecnicas Avanzadas de Desarrollo deProgramas en Entornos Declarativos), recientemente concedido por la CICYT.

1.2. Programacion logico funcional. Generalidades.

Uno de los aspectos mas destacados de la programacion logico funcional es la posibi-lidad de utilizar las funciones de forma reversible, de modo analogo al uso de predicadosen Prolog. Por ejemplo, la concatenacion de listas, en Prolog puede definirse como:

append([],Ys,Ys).append([X|Xs],Ys,[X|Zs]) :- append(Xs,Ys,Zs).

El predicado append esta pensado, en principio, para recibir dos listas y devolver su conca-tenacion en el ultimo argumento. Por ejemplo, el objetivo append([1, 2], [3, 4], L) producirıala respuesta L = [1, 2, 3, 4]. Pero tambien, con esta misma definicion, se puede resolver elobjetivo append(Xs, [3, 4], [1, 2, 3, 4]), que producira la respuesta Xs = [1, 2, 3, 4].

En Haskell, se puede definir una funcion similar:

append [] ys = ysappend (x:xs) ys = (x:append xs ys)

Page 10: Un lenguaje lógico funcional con restricciones1

10 CAPITULO 1. INTRODUCCION

Con esta definicion se puede reducir la expresion append [1, 2] [3, 4] a la lista [1, 2, 3, 4],pero si se intenta reducir append xs [3, 4] el sistema producira un error, porque no puedereducir expresiones que contengan variables.

En programacion logico funcional y en particular, en T OY, se admite la definicionanterior de la funcion append, pero ademas es posible resolver una restriccion comoappend Xs [3, 4] == Zs, que producira el resultado Xs == [1, 2], Zs == [1, 2, 3, 4].Aquı el sımbolo ‘==’ representa restricciones de igualdad y la respuesta obtenida quieredecir que la restriccion planteada append Xs [3, 4] == Zs es cierta si son ciertas las res-tricciones Xs == [1, 2] y Zs == [1, 2, 3, 4]. Las restricciones de la respuesta representanlos valores que deben tomar las variables de la restriccion para satisfacerla. Informalmentepodemos decir que se han “despejado” las variables Xs y Zs (formalmente se dice que lasrestricciones de la respuesta estan en forma resuelta).

Es posible ademas que una restriccion tenga mas de una solucion, como ocurre enel caso de append Xs Y s == [1, 2]. En este caso T OY devolvera una primera solucionXs == [ ], Y s == [1, 2] y, si el usuario lo solicita, calculara a continuacion las siguientesXs == [1], Y s == [2] y tambien Xs = [1, 2], Y s = [ ]. Si se solicitan mas respuestas, elsistema informara de que no existen mas. Este proceso se realiza por backtracking (vueltaatras) de modo similar a como operarıa Prolog con el objetivo correspondiente.

En T OY se admiten ademas restricciones de desigualdad como append [1] Xs /=[1, 2]. En este caso el sistema devuelve como unica respuesta Xs/= [2]. El interes de lasdesigualdades reside en que con ellas se pueden ofrecer respuestas tan concisas como la delejemplo anterior. Las desigualdades aparecen en situaciones comunes en el contexto logicofuncional y en muchos casos, como el del ejemplo anterior, una respuesta que contenga unadesigualdad no puede reemplazarse por un numero finito de respuestas que solo contenganigualdades.

En Haskell, sin embargo, la evaluacion perezosa de las funciones mejora considerable-mente el rendimiento del sistema y permite manejar estructuras infinitas que Prolog nopuede tratar. Por ejemplo, consideremos en Haskell las dos funciones siguientes2:

from n = [n | from (n+1)]

take 0 xs = []take n [] = []take n (x:xs) | n>0 = x : take (n-1) xs

Una llamada como from 3 produce una lista que comienza con 3, seguida de el resultado deevaluar from 4, que a su vez produce una lista que empieza por 4 seguida de el resultadode evaluar from 5... En definitiva, el resultado de la llamada from 3 se reducirıa a lalista infinita [3, 4, 5, ...], que dejarıa al sistema sumido en un computo infinito (en realidad,hasta agotar los recursos de memoria).

La funcion from no tiene interes aisladamente, pero sı lo tiene en combinacion conotras como take. La funcion take devuelve los n primeros elementos de una lista dada(o la lista completa si su longitud es menor o igual que n). Utilizando esta funcion sepuede reducir la expresion take 5 (from 3) que devolvera los 5 primeros elementos de lalista infinita [3, 4, 5, ...], es decir, la lista [3, 4, 5, 6, 7]. Esto es posible debido a la evaluacionperezosa (2.3.6).

2En la ultima regla de take la expresion | n > 0 es una guarda que puede leerse como “si n es mayorque 0”.

Page 11: Un lenguaje lógico funcional con restricciones1

1.3. RESTRICCIONES ARITMETICAS 11

En Prolog no es posible hacer un computo como el que acabamos de describir ya quecualquier predicado que genere una estructura infinita, una vez invocado, intentara generarla estructura completa y producucira un computo no terminante (la pereza se puedesimular, pero Prolog no es perezoso). T OY implementa la evaluacion perezosa y permitehacer el computo de Haskell que acabamos de describir. De hecho, aunque T OY hace unatraduccion a Prolog, debido a la pereza es mas eficiente que Prolog en algunos casos (conprogramas similares), como veremos en 2.6.

Otra de las virtudes de la programacion funcional que tambien incopora Haskell es elorden superior, es decir, la posibilidad de utilizar funciones como argumentos de funciones,y tambien funciones que devuelven funciones. T OY no solo admite orden superior en estesentido, sino que admite tambien variables logicas de orden superior como veremos en(2.3.6).

Por ultimo T OY tambien incorpora un inferidor de tipos similar al de Haskell3 ypermite declarar tipos para las funciones y predicados. Por ejemplo, de la definicion de lafuncion append anterior se deduce que debe tomar dos listas como argumento y devolverotra lista; pero ademas las tres listas deben ser de elementos del mismo tipo (este tipo serepresenta como [A] → [A] → [A]). Por ejemplo, una expresion como append [1, 2] [′a′]esta mal tipada porque la primera es una lista de numeros y la segunda es de caracteres.Los tipos suponen una valiosa ayuda para detectar errores en los programas y, en general,aportan claridad a los programas.

1.3. Restricciones aritmeticas

En la seccion anterior hemos comenzado viendo el significado de la reversibilidad de lospredicados en Prolog. Sin embargo, esta reversibilidad se pierde en el momento en el quese implican operaciones aritmeticas. Por ejemplo, un predicado add para sumar numerosen Prolog se podrıa definir como4:

add(X,Y,Z) :- Z is X+Y.

El objetivo add(3, 4, Z) producirıa la respuesta Z = 7, que es el resultado esperado. Peroel objetivo add(X, 4, 7) produce un error en ejecucion (no es capaz de encontrar la res-puesta X = 3). Aquı se ha perdido la reversibilidad. El objetivo anterior, en realidad,esta planteando al sistema la restriccion lineal X + 4 = 7 en la forma 7 is X + 4, peroel predicado is tiene condiciones en cuanto al modo de uso: en una llamada de la formaX is E, E debe ser una expresion aritmetica sin variables.

En programacion logica, y en Prolog en particular, este problema se ha abordadomediante la incorporacion de restricciones aritmeticas sobre reales. Algunas implementa-ciones de Prolog incluyen un resolutor de restricciones aritmeticas (en particular el sistemaSiscstus Prolog,[Gro96]) y son capaces de solucionar restricciones lineales5. Con estos reso-lutores, un sistema de ecuaciones lineales como X + Y = 2, X − Y = Z, puede resolverseobteniendo la respuesta Y = 2−X, Z = −2 + 2 ∗X. T OY tambien admite este tipo derestricciones que se estudiaran en 2.3.9.

3Aunque sin clases de tipos, una potente extension ([Jon94, PJ93]) al sistema de Hindley-Milner([DM82]), incorporada a Haskell.

4En Prolog la llamada de la forma X is E, produce la unificacion de la variable X con el resultado deevaluar la expresion aritmetica E. Por ejemplo, X is (3 + 4) ∗ 2 unifica X con 14.

5Sobre restricciones no lineales tambien se han hecho estudios, como [Hon92, Han93, Han95a]

Page 12: Un lenguaje lógico funcional con restricciones1

12 CAPITULO 1. INTRODUCCION

1.4. Funciones indeterministas

Otro de los aspectos destacados de T OY son las funciones indeterministas. En pro-gramacion funcional las funciones son entendidas en el sentido matematico usual. Unafuncion, tomando los mismos argumentos, solo puede producir un resultado (que esta de-terminado por la funcion y los argumentos en cuestion). En T OY esto no es ası y unafuncion puede devolver distintos valores al evaluarse sobre los mismos argumentos (puedenentenderse como funciones multivaluadas).

Supongamos la siguiente definicion:

coin = 0coin = 1

Segun esta definicion coin (resultado de lanzar una moneda al aire) es una funcion que notoma ningun argumento y puede producir tanto el resultado 0, como 1. Esta definicion enHaskell no es correcta, aunque el compilador no produce error6. En T OY esta definiciones correcta y para una restriccion como coin == X, el sistema encuentra las respuestasX == 0 y X == 1 (coin es una funcion indeterminista).

Este tipo de funciones permite expresar de forma concisa y simple algunas operacio-nes de naturaleza indeterminista. Tambien pueden utilizarse en vez de los predicados enalgunas situaciones, con ciertas ventajas. Las secciones 2.4 y 2.5 contienen algunos ejem-plos ilustrativos del interes de las funciones indeterministas desde el punto de vista de laprogramacion.

1.5. Organizacion del trabajo

El contenido del trabajo esta organizado en cinco capıtulos, de los que este es el primero.En el segundo se hace una descripcion del sistema que incluye el manejo del entorno. Acontinuacion se hara un recorrido por las distintas construcciones sintacticas que admite ellenguaje, mostrando ejemplos en los que se aprecia la utilidad de cada una de ellas. Despuesveremos dos ejemplos de programacion en los que se resuelven problemas concretos. Elcapıtulo termina con una breve compacion con otros estilos de programacion declarativos.

El segundo capıtulo trata exhaustivamente la traduccion de programas T OY a codigoProlog. Aquı se estudiara el manejo y resolucion de las restricciones de igualdad y des-igualdad, el tratamiento del orden superior, la Estrategia Guiada por la Demanda que seutiliza para la evaluacion de funciones y, por ultimo, las restricciones sobre reales.

En el tercero se abordan algunos aspectos de la semantica del lenguaje. Presentaremosun calulo operacional que refleja la evaluacion perezosa, las funciones indeterministas y lasrestricciones de igualdad y desigualdad. Tambien justificaremos formalmente la correccionde la Estrategia Guiada por la Demanda explicada en el capıtulo anterior. No obstante,el calculo que presentamos no incluye el orden superior, los tipos y las restricciones sobrereales.

El ultimo contiene algunas conclusiones.Por ultimo se incluyen algunos apendices con la gramatica del lenguaje, los programas

que implementan la construccion de arboles definicionales y la generacion de codigo, elprograma de procesado y salida de respuestas, y los archivos de primitivas del lenguaje.

6La reduccion de la expresion coin produce (solo) el resultado 0.

Page 13: Un lenguaje lógico funcional con restricciones1

Capıtulo 2

El sistema T OY

2.1. Introduccion

En este capıtulo se presenta una introduccion al sistema T OY en la que se explica elmanejo del entorno y la sintaxis del lenguaje, a modo de guıa de usuario.

Con respecto al entorno, T OY ofrece un sencillo interprete de comandos integrado enel sistema, que facilita las tareas de escritura, compilacion y ejecucion de un programa.Todos los comandos, y en general, todo el proceso de comunicacion con el sistema se realizadesde la lınea de comandos del mismo.

La sintaxis esta inspirada en el lenguaje funcional Haskell [HFP97, PH97], y su as-pecto es, por tanto, claramente funcional (el apendice A contiene la gramatica completadel lenguaje). A lo largo de la exposicion se ha procurado incluir ejemplos ilustrativosque muestren la utilidad y el potencial expresivo de las distintas construcciones sintacti-cas de T OY. El sistema cuenta con un repertorio de funciones predefinidas que tambienestudiaremos.

El capıtulo contiene ademas, algunos ejemplos de programacion que integran las dis-tintas posibilidades del lenguaje, en especial las restricciones sobre reales y las funcionesindeterministas, que son las dos cualidades fundamentales que aporta T OY a la combi-nacion de los paradigmas logico y funcional. Por ultimo se hace una breve comparacioncon la programacion logica y con la funcional, en especial con Prolog.

2.2. El entorno

2.2.1. Arrancando el sistema

T OY (version 2.0) esta disponible en

http://mozart.sip.ucm.es/incoming/comprimidos/toy.tar.gz

Este archivo se puede desempaquetar y descomprimir con el comando UNIX:

gunzip -c toy.tar.gz | tar -xvf -

que genera tres directorios:

./toySystem: contiene los archivos del compilador propiamente dicho (toy.ql, toy.ini,basic.toy, primitives.ql, primitivesClpr.ql y misc.toy),

13

Page 14: Un lenguaje lógico funcional con restricciones1

14 CAPITULO 2. EL SISTEMA T OY

./examples: algunos ejemplos de programacion en T OY,

./manual: manual de usuario.

El archivo toy.ini es un archivo de configuracion que se tratara en 2.2.7, en basic.toy seencuentran los tipos de las funciones predefinidas de T OY que se abordaran en 2.3.15 ymisc.toy es un archivo de utilidades (funciones y predicados comunes) que se le proporcio-nan al usuario (vease el apendice C). El resto de archivos pertenecen al nucleo del sistemay no son editables (salvo que se disponga de una distribucion con el codigo fuente).

Debe tenerse presente que T OY esta completamente implementado en Prolog y esimprescindible disponer del sistema Sicstus Prolog (version 3#3 o superior) para ejecutarlo.

Para arrancar T OY basta con seguir estos pasos:

desde el directorio toySystem iniciar una sesion de Sicstus,

cargar T OY desde el prompt de Sicstus:

| ?- load(toy).

Sicstus cargara todas las librerıas necesarias y los archivos de T OY.

Cuando el paso previo ha concluido T OY esta cargado en memoria y mostrara elsiguiente mensaje:

TOY 2.0 30th September, 1997TYPE "/h." FOR HELP.

y el prompt del sistema:

TOY>

El sistema esta preparado para funcionar.

En sistemas Unix, para automatizar el proceso, es util incluir un alias de la formatoy=sicstus -l ~/toySystem/toy, reemplazando el path y la invocacion a Sicstus deforma apropiada.

Es posible suspender la ejecucion en cualquier momento invocando al mecanismo deinterrupcion de Sicstus mediante la combinacion de teclas < Ctrl > +c1. Entonces Sicstusmostrara el mensaje:

Prolog interruption (h for help)?

En este punto se puede continuar la ejecucion con ‘c’ o bien abortarla con ‘a’.

2.2.2. Comandos basicos

T OY proporciona un sencillo interface de comunicacion con el usuario mediante unpequeno repertorio de comandos. Con el comando /h. o /help. se obtiene un brevemenu de ayuda. Todos estos comandos deben ir precedidos del sımbolo especial ‘/’ yterminar con ‘.’, y se interpretaran como un termino Prolog, es decir: los argumentosdeben ir entre ‘(’ y ‘)’, No se admiten espacios en blanco entre el comando y ‘(’. Si los

1En una sesion T OY siempre esta ejecutandose Sicstus en segundo plano, sin embargo, el usuario nopuede interactuar directamente con Sicstus mas que para suspender la ejecucion con esta combinacion deteclas.

Page 15: Un lenguaje lógico funcional con restricciones1

2.2. EL ENTORNO 15

argumentos contienen espacios en blanco o sımbolos como ‘/’ (para especificar un path,por ejemplo), entonces deben ir entre comillas simples (’). Por ejemplo, los siguientescomandos son reconocidos por T OY:

/compile(my_program)./ compile( my_program )./load(one_of_my_programs)./system(’cd ..’)./cd(..)./cd(’/home/local/bin’)./q./help.

Y los siguientes no son reconocidos:

/compile (my_program)./cd(/home/local/bin)./cd(../..).

Algunos comandos hacen llamadas directas al sistema operativo:

/cd(< dir >) cambia el directorio de trabajo de T OY al directorio < dir >,

/system(< comm >) lanza el comando < comm > al sistema operativo (a la shellcorrespondiente). Es el modo mas directo que ofrece T OY para comunicarse conel sistema operativo, de hecho, el comando /cd(< dir >) puede obtenerse como/system(’cd < dir >’) y se incluye expresamente por comodidad para el usuario.Otro ejemplo valido es:

/system(’cp my_program.toy /home/users/axterix/examples/.’).

/q, /quit, /e o /exit terminan la sesion T OY.

En los siguientes apartados se explica el funcionamiento del resto de comandos.

2.2.3. Compilando y ejecutando programas T OYLa extension de los archivos que contienen programas T OY es por defecto ‘.toy’,

aunque se admite cualquier otra. Cualquier referencia a un archivo fuente sin extensionexplıcita sera completada con la extension ‘.toy’. T OY traduce los programas de usuarioa codigo Prolog, es decir, el codigo objeto generado por el sistema es Prolog. Ası, la compi-lacion del archivo < file >.toy produce como resultado el archivo Prolog < file >.pl 2

en el que el sistema escribe el codigo Prolog correspondiente a traduccion de las funcionesy predicados que contiene el archivo de entrada, junto con otra informacion. Para queT OY pueda ejecutar este programa (codigo Prolog) debe ser compilado previamente porSicstus. Este proceso se simplifica y se hace relativamente transparente al usuario mediantetres comandos que proporciona T OY:

/compile(< file >) compila el archivo < file > (o < file >.toy si no tieneextension) y genera el archivo < file >.pl. Este archivo no es compilado por Sicstus,de modo que no se pueden resolver objetivos para este programa al final del proceso.

2La extension .pl es la que utiliza por defecto Sicstus Prolog.

Page 16: Un lenguaje lógico funcional con restricciones1

16 CAPITULO 2. EL SISTEMA T OY

/load(< file >) carga el programa < file >.pl, previamente compilado por T OY.Realmente se invoca a Sicstus para que compile el programa Prolog generado porT OY tras ejecutar el comando compile.

Cuando (mediante programa) se incorpora una definicion para una funcion pre-viamente utilizada en la sesion actual de T OY, Sicstus producira un mensaje deadvertencia:

The procedure Name/Arity is being redefined.Old file: ...New file: ...

Do you really want to redefine it? (y, n, p, or ?)

Para cargar la nueva definicion debe escogerse ‘y’, sin embargo, es frecuente redefinirsimultaneamente varias funciones y es util elegir la opcion ‘p’ para que el sistema nohaga una consulta de este tipo por cada una de ellas y tome directamente todas lasdefiniciones nuevas.

/run(< file >) hace en secuencia las dos operaciones anteriores /compile(< file >)+ /load(< file >). Este sera el comando que se utilice con mas frecuencia.

No es posible compilar simultaneamente varios programas (compile, load y run admi-ten solo un argumento), pero puede conseguirse el mismo efecto con la directiva includeque se explicara en 2.3.12.

En la compilacion de un programa, T OY procesa la entrada en varias fases. En primerlugar chequea la sintaxis, las dependencias funcionales y los tipos. Si no se produce ningunerror se procede a la generacion de codigo objeto (codigo Prolog). Durante la compilacionproporciona al usuario informacion sobre el estado en que se encuentra y sobre los erroresque pueda contener la entrada.

Una vez compilado el programa el usuario puede plantear objetivos para dicho progra-ma y lanzalos al sistema desde la lınea de comandos como veremos en 2.3.14. Ejecutar unprograma en T OY es resolver objetivos (restricciones) para dicho programa, como ocurreen otros sistemas como Prolog.

2.2.4. Informacion sobre funciones

Hay dos comandos que permiten obtener informacion sobre las funciones definidas enun programa. El primero de ellos es /type(< fun >) que muestra el tipo de la funcion< fun > en el caso de que dicha funcion este definida en el programa cargado en memoria.Por el momento, no es posible obtener el tipo de una expresion general con este comando,como hacen otros lenguajes funcionales.

El otro comando es /tree(< file >,< fun >) que ofrece informacion sobre la traduc-cion que ha producido T OY para la funcion < fun > definida en el archivo < file >. Enconcreto, muestra el arbol definicional (vease 3.10) asociado a dicha funcion. Esta infor-macion es solo util para aquellos usuarios que posean algunas nociones sobre la estrategiaque utiliza el sistema para la evaluacion de las funciones, o bien para depuracion.

Page 17: Un lenguaje lógico funcional con restricciones1

2.2. EL ENTORNO 17

2.2.5. Salvaguarda de sesiones

Otro de los comandos del sistema es /save(< file >), que sirve para volcar al archivo< file > el estado actual de una sesion con todas las definiciones de funciones, predicados,tipos, etc, que existen en ese momento en memoria. Ademas, se hace una copia de todoslos archivos que necesita el sistema para arrancar. Al ejecutar el archivo < file > desdeun interprete de comandos de UNIX (shell) se restaura automaticamente el estado en quese encontraba el sistema cuando se salvo el contexto con el comando save.

Este comando es una forma de simular un autentico programa ejecutable y es utilcuando se utilice con frecuencia un programa T OY. Sin embargo, este programa no esrealmente un ejecutable porque necesita el sistema Sicstus. Puede utilizarse tambien paraconseguir una copia compilada de T OY que reduce notablemente el tiempo de carga delsistema, siguiendo los siguientes pasos:

iniciar una sesion T OY del modo habitual,

cambiar el directorio actual a aquel en el que se desea obtener la copia compiladamediante el comando cd,

ejecutar el comando /save(< file >) siendo < file > el nombre de la copia que sedesea obtener (por ejemplo /save(toy)),

abandonar la sesion actual con el comando /q

Tras este proceso, en el directorio elegido se ha creado el ejecutable < file > y algunosarchivos mas. La ejecucion de < file > arrancara inmediatamente el sistema.

2.2.6. Activacion de las restricciones sobre los numeros reales

El sistema tiene basicamente dos modos de uso: con restricciones sobre los reales ysin ellas. Como es natural, el primer modo de uso incrementa la potencia del sistemahaciendolo mas expresivo. De hecho, cualquier objetivo que se pueda resolver sin hacer usode las restricciones puede resolverse exactamente igual en el modo de uso que las incluye(no hara uso de ellas), excepto en eficiencia. La resolucion de restricciones sobre realesse apoya directamente en el resolutor que ofrece Sicstus en la librerıa clpr (vease [Hol95]o el propio manual de Sicstus [Gro96, Gro97] para una descripcion detallada de dichalibrerıa). Una vez cargado en memoria dicho resolutor, Sicstus no es capaz de distinguira priori si un determinado objetivo hara o no uso de las restricciones y mantiene unaactitud conservadora suponiendo que sı que utilizara tales restricciones. De este mododebe mantener informacion adicional3 para las variables que intervienen en el computo,incrementando, en consecuencia, el coste de la resolucion de objetivos independientementede que hagan uso o no de las restricciones4.

Por lo tanto, la razon de los dos modos de uso se encuentra en la eficiencia del sistema.Hay dos comandos para intercambiar dichos modos:

3La informacion sobre las restricciones se almacena en forma de atributos asociados a las variables,mediante la librerıa de atributos con la que cuenta Sicstus ([Gro97]).

4Algunos predicados deben redefinirse para tener en cuenta los atributos de las variables para mantenerla consistencia. El tratamiento de los atributos supone un incremento del coste computacional. En parti-cular, una operacion tan frecuente como la unificacion de dos terminos necesita efectuar otras operacionessobre los atributos de las variables de ambos terminos.

Page 18: Un lenguaje lógico funcional con restricciones1

18 CAPITULO 2. EL SISTEMA T OY

/cflpr (constraint functional-logic programming over reals) prepara el sistema paratrabajar con restricciones sobre los reales. Las funciones primitivas aritmeticas nece-sitan nuevas definiciones que se cargaran automaticamente y produciran el mensaje:

The procedure primInfix/3 is being redefined.Old file: /home/jaime/toy/toySystem/toy.plNew file: /home/jaime/toy/toySystem/primitivesClpr.pl

Do you really want to redefine it? (y, n, p, or ?)

El usuario debe responder p para permitir la carga de las nuevas definiciones. A partirde este momento todos los computos numericos, una vez procesados por T OY yreducidos a restricciones inteligibles para el resolutor, seran enviados a este.

/nocflpr descarga el resolutor y restaura las funciones primitivas originales.

Una vez que se han activado las restricciones sobre reales con el comando ‘/cflpr’, elresolutor permanece en memoria durante toda la sesion aunque se desactiven las restriccio-nes. El comando ‘/nocflpr’ le indica al sistema que no debe hacer uso de las restricciones,pero no puede restaurar totalmente el estado anterior del sistema, no puede descargar el re-solutor de memoria5. En consecuencia, los programas que no hagan uso de las restriccionessobre reales ofreceran un mejor rendimiento si estas no se activan durante la sesion, ya queno se forzaran determinadas operaciones (relativamente costosas) que no son necesarias.

Por todo lo expuesto, el procedimiento de carga descrito en 2.2.1, por defecto carga elsistema sin activar las restricciones sobre los reales y es el usuario quien debe activarlasexpresamente. Sobre la forma de uso de las restricciones trataremos en 2.3.9.

2.2.7. Definicion de nuevos comandos para T OYEn los apartados anteriores hemos hecho un recorrido por todos los comandos que

proporciona el sistema. El repertorio es reducido, sencillo y suficiente para acceder a todaslas prestaciones de T OY.

Sin embargo, hay otros comandos que, sin ser imprescindibles, son de uso frecuentey permiten una utilizacion mas amigable del sistema. En T OY, una vez fijado un re-pertorio mınimo hemos optado por facilitar al usuario un mecanismo sencillo y versatilpara definir nuevos comandos personalizados de comunicacion con el sistema operati-vo. El archivo ‘toy.ini’ que se incluye en la distribucion admite hechos Prolog queT OY interpretara como comandos. Por ejemplo, para definir un nuevo comando /dirque muestre todos los archivos del directorio actual (en sistemas Unix), podemos introdu-cir la siguiente lınea en el archivo toy.ini:

command(dir,0,[],["ls -l"]).

El primer argumento es el nombre del comando (dir), el segundo el numero de argu-mentos (0, en este caso), el tercero es la lista de argumentos que se representaran comovariables Prolog (deben comenzar por mayuscula) que van usarse en la construccion delcomando; y el ultimo es la lista de cadenas que han de concatenarse para construir elcomando (una sola cadena en este caso). Con esta definicion el comando /dir ya formaparte del repertorio del sistema.

5verb+/nocflpr+ no es capaz de descargar el resolutor de memoria porque Sicstus no proporciona unpredicado para descargar librerıas.

Page 19: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 19

Otra utilidad comun es el acceso a un editor de texto desde la lınea de comandos. Siqueremos utilizar el editor emacs podemos incluir la lınea:

command(edit,1,[File],["emacs ",File,".toy &"]).

De esta forma, el comando /edit(< file >) arrancara el editor emacs con el archivo< file > .toy y devolvera el control a T OY. Notese el uso de la variable File en laconstruccion del comando en el cuarto argumento. El sistema interpretara el contenido delas variables logicas como cadenas.

2.3. El lenguaje T OYLa sintaxis de T OY es muy similar a la de algunos lenguajes funcionales, como Haskell

([PH97]) o Gofer ([Jon]), aunque utiliza algunas construcciones de Prolog y otras propias.La diferencia mas notable de la sintaxis de T OY con respecto a la de Haskell es

que las variables comienzan con una letra mayuscula, excepto las variables anonimas quesiempre comienzan con ‘ ’. Los identificadores para tipos de datos, constructoras, funcionesy predicados comienzan con una letra minuscula.

Los comentarios se escriben como en Sicstus:

comenzando con ‘ % ’ y terminando con la lınea,

encerrados entre ‘/*’ y ‘*/’ (se admiten comentarios anidados)

En general un programa en T OY esta formado por definiciones de tipos de datos, aliasde tipo, funciones, predicados, operadores infijos y sentencias de inclusion de arhivos. Acontinuacion hacemos una descripcion de cada una de estas construcciones. El apendice Acontiene la gramatica completa del lenguaje.

2.3.1. Definicion de tipos de datos

Las definiciones de tipos de datos pueden aparecer en cualquier parte del programay tienen la misma forma que en Haskell; de hecho, T OY utiliza el sistema de tipos deHindley-Milner. Por ejemplo, el tipo de los pares puede definirse ası:

data pair A = p A A

Esta declaracion introduce el tipo de las parejas de cualquier tipo (pero el mismo paralas dos componentes). El parametro o variable de tipo A que aparece en la definicion haceque sea un tipo un tipo polimorfico: representa tanto las parejas de enteros, como lasde caracteres o las de listas. Ademas, introduce el nuevo sımbolo de constructora p, conel que puede representarse, por ejemplo, el par de enteros p 4 5, o el par de caracteresp ’a’ ’b’, con tipos pair int y pair char respectivamente (T OY tiene predefinido el tipode las tuplas con el que pueden representarse los pares de una forma mas sencilla, comose vera mas adelante).

La sintaxis general de una declaracion de tipo es:

data T X1...Xn = c1 TS11 . . . TS1n1

| . . .| ck TSk1 . . . TSknk

(i)

Page 20: Un lenguaje lógico funcional con restricciones1

20 CAPITULO 2. EL SISTEMA T OY

donde T es el identificador de tipo, las Xi son variables de tipo que parametrizan el tipo.El sımbolo ‘|’ se utiliza para separar las distintas construcciones de las que se compone eltipo. Cada ci es una constructora de datos de aridad ni y tipo:

TSi1 → . . . → TSini → T X1 . . . Xn

siendo cada TSij un tipo construido con la siguiente sintaxis:

TS = X % variable de tipo| tc % tipo constante| (tc TS1 . . . TSm) % tipo construido

Las declaraciones de tipos de datos en un programa T OY deben satisfacer ademas lassiguientes restricciones:

no puede haber dos declaraciones de tipo con el mismo nombre,

el lado izquierdo de la declaracion debe ser lineal, es decir, las variables X1, ..., Xn

que aparecen en lado izquierdo de la declaracion deben ser distintas,

las variables del lado derecho de la declaracion deben ser del conjunto {X1, ..., Xn},

El caso n = 0 en la expresion i corresponde a la definicion de tipos constantes o nopolimorficos como, por ejemplo, el tipo de los pares de enteros que puede declararse ası:

data pairInt = p int int

y el caso ni = 0 para todo i = 1..k corresponde al caso de constructoras constantes, comoel tipo enumerado de los colores:

data colour = red | green | blue

que introduce el nombre colour como un nuevo tipo de datos y los nombres red, greeny blue como (unicas) constructoras o valores del tipo.

T OY admite tipos recursivos como la siguiente definicion para los numeros naturales:

data nat = zero | suc nat

ası como la construccion de nuevos tipos a partir de tipos ya definidos (o predefinidos enel sistema) como los arboles binarios con numeros naturales en las hojas:

data treenat = leaf nat | branch treenat treenat

y tipos mutuamente recursivos como (pares e impares):

data even = evenzero | evensuc odddata odd = oddsuc even

Otro ejemplo de tipo polimorfico y recursivo es el de los arboles binarios con hojas detipo polimorfico, que puede definirse como:

data tree A = leaf A | branch (tree A) (tree A)

Page 21: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 21

2.3.2. Tipos predefinidos del sistema

Los siguientes tipos estan predefinidos en T OY:

bool, definido como data bool = true | false (esta declaracion se encuentra enbasic.toy),

int, real, que representan los tipos especiales de los numeros enteros y reales respec-tivamente. Cada numero (entero o real) es una constructora de aridad 0 y hay portanto un numero infinito de ellas. La definicion de los enteros serıa de la forma:

data int = ... -2 | -1 | 0 | 1 | 2 ...

que obviamente no es una definicion valida para el sistema. Tanto los enteros comolos reales estan definidos a bajo nivel, es decir, su declaracion no es visible al usuario(no tienen declaracion en basic.toy).

char es el tipo predefinido para caracteres individuales. Los caracteres deben ir entrecomillas simples como ’a’, ’@’, ’9’ o ’-’. Puesto que hay un numero finito de ellos(codigos ascii) son definibles en el sistema, sin embargo, tambien se implementan abajo nivel como los numeros. La siguiente tabla muestra el conjunto de caracteresespeciales (no-imprimibles) que necesitan secuencias de escape (barra invertida ‘\’):

Caracter T OY significado’\\’ barra invertida’\’ ’ comilla’\”’ comilla doble’\n’ salto de lınea’\r’ retorno de carro’\t’ tabulador horizontal’\v’ tabulador vertical’\f’ salto de pagina’\a’ senal acustica’\b’ espacio’\d’ borrado’\e’ escape

El tipo de las cadenas no es predefinido, pero puede definirse facilmente como listade caracteres con la declaracion (alias de tipo, tratados en el siguiente apartado):

type string = [char]

como puede verse en el archivo de utilidades misc.toy que acompana a la distribucion.Para facilitar el uso de cadenas T OY admite la representacion de constantes de tipocadena entre comillas dobles. Por ejemplo, la cadena

[’t’,’h’,’i’,’s’,’ ’,’i’,’s’,’ ’,’a’,’ ’,’s’,’t’,’r’,’i’,’n’,’g’]

puede representarse como

Page 22: Un lenguaje lógico funcional con restricciones1

22 CAPITULO 2. EL SISTEMA T OY

"this is a string"

El tipo de las listas polimorficas tiene una sintaxis especial, similar a la de Haskell.Por ejemplo, [int] representa el tipo de las listas de numeros enteros. Igual queen Haskell la constructora ‘[]’ denota la lista vacıa y ‘:’ es el operador infijo deconstruccion de listas. T OY no contiene una definicion explıcita de las listas, peroinformalmente puede asumirse la siguiente declaracion:

data [A] = [] | A:[A]

(Una lista de tipo A es, o bien la lista vacıa, o bien una lista formada por un elementode tipo A seguida de una lista de tipo A).

El sistema admite ademas la representacion de listas en notacion Prolog, es decir, laexpresion [X | Xs] representa la lista (X:Xs).

Las tuplas son tambien un tipo predefinido especial, ademas de por la notacionque utilizan, porque representan un tipo de datos con infinitas constructoras. Porejemplo, (int,int) representa el tipo de los pares de enteros y (int,int,int)representa el tipo de las ternas de enteros. Es decir, se utiliza una construccionsimilar para representar tuplas de cualquier aridad: (T1, ..., Tn) representa el tipo delas n-tuplas con componentes de tipos T1, ..., Tn. En el lenguaje serıa definible el tipode las tuplas de aridad determinada; por ejemplo para las tuplas de aridad 3 (ternas)se podrıa hacer la declaracion:

data tup3 A B C= t A B C

que introduce la constructora t (las ternas de enteros se pueden definir instanciandoeste tipo: tup3 int int int). Pero no se puede definir el tipo de las tuplas de cualquieraridad y por eso esta predefinido a bajo nivel (no visible al usuario).

2.3.3. Alias o sinonimos de tipo

Los alias de tipo son construcciones muy sencillas que pueden aparecer en cualquierparte del programa. Son innecesarios en realidad, pero permiten escribir programas masbreves y legibles. Pueden entenderse como macros parametricas definidas por el usuarioque sirven para dar nombre a tipos ya existentes. El tipo de las cadenas o tipo string yase definio en el apartado anterior como lista de caracteres mediante el alias:

type string = [char]

La forma general de un alias de tipo es:

type AliasName X1...Xn = typeExpression

donde AliasName es el nuevo nombre de alias de tipo, X1, ..., Xn son n variables y typeEx-pression es un tipo valido que puede hacer uso de las variables X1, ..., Xn. Deben satisfacerademas las siguientes restricciones:

el lado izquierdo de la declaracion debe ser lineal, es decir, las variables X1, ..., Xn

deben ser distintas,

Page 23: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 23

las variables del lado derecho typeExpression deben ser del conjunto {X1, ..., Xn},la definicion de un alias puede depender de otros alias o tipo de datos, siempre queestos hayan sido previamente definidos (o esten predefinidos en el sistema),

no se admite recursion, ni recursion mutua en las definiciones de alias de tipo.

Supongase, por ejemplo, un programa que utiliza numeros complejos representadoscomo pares de reales. Estos pares no son una construccion nueva y, en consecuencia, eltipo de los complejos no es un tipo nuevo en realidad, ni introduce sımbolos de construc-tora nuevos. Es decir, no tiene sentido hacer una declaracion data complex = .... Sinembargo, sera de utilidad poder hacer referencia al tipo de los complejos en las declara-ciones de funciones que operan sobre ellos. Esto puede conseguirse con la declaracion delalias:

type complex = (real,real)

Tambien podrıa definirse el tipo (mas general) de los pares y declarar los complejoscomo una instancia:

type pair A B = (A,B)type complex = pair real real

2.3.4. Definicion de funciones

Las funciones en T OY se definen mediante reglas de reescritura condicionales repre-sentadas como ecuaciones condicionales. Por ejemplo, la funcion append que toma doslistas y devuelve la lista resultante de concatenarlas se puede definir como:

append [] Ys = Ysappend [X|Xs] Ys = [X|Zs] <== append Xs Ys == Zs

Esta es la notacion currificada tıpica (salvo por la restriccion <== append Xs Ys == Zs)de algunos lenguajes funcionales como Haskell, y como es habitual en estos lenguajes, laaplicacion funcional asocia a la izquierda. Una aplicacion de append es, por ejemplo,append [1,2] [3,4], que es equivalente a (append [1,2]) [3,4]. En otras palabras,la aplicacion de append al argumento [1,2] devuelve como resultado la nueva funcion(append [1,2]) que se aplica a su vez a [3,4].

La sintaxis general de una regla es:

f t1...tn︸ ︷︷ ︸cabeza

= e︸︷︷︸cuerpo

<== C1, ..., Cm︸ ︷︷ ︸restricciones

(ii)

donde f es el nombre de la funcion, los ti son patrones (ver abajo), e es una expresion (verabajo) y los Ci son restricciones de la forma e13e2 separadas por ‘,’ y donde 3 ∈ {==, /=}.

Una regla de esta forma tiene una lectura condicional bastante intuitiva: la expresionf t1...tn se puede reducir a la expresion e si se satisfacen las restricciones C1, ..., Cm. Enlas restricciones el sımbolo == se utiliza para la igualdad estricta y /= para desigualdades.En 2.3.8 estudiaremos con mayor detalle las restricciones de igualdad y desigualdad (sobrelas aritmeticas trataremos en 2.3.9).

En el caso de que la regla no tenga restricciones (m = 0) se omitira el sımbolo <==y una restriccion de la forma e == true puede abreviarse a e (o dicho de otro modo, una

Page 24: Un lenguaje lógico funcional con restricciones1

24 CAPITULO 2. EL SISTEMA T OY

expresion sin uno de los sımbolos {==, /=} en la raız es interpretada automaticamentepor el sistema como la restriccion e == true).

La sintaxis general para las expresiones es:

E = X % variable| num % numero (entero o real)| (E1, ..., En) % tupla| c % constructora| f % funcion| (E1 E2) % aplicacion

(iii)

siendo E1, E2 expresiones. Dado que la aplicacion funcional asocia por la izquierda, puedenomitirse los parentesis de acuerdo con ello. Ası, por ejemplo, ((c X) Y ) es lo mismo quec X Y . En general, E1 E2 E3...En es lo mismo que (...((E1 E2)E3)...En). Notese que tantolos numeros como las tuplas aparecen explıcitamente en la regla de formacion a pesarde ser constructoras. El motivo es que representan infinitas constructoras (vease 2.3.2) ynecesitan un tratamiento especial (no quedan capturadas por las otras alternativas de laregla).

Otro hecho importante es que la regla de formacion (iii) admite aplicaciones parcialestanto de funciones como de constructoras; por ejemplo, append [1] es una aplicacionparcial de append; (1:) es una aplicacion parcial de la constructora de listas ‘:’. Ambasson expresiones funcionales y tienen el mismo efecto al aplicarlas sobre una lista: colocan1 como cabeza y la lista argumento como cola.

Los patrones son un caso particular de expresiones que no contienen llamadas a funcion(aplicaciones totales de funciones). Esto quiere decir que son expresiones irreducibles oformas normales. La sintaxis general de los patrones es:

T = X % variable| num % numeros| (T1, ..., Tn) % tuplas| (c T1...Tm) % c constructora de aridad n, 0 ≤ m < n| (f T1...Tm) % f funcion de aridad n, 0 ≤ m < n

En particular, T OY admite patrones de primer orden que corresponden a los patronesde Haskell y son de la forma:

T = X % variable| num % numero (entero o real)| (T1, ..., Tn) % tuplas| (c T1...Tn) % c constructora de aridad n

siendo T1, ..., Tn patrones de primer orden. Pero ademas, T OY tambien admite patronesde orden superior, es decir, admite como patrones expresiones en las que pueden aparecerconstructoras y funciones aplicadas parcialmente. La sintaxis de estos patrones es:

T = X % variable| (c T1...Tm) % c constructora de aridad n, 0 ≤ m < n| (f T1...Tm) % f funcion de aridad n, 0 ≤ m < n

siendo T1, ..., Tn patrones cualesquiera (de orden superior en el caso de las tuplas). Porejemplo, el siguiente programa es correcto en T OY:

Page 25: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 25

% tipo de los naturalesdata nat = zero | suc nat

% suma de naturalesplus zero Y = Yplus (suc X) Y = suc (plus X Y)

f suc = true % suc es un patron de orden superiorf (plus X) = true % (plus X) es un patron de orden superior

Por lo que acabamos de ver, en T OY los conceptos de forma normal y patron sonsinonimos y pueden definirse como expresiones irreducibles. En programacion funcional(en concreto en Haskell) un patron no es cualquier expresion irreducible: no se admitenaplicaciones parciales. Por lo tanto, esto una caracterıstica destacada de T OY que ofreceposibilidades muy interesantes ([CR98, GHR97]).

Desde el punto de vista sintactico, T OY es muy poco restrictivo en cuanto a la formade las reglas que definen una funcion f : basta con que dichas reglas sean de la forma(ii) y que todas tengan el mismo numero n de argumentos, que es la aridad de programa(sobre los tipos hay algunas restricciones mas que veremos despues). Hay una condicionmas que no se refleja en la sintaxis y que el usuario debe conocer: por razones semanticas,la cabeza f t1...tn debe ser lineal, lo que significa que las variables no pueden tener masde una aparicion. T OY admite repeticiones de variables en las cabezas, pero en estecaso hace automaticamente una transformacion sintactica que elimina dichas repeticionesintroduciendo nuevas variables y restricciones de igualdad estricta en la regla. Por ejemplo,una regla como:

f X X = 0

sera traducida a:

f X Y = 0 <== X==Y

El hecho de que T OY admita cabezas no lineales debe entenderse como un azucar sintacti-co que permite abreviar la escritura de las reglas. En realidad, en ejecucion el sistemasiempre utiliza reglas con cabezas lineales6.

2.3.5. Tipos de las funciones

Toda funcion f definida en un programa T OY (mediante reglas de la forma f t1...tn =e <== e13e′1, ..., em3e′m, debe tener asociado un tipo principal τ1 → ... → τk → τ , dondeτ no es de la forma ‘ → ’ (no es un tipo funcional). Diremos que k es la aridad del tipode f (en contraste con la aridad de programa que es el numero de argumentos que tienenlas reglas de f) y deben cumplirse las condiciones:

n ≤ k (la aridad de programa es menor o igual que la aridad del tipo de la funcion),

para toda regla de f :6En distribuciones anteriores, el sistema producıa un WARNING cuando precisaba hacer esta transfor-

macion. Sin embargo, en la practica esta informacion no era especialmente relevante para el usuario y enla distribucion actual el mensaje esta deshabilitado.

Page 26: Un lenguaje lógico funcional con restricciones1

26 CAPITULO 2. EL SISTEMA T OY

• el tipo de cada patron ti es τi,

• el tipo de la expresion e debe ser τn+1 → ... → τk → τ ,

• para toda restriccion ei3e′i, las expresiones ei y e′i deben tener el mismo tipo.

Los tipos de las funciones son inferidos por T OY y, opcionalmente, pueden ser decla-rados en el programa de la misma forma que en Haskell:

f :: T1 → ... → Tk → T

Para la funcion append definida anteriormente se podrıa declarar el tipo:

append :: [int] -> [int] -> [int]

El operador -> asocia por la derecha (al reves que la aplicacion de funciones), con lo queeste tipo es equivalente a [int]->([int]->[int]) que es consistente con la forma deaplicacion de funciones: al aplicar append sobre un argumento de tipo [int] se obtieneuna nueva funcion con tipo [int]->[int].

Con las dos reglas anteriores para append el inferidor de tipos de T OY determinael tipo mas general de la funcion. De estas reglas se deduce que append toma dos listasy devuelve otra. Las tres listas deben ser del mismo tipo, pero este no esta concretado,puede ser cualquiera. Es decir, se infiere el tipo polimorfico [A]->[A]->[A], donde A esuna variable de tipo. El sistema entonces hace un contraste o comprobacion de tipos entreel tipo inferido y el tipo declarado y detecta que el tipo declarado es un caso particulardel inferido. Esto esta permitido y puede entenderse como una restriccion impuesta por elusuario sobre el modo de uso de la funcion: el usuario proporciona una definicion generalpara una funcion pero restringe el modo de uso a un caso mas particular (en este casola definicion sirve para listas cualesquiera y la declaracion de tipo restringe el modo deuso a listas de enteros). Cuando sucede esto, T OY produce un mensaje de advertencia owarning en tiempo de compilacion, pero respeta la declaracion del usuario:

TYPE WARNING: Inferred type is more general than declared one infunction append

Inferred type: [ _A ] -> [ _A ] -> [ _A ]Declared type: [ int ] -> [ int ] -> [ int ]Declared one remains.

Tambien puede declararse append como una funcion polimorfica que podra utilizarsepara concatenar dos listas del cualquier tipo y producir otra lista, siendo las tres listas delmismo tipo:

append :: [A] -> [A] -> [A]

Naturalmente, con esta definicion queda capturada la anterior cuyo uso era mas restrin-gido. Ahora el tipo declarado y el inferido son el mismo y el sistema no produce ningunmensaje.

La situacion que falta por explorar es cuando el tipo declarado es mas general queel inferido, pero tal situacion no es admisible. Intuitivamente, el usuario pretende definiruna funcion mas general que la que realmente esta definiendo. Por ejemplo, si declara eltipo A->B->C para append definida por las dos reglas anteriores, el usuario “define unafuncion que opera sobre listas, pero pretende que opere sobre cualquier tipo de datos

Page 27: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 27

y no necesariamente del mismo”. Sin embargo, no se pueden evaluar expresiones comoappend 1 2 o append [1,2] [’a’]7. En este caso se produce un mensaje de error:

TYPE ERROR: Contradictories types for function appendDeclared type: _A -> _B -> _CInferred type: [ _A ] -> [ _A ] -> [ _A ]with no possible conversion.Variable type is assumed to go on with the inference.

Cuando el sistema detecta un error de tipos, asocia un tipo variable a la expresion que loprovoca y continua la inferencia. Esto puede entenderse como la polıtica de recuperacionde errores del inferidor, con la que se pretende evitar la propagacion del error a otrasexpresiones que tambien producirıan error de tipo. Si se propagase el error, el resultadopodrıa ser una larga secuencia de errores de tipo provocados por una sola expresion, que envez de ayudar al usuario a localizar su error servirıa para lo contrario. Al asociar un tipovariable a la expresion problematica (mas interna) queda garantizado que esa expresionno provocara mas errores de tipo.

2.3.6. Funciones de orden superior y estructuras infinitas

Al igual que en Haskell, T OY admite funciones de orden superior, es decir, funcionesque toman funciones como argumentos, o funciones cuyo resultado es de tipo funcional.Un ejemplo de orden superior es la funcion map, que puede definirse como:

map F [] = []map F [X|Xs] = [F X|map F Xs]

Esta funcion recibe un parametro F de tipo funcional como primer argumento y unalista como segundo, y produce la lista resultante de aplicar F a cada elemento de la listaargumento. La expresion F X en el cuerpo de la segunda regla es la aplicacion de la expre-sion funcional F al elemento X, donde lo llamativo es que F viene dada como argumento, esdecir, la funcion F concreta es desconocida en la definicion de map. Por ejemplo, si tenemosel tipo de los naturales:

data nat = zero | suc nat

se puede evaluar la expresion map suc [zero, suc zero, suc (suc zero)]. El resul-tado se consigue aplicando suc a cada uno de los elementos de la lista argumento yes [(suc zero), (suc (suc zero)), (suc (suc (suc zero)))]. La potencia expresi-va de map reside en el hecho de que permite aplicar cualquier funcion a cualquier lista deargumentos, siempre que los tipos sean consistentes. En general, las funciones de ordensuperior son utiles para definir nuevas funciones de una forma sencilla y elegante. En elarchivo de utilidades misc.toy pueden encontrarse diversos ejemplos que utilizan map yotras funciones de orden superior.

El orden superior en T OY no termina aquı. A diferencia de los lenguajes funcionalespuros, en T OY es posible hacer computos que impliquen variables logicas de orden su-perior. Por ejemplo, dado un programa que contenga una definicion para append como laque vimos en 2.3.4, es posible resolver el objetivo F [1,2] [3] == [1,2,3]. Observese

7En Prolog, que no posee sistema de tipos, sı es posible concatenar listas de distintos tipos. Sin embargo,T OY es un lenguaje fuertemente tipado como Haskell, en el que esto no es posible.

Page 28: Un lenguaje lógico funcional con restricciones1

28 CAPITULO 2. EL SISTEMA T OY

que aquı la varible logica F representa un valor funcional, es decir, es una variable deorden superior. El sistema es capaz de resolver esta restriccion, encontrando para F elvalor append. Este tipo de variables suponen un recurso expresivo importante sobre el quevolveremos en 2.4.

Las posibilidad de manejar estructuras inifinitas es tambien habitual en los lenguajesfuncionales actuales. Un ejemplo clasico es la funcion from que ya se comento en 1.2, yque definıamos como:

from N = [N|from (N+1)]

Esta funcion genera (potencialmente) una secuencia de enteros infinita. Por ejemplo, laexpresion from 1 se evaluarıa a la lista [1,2,3,4,5,6,...]. En realidad este computo noterminarıa y este tipo de funciones no tienen mucha utilidad por sı mismas. Sin embargo,combinadas con otras funciones ofrecen posibilidades interesantes. Por ejemplo, la funcionnth definida como

nth N [X|R] = if (N==1) then X else nth (N-1) R

calcula el n-esismo elemento de una lista dada. Ahora es posible reducir la expresionnth 5 (from 100) que devolvera el valor 105.

Esta ultima reduccion es posible debido a la pereza del lenguaje: para calcular elquinto elemento de la lista generada por from 100, no es necesario evaluar completamenteesta lista (el computo no termina). En realidad solo es necesario calcular los primeros 5elementos, con los que la funcion nth ya puede calcular el quinto, que es 105.

2.3.7. Funciones indeterministas

La libertad sintactica que ofrece T OY en la definicion de funciones tiene otras con-secuencias especialmente interesantes desde el punto de vista semantico, que requierenmencion especial. No se han impuesto condiciones que impidan que el cuerpo de las re-glas contenga variables que no aparecen en la cabeza, ni tampoco se exigen condicionesde confluencia8. La ausencia de estas condiciones esta relacionada con el hecho de queT OY admita funciones indeterministas. Por ejemplo

% eleccion indeterminista entre dos valoreschoice X Y = Xchoice X Y = Y

es una funcion que toma dos valores y devuelve uno de ellos de forma indeterminista. Unafuncion indeterminista puede entenderse como una funcion multivaluada, es decir, unafuncion que, para unos mismos argumentos produce varios valores distintos.

La evaluacion de choice 0 1 producirıa 0 como primer valor, pero por backtracking seobtendrıa 1 como segunda posibilidad. En general, las funciones indeterministas puedenreemplazar a predicados en muchas ocasiones con algunas ventajas, como veremos enalgunos ejemplos en al final del capıtulo.

La introduccion de este tipo de funciones plantea algunas cuestiones en cuanto a laforma de evaluacion que debe implementar el sistema. Para ilustrarlo, consideremos elsiguiente ejemplo inspirado en [Hus92]:

8En otros sistemas como BABLOG estas condiciones se conocıan como inexistencia de variables extra(o determinismo local) y no ambiguedad respectivamente (en [AG94] y [LLR93] pueden encontrarse lasdefiniciones formales)

Page 29: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 29

coin = 0coin = 1

double X = X+X

Aquı, coin es una funcion indeterminista que puede reducirse tanto a 0 como a 1 (porbacktraking el sistema llevara a cabo ambas reducciones). La funcion double duplica elvalor del argumento (numerico) que recibe y no plantea problemas por sı misma. Pero,¿que sucede al reducir la expresion double coin?. Si se reduce (utilizando la regla de double)a la expresion coin + coin, es posible que mas tarde la primera expresion coin se reduzcaa 0 y la segunda a 1 (o viceversa), con lo que la expresion double coin se habrıa reducidoa 1. Este no es un resultado deseado de acuerdo con la semantica por call-time choice queadoptamos para nuestro lenguaje, siguiendo el enfoque de [Hus93].

Intuitivamente, call-time choice significa lo siguiente: dada una llamada f e1..., en, seelige un valor fijo para cada uno de los argumentos e1, ..., en antes de aplicar las reglaspara f . En nuestro ejemplo double coin, esto se puede conseguir evaluando coin antes deaplicar la regla de double, pero entonces la evaluacion no serıa perezosa. Para preservarla pereza del lenguaje y capturar la semantica por call-time choice se utiliza comparticiono sharing: double recibe el argumento coin sin evaluar, pero de modo que cuando en laexpresion coin + coin una de las expresiones coin se reduzca, la otra tome el mismo valorautomaticamente. De este modo, las soluciones obtenidas para la expresion inicial son 0 y2 como cabrıa esperar.

En (3.6) volveremos sobre el sharing y explicaremos en detalle como se implementa enT OY.

2.3.8. Restricciones de igualdad y desigualdad

En la seccion 2.3.4 veıamos la forma sintactica de las restricciones de igualdad y de-sigualdad pero no abordamos en profundidad el significado que tienen. En programacionfuncional existen igualdades (==) y desigualdades ( /=). Sin embargo, el significado esnotablemente diferente al que tienen en T OY. Por ejemplo, en Haskell una igualdad entredos expresiones es cierta si ambas se reducen a formas normales iguales. Ası, 3+4 == 7 sereduce a true mientras que 3 + 4 == 5 produce false. Pero una restriccion que contengavariables como x == 4, no puede evaluarse.

En Prolog tanto la igualdad (==) como la desigualdad (\ ==) entre terminos se tratandesde el punto de vista sintactico. Por ejemplo, 3+4 == 7 produce un fallo y 3+4\ == 7tiene exito (3 + 4 y 7 no son el mismo termino); X == X tambien produce exito, peroX == Y falla. No obstante, en Prolog tambien existe la unificacion: X = Y tiene exito,ligando X con Y . Pero no existen funciones, es decir, no hay reduccion o evaluacion comoen funcional. Por ejemplo, X = 3 + 7 tiene exito unificando X con el termino 3 + 7 (no seevalua la suma).

En T OY se habla de restricciones de igualdad y restricciones desigualdad (estrictas9)porque efectivamente ambas se comportan como restricciones. Una igualdad entre dosexpresiones es cierta si ambas expresiones pueden reducirse a una forma normal comun.Pero el concepto de reduccion en programacion logico funcional es mas amplio que enfuncional puro, ya que, por un lado son reducibles expresiones con variables, y por otro, la

9El calificativo estricta hace referencia a una cuestion puramente semantica y quiere decir que si algunode sus argumentos es indefinido (⊥) el resultado es infefinido.

Page 30: Un lenguaje lógico funcional con restricciones1

30 CAPITULO 2. EL SISTEMA T OY

reduccion es, en general, un computo indeterminista (una misma expresion puede reducirsea mas de una forma normal). De hecho, la reduccion en el contexto logico funcional sesuele llamar narrowing (estrechamiento), porque no es simplemente reescritura (tambienhay unificacion).

Por ejemplo, en T OY una igualdad como 3 + 4 == 7 tiene exito (se evalua la sumacomo en funcional). Tambien 3 + 4 == X tiene exito y ademas unifica la variable Xcon el valor 7. Es decir, la igualdad es cierta siempre que X sea 7, por lo que el sistemaproducira un exito y en la respuesta incluira la restriccion X == 7. Las restriccionesde igualdad en una respuesta pueden entenderse como restricciones o como sustituciones(ligaduras). En 2.3.14 veremos la forma y la interpretacion que tienen las respuestas en elsistema.

Una desigualdad estricta entre dos expresiones es cierta en T OY si ambas expresionespueden reducirse a expresiones que continen una constructora distinta en la misma posicion(conflicto de constructoras). Por ejemplo, [2, 3 + 4] /= [2, 5] tiene exito porque el primerargumento (el lado izquierdo de la desigualdad) puede reducirse a la expresion [2, 7] y elsegundo elemento de las listas (que ocupa la misma posicion en ambas expresiones) [2, 7]y [2, 5] es diferente (7 en un caso y 5 en el otro). Esta desigualdad en funcional tambiense reducirıa a true; pero en T OY, como restricciones que son, las desigualdades tambienpueden contener variables. Por ejemplo, [X, 3 + 4] /= [2, 7] produce un exito restringido aque X sea distinto de 2 (la restriccion X /= 2 forma parte de la respuesta).

Las desigualdades resultaran especialmente utiles no solo en los programas, sino tam-bien en las respuestas que calcula el sistema. Consideremos las funciones member, quecomprueba si un elemento pertenece a una lista dada, y size que calcula la longitud deuna lista:

member X [] = falsemember X [Y|Ys] = if X==Y then true else member X Ys

size [] = 0size [X|Ys] = if member X Ys then size Ys else (size Ys)+1

(La funcion if then else es una primitiva del sistema, 2.3.15).La restriccion size [X,Y] == N es cierta bajo las condiciones X == Y,N == 1,

pero tambien es cierta siendo N == 2 y X e Y distintos entre sı. La condicion de queX e Y sean distintos es facilmente expresable con una desigualdad: X /= Y . Pero sindesigualdades darıa lugar a una familia infinita de soluciones (todos los posibles pares deenteros distintos).

Otro ejemplo, puede ser la restriccion size [X] /= X. En este caso la desigualdadX /= 1 es una respuesta elegante que captura todas las posibles soluciones a la restriccion(de otro modo habrıa tambien infinitas soluciones).

Ya hemos comentado que una misma expresion puede tener mas de una reduccionposible. Este indeterminismo permite obtener distintas respuestas a una misma restriccion.Consideremos la funcion append definida en 2.3.4 y la restriccion append X [2] == Y.Haciendo reduccion (estrechamiento) con la primera regla de append, la igualdad se hacecierta con las restricciones X == [ ], Y == [2]. Aplicando la segunda regla (y despuesla primera), tambien se satisface la igualdad con las restricciones X == [A], Y == [A, 2](independientemente del valor que pueda tomar la variable A). Tambien es cierta con lasrestricciones X == [A, B], Y == [A,B, 2]. Esta restriccion tiene infinitas respuestas queel sistema ira calculando por backtraking.

Page 31: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 31

Pero el indeterminismo del sistema no acaba aquı. El hecho de admitir funciones in-deterministas en nuestro lenguaje tiene algunas consecuencias sobre las igualdades y des-igualdades que merecen algun comentario. Una misma igualdad puede producir un exitoy un fallo. Por ejemplo, consideremos la funcion coin definida en el apartado 2.3.7: laigualdad coin==0 tiene exito reduciendo coin a 0, pero produce fallo si coin se reduce a1.

Llevando lo anterior al extremo, en T OY una igualdad y una desigualdad entre dosmismas expresiones pueden ser satisfactibles simultaneamente. Las restricciones coin == 0,coin /= 0 tienen exito y esto es coherente con nuestra semantica de la igualdad y la des-igualdad: puesto que coin es una funcion indeterminista, la primera expresion coin puedereducirse a 0 y la segunda a 1, con lo que se satisfacen ambas restricciones. En las restric-ciones X==coin, X ==0, X /= 0 es distinto, puesto que ahora coin se reduce una solavez y el resultado se asocia a la variable X. Por lo tanto, X puede tomar los valores 0o 1, pero en ambos casos no se pueden hacer ciertas simultaneamente las restriccionesX==0, X /= 0.

2.3.9. Restricciones sobre los numeros reales

En 2.3.4 vimos que el sistema admite restricciones de igualdad y desigualdad, dejan-do excluidas las arimeticas deliberadamente. Realmente T OY no maneja restricciones dereales directamente, sino funciones aritmeticas. Decıamos que una expresion e en las res-tricciones sin uno de los sımbolos {==, /=} en la raız es interpretada automaticamentecomo la restriccion e == true. Ası pues, una expresion como 3 + X <= 5 se interpretacomo (3+X <= 5) == true, que es una igualdad que involucra a la funcion ’<=’ (menoro igual).

No obstante, el usuario dispone de toda la potencia de este tipo de restricciones([JM94, FHK+93, Coh90, Gro97]) y, en general, la conversion anterior pasara inadvertida.El sistema arranca inicialmente sin activar las restricciones (sin el resolutor en memoria)por motivos de eficiciencia segun se explico en 2.2.6. Puede activarlas con el comando/clpr.

Para reales se tienen las restricciones correspondientes a los operadores aritmeticoshabituales, {<, >,<=, >=}. Pero ademas se tienen restricciones de igualdad y desigualdad,que utilizan los mismos sımbolos de la igualdad y desigualdad estrictas, == y /=. Sinembargo, no tienen tienen el mismo comportamiento (las de reales deben ser tratadas porel resolutor). El sistema se encargara de distinguir en cada caso la clase de restriccion quese le presenta y la forma de procesarla, liberando al usuario de la incomodidad de utilizardistintos sımbolos. Todas las funciones aritmeticas son primitivas del sistema (2.3.15) cuyadeclaracion se encuentra en el archivo de la distribucion basic.toy (apendice B) y son ellaslas que se encargan de producir las verdaderas restricciones y enviarlas al resolutor deSicstus de manera apropiada.

No todas las restricciones numericas necesitan el resolutor. Por ejemplo, una restriccioncomo 4+5 < 10 puede resolverse sin el. Sin embargo, 4+X < 10 debe utilizar el resolutorpara encontrar la respuesta X < 6. En general, aquellas en las que no aparecen variablesno necesitan utilizar el resolutor.

El usuario es responsable de determinar si su programa utiliza o no restricciones sobrelos reales. Sin embargo, si se intenta resolver un objetivo que utiliza restricciones sin acti-varlarlas previamente, T OY detectara la anomalıa en tiempo de ejecucion y producira unmensaje de error sugiriendo la activacion:

Page 32: Un lenguaje lógico funcional con restricciones1

32 CAPITULO 2. EL SISTEMA T OY

RUNTIME ERROR: Variables are not allowed in arithmetical operations.(/cflpr. should be active to do this)

El resolutor de Sicstus es un resolutor de restricciones lineales, lo que significaque admite todo tipo de restricciones, pero solo resuelve las lineales. Las no lineales sesuspenden (previa normalizacion) a la espera de que se conviertan en lineales. Por ejemplo,un sistema de ecuaciones lineales como X + 2 ∗ Y == 8, 3 ∗X − Y == 3 puede resolverseobteniendo las sustituciones (en forma de restricciones) X == 2, Y == 3. Las no linealescomo X2 == 4, X > 0 se suspenden, y si no llegan a convertirse en lineales como eneste caso, T OY las presentara en forma normalizada como parte de la respuesta. En esteejemplo presentara 4−X2 == 0, X > 0 (a pesar de que X == 2 es solucion). En [Hol95]o en [Gro96] se trata este asunto con mayor profundidad.

2.3.10. Definicion de predicados

T OY admite definicion de predicados al estilo Prolog. Por ejemplo, el predicado appendpuede definirse como:

p_append [] Ys Ys :- truep_append [X|Xs] Ys [X|Zs] :- p_append Xs Ys Zs

Observese que, a diferencia de Prolog, se utiliza notacion currificada, las clausulas noacaban en “.” y no se admiten cuerpos vacıos, por lo que los hechos (como la primeraclausula de p_append) tienen como cuerpo true.

La sintaxis general de los predicados es:

p t1...tn : − C1, ..., Cn (iv)

siendo t1, ..., tn patrones y C1, ..., Cn restricciones (de la misma forma que en las funciones).En realidad, un predicado en T OY no es mas que una funcion booleana; de hecho

es una funcion que devuelve el valor true. La expresion general anterior (iv) se traduceautomaticamente a la regla de funcion:

p t1...tn = true <== C1, ..., Cn

Por ejemplo, el predicado p_append anterior no es mas que un “azucar sintactico” de lafuncion:

p_append [] Ys Zs = true <== Ys==Zsp_append [X|Xs] Ys [Z|Zs] = true <== X==Z, p_append Xs Ys Zs

Observese que en la primera regla se introduce una nueva variable Zs y la restriccionYs==Zs para conseguir la linealidad. Y en la segunda se hace lo propio con la variable Z yla restriccion X==Z.

Ademas de las diferencias de notacion con respecto a Prolog mencionadas antes, T OYhace inferencia de tipos para los predicados y opcionalmente se puede declarar tipo paraellos, que sera de la forma:

p :: T1 → ... → Tn → bool

es decir, es el tipo de la funcion asociada (debe cumplir todas las condiciones que seimponıan a los tipos de las funciones).

Para el predicado p_append el tipo inferido es:

[A] -> [A] -> [A] -> bool

Page 33: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 33

2.3.11. Operadores infijos y secciones

T OY admite notacion infija para constructoras y funciones binarias (de aridad 2). Haydos formas de uso de esta notacion:

mediante una declaracion expresa de operador (constructora o funcion) infijo. Estasdeclaraciones son de la forma:

infix[l,r] priority operatorName1, ..., operatorNamen

donde priority es un entero positivo que representa la precedencia (cuanto mayores este entero mayor es la precedencia) y operatorName1, ..., operatorNamen sonlos nombres de los operadores (separados por comas) que se estan declarando. Lapalabra reservada infix se utiliza para operadores no asociativos. Los operadoresasociativos por la izquierda (derecha resp.) se declaran con infixl (infixr resp.).Los nombres de constructora deben ir siempre precedidos del sımbolo ‘:’.

Para la eleccion de los nombres hay dos alternativas:

• poner el nombre del operador entre apostrofes (‘). Por ejemplo:

infixr 40 ‘and‘ % and paralelo% (asociativo por la derecha)

• si no se utilizan los apostrofes, puede utilizarse un repertorio limitado de sım-bolos, teniendo en cuenta ademas, que el sistema ya posee algunos operadoresreservados. Por ejemplo:

infix 50 +++ % suma de complejosinfix 60 :/ % constructora del tipo de

% los racionales

En el apendice A hay una descripcion detallada de los sımbolos que puedenutilizarse, ası como de los operadores reservados del sistema.

la otra manera de utilizar constructoras o funciones binarias como operadores infijosconsiste en escribir dicho operador entre apostrofes ‘. Por ejemplo, la division enteradiv (primitiva del sistema) esta utilizada de forma infija en la expresion 5 ‘div‘ 2.

Por otro lado, todo operador infijo puede utilizarse de forma prefija escribiendolo entreparentesis, como en la expresion (+) 3 5 (equivalente a 3 + 5).

En el archivo basic.toy que se distribuye con el sistema se encuentra la declaracion delos operadores infijos predefinidos de T OY, que se trataran en el apartado de funcionesprimitivas (2.3.15).

Otro asunto relacionado con los operadores infijos son las secciones. Una seccion es unanotacion especial que puede utilizarse para aplicaciones parciales de dichos operadores. Porejemplo, la funcion “suma 3” puede escribirse como aplicacion parcial en forma de seccioncomo (3+). Esta funcion puede aplicarse a otro argumento como en la expresion (3+) 5(que se evaluara a 8). La anterior es una seccion izquierda, pero tambien pueden definirsesecciones derechas. La correspondiente en este caso serıa (+3) que, al ser ’+’ una operacionconmutativa es equivalente a la primera. Un caso mas interesante puede ser el de la funcion

Page 34: Un lenguaje lógico funcional con restricciones1

34 CAPITULO 2. EL SISTEMA T OY

’-’. Ahora la seccion (-3) corresponde a la funcion “resta 3”, mientras que la seccion (3-)es la funcion “resta a 3”.

Las secciones resultan utiles combinadas con otras funciones. Por ejemplo, sea la fun-cion map definida como en 2.3.6. Utilizando la seccion (3-) la expresionmap (3-) [1,2,3,4,5,6] se reducirıa a [-2,-1,0,1,2,3].

En 2.3.15 veremos como las secciones pueden traducirse a sintaxis T OY, utilizando laprimitiva flip.

2.3.12. Inclusion de archivos

En muchos casos puede resultar util dividir los programas grandes en piezas mas pe-quenas de codigo, o simplemente, agrupar funciones de uso frecuente en un archivo (estees el caso del archivo misc.toy) para reutilizarlo en otros programas. Para este proposito elsistema cuenta con la directiva include. Por ejemplo, si se esta escribiendo un programa enun archivo < File1 >, se puede usar en cualquier punto del programa la directiva include“< File2 >” para poder hacer uso de las definiciones que contiene < File2 >. Noteseque el argumento de include es una cadena que debe ir entre comillas dobles. El efecto deesta directiva es el mismo que tendrıa incluir literalmente el archivo < File2 > en el lugardonde aparecıa el include.

El archivo misc.toy que se distribuye con el sistema contiene muchas funciones de usofrecuente que pueden utilizarse mediante la directiva:

include ‘‘misc.toy’’

Este archivo es similar al preludio de Gofer o Haskell, pero contiene ademas otras defini-ciones propias del paradigma logico funcional (vease el apendice C).

2.3.13. Regla de indentacion

En los ejemplos vistos hasta ahora se han utilizado algunas reglas implıcitas de indenta-cion. En este apartado se precisan estas reglas que estan tomadas parcialmente de Haskell.Su conocimiento, en Haskell es especialmente interesante para comprender las construccio-nes reservadas where y let que son bastante habituales. Por el momento T OY no incorporatales construcciones, sin embargo estas reglas pueden ayudar a comprender algunos men-sajes de error sintactico.

La unidad basica de codigo en un programa T OY es la sentencia. Una sentenciacomienza con una llave abierta ({) seguida de una o varias secciones y termina con unallave cerrada (}). Una seccion puede ser una inclusion de archivo (include), una definicionde un tipo de datos (data), declaracion de un alias de tipo (type), una declaracion de tipode una funcion o predicado, una declaracion de un operador infijo (infix) o un conjunto dereglas de funcion o predicado. T OY delimita automaticamente las sentencias, pero admitetambien la anotacion explıcita, como en el siguiente programa:

{include ‘‘misc.toy’’

}{

append :: [A] -> [A] -> [A]}{

Page 35: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 35

append [] Ys = Ys;append [X|Xs] Ys = [X|append Xs Ys]

}

Las declaraciones que aparecen al mismo nivel (en la misma sentencia) deben ir separa-das por punto y coma, como ocurre con las dos reglas de append. Las reglas de indentacionpueden resumirse en:

antes de leer el primer caracter de cada seccion se inserta automaticamente una llaveabierta,

el final de una seccion se alcanza cuando la unica llave abierta que se tiene es ladescrita en el apartado anterior y el primer caracter de la lınea actual se encuentraen una columna igual o menor que la columna que ocupa el primer caracter de laseccion,

si el primer caracter de la lınea actual esta en la misma posicion que el primercaracter del nivel mas interno de llaves abiertas y este nivel es estrictamente mayorque 1, entonces se inserta un punto y coma,

se inserta una llave cerrada siempre que se encuentra un token desconocido,

las lıneas vacıas, las que contienen solo espacios en blanco o tabuladores y los comen-tarios, no forman parte de las reglas de indentacion. Todas ellas se tratan comoespacios en blanco.

2.3.14. Objetivos

Computar en T OY es resolver objetivos, es decir, una vez que el usuario ha escrito ycompilado su programa, el sistema esta preparado para buscar respuestas a restriccionesplanteadas para ese programa. Por lo tanto, los objetivos siguen la misma sintaxis que lasrestricciones en la definicion de funciones:

C1, ..., Cn

donde cada Ci es de la forma e13e2 y 3 ∈ {==, /=}. Recordemos que el sistema admitecomo restriccion una expresion booleana e que se interpreta como la restriccion de igualdadestricta e == true (las restricciones aritmeticas se interpretan de este modo como vimosen 2.3.9).

T OY interpreta como objetivo todo lo que encuentra en el prompt que no sea uncomando, es decir, cualquier cadena de texto que no comienza con “/”. Notese los objetivos,a diferencia de los comandos, no terminan con “.”.

Si las restricciones del objetivo son insatisfactibles el sistema responde no y finalizael computo (segundo computo del ejemplo anterior). En el caso de que sean satisfacti-bles responde yes, muestra en forma resuelta las restricciones producidas por el computoy pregunta al usuario si desea obtener mas repuestas: more solutions [y]?. En casoafirmativo, si el usuario responde y (o simplemente pulsa Intro), T OY buscara mas res-puestas haciendo backtracking. En todos los casos, al final de la respuesta, muestra eltiempo invertido en el computo10.

10En este tiempo no se incluyen los tiempos de traduccion del objetivo y tipado del objetivo, ası comotampoco el tiempo de salida de la respuesta. Esta medida es mas apropiada para hacer comparacionesentre distintos programas T OY ya que recoge el tiempo real de resolucion de objetivos.

Page 36: Un lenguaje lógico funcional con restricciones1

36 CAPITULO 2. EL SISTEMA T OY

T OY incorpora un sofisticado mecanismo para minimizar la cantidad de informacionque se presenta en las respuestas, ası como para conseguir una lectura sencilla de lasmismas. El codigo que lleva a cabo este proceso se muestra en el apendice I. A continuacionmostramos algunos ejemplos reales de computo.

Una respuesta afirmativa puede no tener restricciones asociadas, como en el objetivo:

TOY> append [1] [2] == [1,2]

yes

Elapsed time: 0 ms.

more solutions [y]?

no.

Elapsed time: 0 ms.

TOY>

Si hay restricciones asociadas, T OY las presenta en forma resuelta en este orden:

1. Las primeras son las de igualdad que representan ligaduras de variables o sustitu-ciones. En forma resuelta significa que son de la forma X == t, es decir, el ladoizquierdo es una variable y el lado derecho es una forma normal. Por ejemplo:

TOY> append [1,2] [3,4] == L

yesL == [ 1, 2, 3, 4 ]

Elapsed time: 0 ms.

more solutions [y]?

no.

Elapsed time: 0 ms.

TOY> append [1,2] [3] == L, append L L == K

yesL == [ 1, 2, 3 ]K == [ 1, 2, 3, 1, 2, 3 ]

Elapsed time: 0 ms.

Page 37: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 37

more solutions [y]?

no.

Elapsed time: 0 ms.

TOY>

Las formas normales tambien pueden contener aplicaciones parciales puesto que sonexpresiones irreducibles, como en el siguiente computo:

TOY> append [1] == F

yesF == (append [ 1 ])

Elapsed time: 0 ms.

more solutions [y]?

no.

Elapsed time: 0 ms.

TOY>

2. A continuacion, en caso de existir, se muestran entre llaves las restricciones de des-igualdad. Las desigualdades resueltas tienen la forma X /= t, donde el lado izquierdoes una variable y el lado derecho es una forma normal. Un computo con desigualdadespuede ser:

TOY> append [1] X /= [1,2]

yes{ X /= [ 2 ] }

Elapsed time: 0 ms.

more solutions [y]?

no.

Elapsed time: 0 ms.

TOY>

Page 38: Un lenguaje lógico funcional con restricciones1

38 CAPITULO 2. EL SISTEMA T OY

3. Por ultimo, y solo en caso de que el sistema este utilizando restricciones sobre reales(modo de uso /cflpr.) tambien mostrara (entre llaves) las restricciones aritmeticasresultantes del computo, en la forma resuelta que proporciona el resolutor ([Gro97]).Por ejemplo:

TOY> X + Y + Z == 3, X - Y + Z == 1

yesY == 1

{ X==2.0-Z }

Elapsed time: 10 ms.

more solutions [y]?

no.

Elapsed time: 0 ms.

TOY> X^2 == 4, X>0

yes{ X>0.0 }{ 4.0-X^2.0==0.0 }

Elapsed time: 0 ms.

more solutions [y]?

no.

Elapsed time: 0 ms.

TOY>

En el ultimo objetivo se introduce la restriccion no lineal X^2 == 4. En este casoel resolutor simplemente la suspende. Si al final del computo hay restricciones nolineales (suspendidas), estas se presentaran en la forma resuelta que proporciona elresolutor como en este caso (no se presenta la respuesta X==2).

Otro ejemplo, puede ser el siguiente:

TOY> X^2 + sin Y == Z

yes{ Z==_D+_E }{ _E-sin(Y)==0.0 }

Page 39: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 39

{ -(X^2.0)+_D==0.0 }

Elapsed time: 10 ms.

more solutions [y]?

no.

Elapsed time: 0 ms.

TOY>

2.3.15. Funciones primitivas

T OY cuenta con algunas funciones predefinidas o primitivas que pueden utilizarse encualquier programa sin necesidad de definirlas de nuevo. Dichas funciones estan progra-madas a “bajo nivel” y su codigo no es accesible al usuario, pero las declaraciones de tipode todas ellas pueden verse en el archivo basic.toy que se incluye en la distribucion (veaseel apendice B).

Entre dichas funciones estan las operaciones aritmeticas habituales:

(+),(-),(*),(/) :: real -> real -> real

cuyas precedencias y asociatividades vienen dadas por las declaraciones de operadoresinfijos (a mayor prioridad mayor precedencia):

infix 80 *,/infixl 70 +,-

Aunque estos operadores estan declarados para numeros reales pueden funcionar tambiencon enteros. En cierto sentido estos operadores pueden considerarse funciones sobrecar-gadas: tienen tambien de modo implıcito la declaracion int → int → int y si para unaocurrencia de uno de estos operadores, el inferidor tiene suficiente informacion para deter-minar que el tipo de alguno de los argumentos o el tipo del resultado es entero, entoncesel tipo de dicha ocurrencia sera int → int → int. En caso de que el inferidor no tengainformacion suficiente para determinar si se trata de la suma de enteros o de reales, in-terpretara que se trata de la suma de reales. Informalmente, puede decirse que la sumade enteros esta “incluida” en la suma de reales y si T OY no puede determinar de cual setrata interpreta la mas general, es decir, la de reales. Esto no puede considerarse sobrecargaen sentido general, ya que el codigo de uno u otro modo de uso es el mismo; sin embargo,con esta distincion en el tipo de las operaciones aritmeticas el sistema nunca intentara,por ejemplo, hacer la division entera con numeros reales, ya que el inferidor detectara laanomalıa y producira un mensaje de error en tiempo de compilacion.

A continuacion se presentan el resto de primitivas del sistema, algunas de las cua-les tambien estan sobrecargadas en el sentido que se acaba de exponer. Por ejemplo, lasfunciones max/2 y min/2, que calculan el maximo y el mınimo de dos numeros respectiva-mente, estan sobrecargadas en el mismo sentido. Para la exponenciacion T OY incorporatres funciones:

Page 40: Un lenguaje lógico funcional con restricciones1

40 CAPITULO 2. EL SISTEMA T OY

infix 90 ^,**

(^) :: real -> int -> real(**) :: real -> real -> realexp :: real -> real

La primera de ellas toma un exponente entero (segundo argumento) y como base puedetomar tanto un entero, como un real (devuelve un real). La segunda puede tomar un enteroo un real como base, pero el exponente y el resultado seran de tipo real. Y la ultima, esla exponencial natural (toma como base e) y devuelve un real.

Otras funciones: ln/1 es logaritmo natural y log/2 opera con cualquier base. La funcionuminus/1 es la de cambio de signo y abs/1 es el valor absoluto (ambas para enteros yreales); sqrt/1 es la raız cuadrada (para enteros y reales) que siempre devuelve un real.T OY tambien tiene predefinidas las funciones trigonometricas usuales (vease el apendiceB) que operan siempre sobre reales. Para enteros el sistema cuenta con las operaciones dedivision entera div/2 y resto de la division entera mod/2 y tambien con el maximo comundivisor gcd/2.

Para la conversion de enteros a reales existe la funcion toReal/1 y los reales se puedenredondear o truncar para convertirlos a enteros con las funciones round/1 y trunc/1.Tambien existen las funciones floor/1, que calcula el mayor entero menor o igual que elargumento, y ceiling/1 que calcula el menor entero mayor o igual que el argumento.

Los operadores relacionales tienen la siguiente declaracion:

infix 50 < ,<=,>,>=

(<),(<=),(>),(>=) :: real -> real -> bool

y estan tambien sobrecargados en el sentido anterior, es decir, tienen implıcita la declara-cion int → int → bool; si alguno de los argumentos es de tipo entero este es el tipo que elinferidor asocia al operador. Estos operadores sirven tambien para expresar restriccionesaritmeticas tal y como se explico en 2.3.9.

Otras primitivas que introduce T OY son las funciones de igualdad y desigualdad cuyadeclaracion es:

infix 20 ==, /=(==),(/=) :: A -> A -> bool

La sobrecarga aquı se hace mas patente. Una restriccion de igualdad no es lo mismoque una llamada a la funcion igualdad. Por ejemplo, en (1 + 2 == 4) == B, el primersımbolo == es una llamada a la funcion de igualdad, mientras el segundo juega el papelde restriccion de igualdad. La funcion igualdad, al evaluarse con los argumentos 1 + 2 y 4devuelve false, y este valor es el que toma B, por lo que el sistema devolvera la respuestaB == false. La distincion entre la funcion y la restriccion puede entenderse del siguientemodo: una llamada a la funcion igualdad es algo que debe evaluarse, mientras que unarestriccion es algo que debe satisfacerse. Lo mismo ocurre con la funcion desigualdad y lasrestricciones de desigualdad. En este sentido, los sımbolos == y /= estan sobrecargados,ya que se utilizan tanto para funciones como para restricciones con significado distinto.

Otro ejemplo del uso de la funcion igualdad puede ser la funcion member que, dado unelemento y una lista (de elementos del mismo tipo), devuelve true si el elemento esta enla lista y false en caso contrario. Repasemos la definicion que vimos en 2.3.8:

Page 41: Un lenguaje lógico funcional con restricciones1

2.3. EL LENGUAJE T OY 41

member X [] = falsemember X [Y|Ys] = if X==Y then true else member X Ys

En la segunda regla, cuando la lista no es vacıa, si el elemento es igual a la cabeza dela lista, entonces la funcion se evalua a true; si no es igual a la cabeza, se estudia lapertenencia de dicho elemento al resto de la lista. La comprobacion de si el elemento esigual a la cabeza de la lista se realiza mediante la funcion igualdad. Si esta funcion seevalua a true, entonces se toma la primera opcion del if then else que es true y si se evaluaa false se selecciona la segunda opcion.

Las funciones de igualdad y desigualdad podrıan definirse en sintaxis T OY utilizandolas restricciones del siguiente modo:

X == Y = true <== X == YX == Y = false <== X /= Y

X /= Y = false <== X == YX /= Y = true <== X /=Y

Sin embargo, T OY no utiliza estas definiciones sino que incorpora codigo especıfico paraellas a bajo nivel (3.13,3.14). La razon es que estas funciones son de uso relativamentefrecuente y admiten algunas optimizaciones que no podrıan llevarse a cabo sobre las dosdefiniciones anteriores.

T OY tambien incorpora como primitivas las funciones:

if_then :: bool -> A -> Aif_then_else :: bool -> A -> A -> A

que son las funciones correspondientes a las construcciones if <Condicion> then y if<Condicion> then <Expresion1 > else <Expresion2 >, donde <Condicion> es una ex-presion booleana, y <Expresion1 > y <Expresion2 > deben ser del mismo tipo. En rea-lidad estas construcciones son un “azucar sintactico” que el analizador sintactico traduceautomaticamente a las funciones correspondientes. Estas funciones, como todas las pri-mitivas, estan definidas a bajo nivel pero podrıan definirse en la sintaxis de T OY delsiguiente modo:

if_then true X = X

if_then_else true X Y = Xif_then_else false X Y = Y

Por ultimo, T OY incorpora como primitiva la funcion de orden superior flip que sirvepara intercambiar el orden de los argumentos en la aplicacion de una funcion. El tipo es:

flip :: (A -> B -> C) -> B -> A -> C

y la definicion en sintaxis T OY podrıa ser (aunque se codifica a bajo nivel):

flip F X Y = F Y X

Por ejemplo, la expresion flip (−) 4 2 se evaluarıa a (−) 2 4, o lo que es lo mismo 2−4.Esta funcion se incorpora como primitiva para la traduccion de secciones (2.3.11) como(−3) 5. En esta expresion (−3) es una funcion que le resta 3 al argumento que se le pasa;si se le pasa 5 debe evaluar 5− 3. El problema es que el parametro 5 aparece como primerargumento y para aplicar (−3) hay que invertir el orden de los argumentos, y esto es loque hace flip. La expresion (−3) 5 se traduce a flip (−) 3 5 que tiene el comportamientodeseado.

Page 42: Un lenguaje lógico funcional con restricciones1

42 CAPITULO 2. EL SISTEMA T OY

2.4. Ejemplo 1. Regiones en el plano

En esta seccion desarrollamos paso a paso un programa que trata sobre regiones en elplano y que muestra algunas de las posibilidades que ofrece el uso conjunto de las funcionesy las restricciones. En [HLS+97] se presenta una version de este mismo ejemplo, ası comouna discusion que compara la programacion logica pura con restricciones (CLP (R)) frentea la programacion logico funcional con restricciones (CFLP (R)). Este ejemplo tambiense incluye en la distribucion en el archivo region.toy. Para ejecutarlo T OY debe teneractivadas las restricciones sobre reales (comando /cflpr).

Las regiones seran conjuntos de puntos en el plano que vienen representados por sufuncion caracterıstica. Esta aproximacion es tıpica en programacion funcional. La novedadaquı es que las restricciones proporcionan un modo mucho mas flexible para utilizar lasfunciones, e incluso para definir otras nuevas. Por otro lado, las funciones no deterministasaportan una gran riqueza expresiva que permite definiciones muy directas para algunasoperaciones de naturaleza indeterminista.

Comenzamos definiendo dos alias de tipo: los puntos son parejas de reales y las regionesestan definidas por su funcion caracterıstica (tipo funcional):

type point = (real,real)type region = point -> bool

Una primera operacion habitual en este contexto es la de pertenencia que notaremoscon el operador infijo <<-, asociativo por la derecha:

infixr 50 <<-(<<-):: point -> region -> boolP <<- R = R P

La regla de esta funcion puede leerse como: el valor de la afirmacion “el punto P pertenecea la region R” es el resultado de aplicar R (funcion caracterıstica) a P .

Utilizaremos los operadores logicos habituales de conjuncion (/\), disyuncion (\/) yla negacion logica (not):

infixr 40 /\infixr 30 \/

false /\ X = falsetrue /\ X = X

true \/ X = truefalse \/ X = X

not true = falsenot false = true

En este caso utilizamos la definicion secuencial para la conjuncion y la disyuncion. Laversion paralela de la conjuncion podrıa definirse como: false ∧X = false, X ∧ false =false y true∧true = true. Aunque ambas versiones de la conjuncion son logicamente equi-valentes, operacionalmente tienen un comportamiento distinto (esto se comprendera mejorcuando analicemos la estrategia de evaluacion de T OY en 3.10).

Ya estamos en disposicion de definir las primeras regiones. La mas sencilla es el punto(un punto es una region):

Page 43: Un lenguaje lógico funcional con restricciones1

2.4. EJEMPLO 1. REGIONES EN EL PLANO 43

point :: point -> regionpoint P Q = P==Q

La regla puede leerse como: el punto P pertenece a la region que solo contiene elpunto Q si P y Q son el mismo. Notese que estamos empleando deliberadamente el mismonombre point tanto para un alias de tipo, como para una funcion: nos interesa pensaren los puntos como pares de reales y tambien como regiones en el plano. Esto no suponeningun problema para el compilador.

Otras dos regiones triviales son la region vacıa y el plano (ningun punto pertenece ala region vacıa y todos los puntos pertenecen al plano):

emptyReg, thePlane :: regionemptyReg P = falsethePlane P = true

Un rectangulo queda definido (por ejemplo) por la esquina inferior izquierda y lasuperior derecha, y un cırculo por su centro y su radio (ecuacion habitual del cırculo):

rectangle :: point -> point -> regionrectangle (A,B) (C,D) (X,Y) =

(X >= A) /\ (X <= C) /\ (Y >= B ) /\ (Y <= D)

circle :: point -> real -> regioncircle (A,B) R (X,Y) = (X-A)*(X-A)+(Y-B)*(Y-B) <= R*R

Sobre las regiones definimos algunas operaciones tıpicas como la interseccion y la union,que seran funciones de orden superior:

intersct, union :: region -> region -> regionintersct R R’ P = P <<- R /\ P <<- R’union R R’ P = P <<- R \/ P <<- R’

La lectura de las reglas es simple. Por ejemplo, la de la interseccion afirma que un puntoP pertenece a la interseccion de las regiones R y R′ si P pertenece a R y tambien a R′.

T OY ofrece posibilidades interesantes en cuanto a los patrones en la definicion defunciones: admite como patron cualquier forma normal y en particular admitira patrones deorden superior. En la practica, esto significa que podemos distinguir casos en la definicionde una funcion de orden superior, de acuerdo con las distintas “formas intensionales” quelos argumentos pueden adoptar. Por ejemplo, la funcion intersect puede definirse demodo que contemple el hecho de que la interseccion de cualquier region con la region vacıaes la region vacıa o que la interseccion de dos rectangulos es otro rectangulo:

intersect’ :: region -> region -> regionintersect’ emptyReg R = emptyRegintersect’ R emptyReg = emptyRegintersect’ (rectangle (A,B) (C,D)) (rectangle (A’,B’) (C’,D’)) =

if (A’’ <= C’’) /\ (B’’ <= D’’)then rectangle (A’’,B’’) (C’’,D’’)else emptyReg<== A’’== max A A’ , B’’== max B B’,

C’’== min C C’ , D’’== min D D’

Page 44: Un lenguaje lógico funcional con restricciones1

44 CAPITULO 2. EL SISTEMA T OY

intersect’ R R’ = intersect R R’<== R /= emptyReg, R’ /= emptyReg,

(R,R’) /= (anyRectangle,anyRectangle)

anyRectangle = rectangle undefined undefined

undefined :: Aundefined = if false then undefined

Las dos primeras reglas cubren el caso de que una de las regiones sea vacıa y la tercerael caso en el que ambas regiones son rectangulos (las funciones max y min calculan el maxi-mo y el mınimo entre dos numeros respectivamente). La ultima regla se aplica si no esaplicable ninguna de las anteriores y utiliza la anterior definicion de intersect. Para queesta regla no sea aplicable en los casos que tratan las anteriores, tiene que comprobar queninguno de los argumentos es la region vacıa y que no son dos rectangulos. Esta ultimarestriccion (R,R’) /= (anyRectangle,anyRectangle) requiere algunos comentarios. Se tratade una desigualdad entre dos parejas de elementos y para resolverla habra que resolveralguna de las (dos) desigualdades entre las componentes. En este caso ambas desigualda-des tienen la misma forma R /= anyRectangle, cuya lectura es “R no es un rectangulo”.Tal y como hemos definido los rectangulos, todos son de la forma rectangle . Entonces,para comprobar que R no es un rectangulo, debemos comprobar que no es de esta forma(que no se ajusta a este patron). En la definicion de anyRectangle se utiliza la funcion(constante) undefined, que es una funcion que siempre falla. Con esta definicion, la con-dicion R /= anyRectangle es equivalente a R /= rectangle\ undefined\ undefined,es decir, R no toma la forma rectangle (el valor de los argumentos ’ ’ no nos importa),que es precisamente lo que se busca11.

La definicion de undefined utiliza la funcion igualdad y puede leerse como: si es ciertoque false es igual a true entonces devolver el resultado de undefined (realmente lo quedevuelva en este caso importa poco). Obviamente, false no es igual a true y la funcionigualdad devuelve false; como el if no tiene alternativa se produce un fallo automatico,que es justo lo que se pretende.

El exterior o complementario de una region es otra region que puede definirse utilizandola negacion:

outside :: region -> region

outside R P = not (P <<- R)

Las funcion interserc’ utiliza patrones de orden superior, que no se permiten enlenguajes funcionales puros como Haskell y, por lo tanto, no admisible en este lenguaje. Elresto de definiciones vistas hasta el momento sı son admisibles en este tipo de lenguajes y,en particular, en Haskell. Sin embargo, debido a la reversibilidad de las funciones y a lasrestricciones T OY es capaz de hacer computos que carecen de sentido en programacionfuncional pura utilizando las funciones expuestas. Por ejemplo, se le puede pedir al sistemaque calcule los puntos P que pertenecen a la interseccion de dos rectangulos:

TOY> P <<- intersect (rectangle (0,0) (3,2)) (rectangle (1,1) (4,3))

11En general, una condicion de la forma X /= c undefined (c constructora de aridad 1) expresa deforma implıcita una cuantificacion universal. Esta condicion es equivalente a decir ∀Y (X /= c Y ), que asu vez, equivale a decir “X no toma la forma c ”.

Page 45: Un lenguaje lógico funcional con restricciones1

2.4. EJEMPLO 1. REGIONES EN EL PLANO 45

yesP == (_A, _B)

{ _B>=1.0 }{ _B=<2.0 }{ _A>=1.0 }{ _A=<3.0 }

La respuesta es que P debe tener la forma (A,B), donde A y B cumplen las restriccionesque cabe esperar.

Las operaciones de interseccion y union pueden generalizarse para que operen sobreconjuntos de regiones representados como listas. Para ello es util el combinador foldrclasico en programacion funcional. Este operador toma una funcion, una lista y un elemento“neutro” y utiliza el tercer argumento como parametro acumulador. En T OY se puededefinir como:

foldr :: (A -> B -> B) -> B -> [A] -> Bfoldr F Z [] = Zfoldr F Z [X|Xs] = F X (foldr F Z Xs)

Por ejemplo la llamada foldr (*) 1 [2,3,4] hace la multiplicacion (2 ∗ (3 ∗ (4 ∗ 1)) ydevuelve 24 (1 es el elemento neutro para ∗).

Con esta funcion, la generalizacion de la interseccion y union de regiones se puededefinir como:

intersectAll, unionAll :: [region] -> regionintersectAll = foldr intersect thePlaneunionAll = foldr union emptyReg

Observese que en la interseccion se toma el plano como elemento neutro, mientras que enla union se toma la region vacıa.

Para definir nuevas operaciones sobre regiones introducimos el nuevo alias de tipo delos vectores, representados por un par de reales que se interpreta como un numero complejo(coordenadas cartesianas). Para los vectores definimos tambien las operaciones habitualesde suma, resta, multiplicacion, division y producto por un escalar, que notaremos con lossımbolos aritmeticos seguidos de ‘.’ (#. para el producto por un escalar):

type vector = (real,real)

(+.),(-.),(*.),(/.) :: vector -> vector -> vector(#.) :: real -> vector -> vector

(X,Y) +. (U,V) = (X+U,Y+V)(X,Y) -. (U,V) = (X-U,Y-V)(X,Y) *. (U,V) = (X*U-Y*V,X*V+Y*U)(X,Y) /. (U,V) = ((X*U+Y*V)/A,(Y*U-X*V)/A) <== A == U*U+V*V, A > 0K #. (X,Y) = (K*X,K*Y)

Con este tipo y sus operaciones podemos definir, por ejemplo la envoltura convexa(conv hull) de un conjunto de puntos. Identificamos los puntos con sus vectores de posiciony utilizamos la equivalencia:

Page 46: Un lenguaje lógico funcional con restricciones1

46 CAPITULO 2. EL SISTEMA T OY

P ∈ convhullP1, ..., Pn ⇔ P =∑

i=1..n λiPi, con λi ≥ 0, ∀i = 1..n y∑

i=1..n λi = 1

De acuerdo con esta definicion el predicado conv_hull puede programarse como:

conv_hull :: [point] -> regionconv_hull Ls P :- P == lin_comb Ls 1

lin_comb [] 0 = (0,0)lin_comb [X|Xs] Sum = (K #. X) +. lin_comb Xs Rest

<== Sum == Rest + K, K >=0, Rest >=0

La lectura de la regla de conv_hull es la siguiente: un punto P esta en la envoltura convexade una lista de puntos Ls si dicho punto puede obtenerse como combinacion lineal de losvectores de posicion de los puntos de Ls; la funcion lin_comb se encargara de que loscoeficientes de dicha combinacion lineal sean todos positivos o nulos y que sumen 1. Lafuncion lin_comb devuelve una combinacion lineal de los puntos que se le pasan comoprimer argumento, cuyos coeficientes suman la cantidad que se le pasa como segundoargumento (por eso en conv_hull el segundo argumento es 1). Este segundo argumento esuna especie de parametro acumulador que se va decrementando hasta llegar a 0. Ası unacombinacion lineal de una lista de puntos [X|Xs] (segunda regla) sera el primero de ellos Xmultiplicado por una constante K mas una combinacion lineal del resto; en las restriccionesnos ocupamos de que K ≥ 0 y de que todos los coeficientes al final sumen 1.

Una observacion importante acerca de la segunda regla de lin_comb es que K y Rest(que aparecen en el cuerpo y las restricciones) no estan determinadas por los argumentos dela funcion [X|Xs] y Sum, lo que implica que lin_comb es una funcion indeterminista.

Ahora podemos hacer el siguiente computo:

TOY> P <<- conv_hull [(1,2), (2,3), (4,3), (4,1), (3,2)]

yesP == (_A, _B)

{ _A-_B>= -0.9999999999999996 }{ _A+3.0*_B>=6.999999999999999 }{ _A=<4.0 }{ _B=<3.0 }

El resultado puede comprobarse graficamente en la figura 2.1.

(1,2)

(2,3) (4,3)

(4,1)

(3,2)x

Figura 2.1: Envoltura convexa de un conjunto de puntos

Page 47: Un lenguaje lógico funcional con restricciones1

2.4. EJEMPLO 1. REGIONES EN EL PLANO 47

Otras operaciones sobre regiones son las traslaciones, que desplazan una region (se-gundo argumento) de acuerdo con un vector de traslacion (primer argumento) y producenotra region, y las homotecias, que toman un punto como el centro de homotecia (primerargumento), un real como razon de homotecia (segundo argumento) y una region (tercerargumento) y devuelven otra region (vease la figura 2.2):

translate:: vector -> region -> regionhomothety:: point -> real -> region -> region

translate V R P = (P -. V) <<- Rhomothety C F R P = (C +. ((1/F) #. (P -. C))) <<- R

RC

P

F = 2

Figura 2.2: Homotecia

Como T OY es un lenguaje perezoso, es posible definir estructuras infinitas, como la lis-ta infinita de regiones obtenidas por sucesivas traslaciones de una region inicial (rayItems),e incluso es planteable la posibilidad de hacer la union de todas esas regiones, como haceray:

ray :: vector -> region -> regionray V R = unionAll (rayItems V R)

rayItems :: vector -> region -> [region]rayItems V R = [R|rayItems V (translate V R)]

Se puede lanzar el objetivo (5,1) <<- ray (1,0) (circle (0,0) 2), que produceuna respuesta afirmativa. En el computo se va desplazando el cırculo una y otra vez, hastaque alguno de los cırculos resultantes captura al punto (5, 1); en ese momento se obtieneun exito y para el computo. La estructura infinita se ha evaluado solo parcialmente. Noocurre lo mismo si lanzamos el objetivo (0,3) <<- ray (1,0) (circle (0,0) 2). Eneste caso el computo nunca termina ya que, este punto no pertenece a esa region, pero paraaveriguarlo T OY “debe calcular infinitas regiones”. De hecho la funcion ray solo terminacuando el punto sı pertenece a la region, es decir, computa una funcion semicaracterıstica.Sin embargo, esto no es realmente un problema del sistema ya que las uniones infinitasde conjuntos recursivos es, en general, no recursiva, sino recursivamente enumerable. O loque es lo mismo, determinar si un punto pertenece a una union infinita de regiones no esdecidible en general (es parcialmente decidible).

La traslacion rıgida o solida es una operacion que toma un vector de traslacion y unaregion y devuelve la region formada por: la region original, la region trasladada y todoslos puntos que se encuentran en el movimiento. Esto es lo que hace la funcion move:

move :: vector -> region -> regionmove (U,V) R P :- 0 <= K, K <= 1, translate (K*U,K*V) R P

Page 48: Un lenguaje lógico funcional con restricciones1

48 CAPITULO 2. EL SISTEMA T OY

Notese que la variable K de la regla anterior es local a las restricciones: no aparece enla cabeza del predicado, es decir, es una variable cuantificada existencialmente de formaimplıcita.

El siguiente es un ejemplo de objetivo que combina la potencia de las restricciones conla reversibilidad de las funciones:

TOY> P <<- move (3,1) (rectangle (0,0) (2,3))

yesP == (_A, _B)

{ _A-3.0*_B=<2.0 }{ _A-3.0*_B>= -9.0 }{ _A>=0.0 }{ _A=<5.0 }{ _B>=0.0 }{ _B=<4.0 }

En el resultado aparecen las ecuaciones de seis semiplanos que determinan la regionresultante de hacer el movimiento (graficamente en la figura 2.3). Ademas observese quela variable existencial K de la definicion de move no aparece en la respuesta.

(3,1)

(2,3)

(0,0)

x P

Figura 2.3: Movimiento solido

Planteemos ahora un problema que utiliza algunas de las funciones anteriores:

PROBLEMA DEL LABERINTO: Dada una region R, una secuencia de movi-mientos Mvs y un conjunto de puntos Pts, reordenar Mvs en una nueva secuencia Mvs′

de modo que la aplicacion secuencial de los movimientos de Mvs′ a la region R no en-cuentre ningun punto de Pts.

Para ilustrar el objetivo que pretendemos vease la figura 2.4. Dada la region R, unahomotecia (1) y una traslacion (2), y los puntos P1 a P5, podemos aplicar a R primerola traslacion (2) y luego a la region resultante la homotecia (1), sin tocar ninguno de lospuntos P1 a P5.

La solucion se consigue por generate & test12:

12Este es un metodo clasico de busqueda de soluciones que consiste en generar posibles candidatos asolucion (generalmente por backtraking) y comprobar despues si, efectivamente, son soluciones al problema.

Page 49: Un lenguaje lógico funcional con restricciones1

2.4. EJEMPLO 1. REGIONES EN EL PLANO 49

R

P1

P2

P3

P4

(2)

(1)

Mvs = { (1), (2) }

P5

Figura 2.4: Laberinto

solution::region -> [point] -> [region->region] -> [region->region]solution R Pts Mvs = Mvs’ <== check R Pts Mvs’ % test

where Mvs’ == permut Mvs % generate

Donde permut genera una permutacion de la lista dada y check comprueba que, efec-tivamente, la secuencia de movimientos Mvs’ aplicada a R no toca los puntos de Pts.Despues veremos la definicion de estas funciones.

Por el momento, T OY no admite construcciones where, por lo que lo reemplazaremospor una construccion equivalente, pero admisible para T OY (esta transformacion siemprepuede hacerse, aunque en la version actual T OY no la hace de modo automatico):

solution R Pts Mvs = solAux R Pts (permut Mvs)

solAux R Pts Mvs = Mvs <== check R Pts Mvs

La funcion permut produce, de forma indeterminista, las distintas posibles permuta-ciones de una lista dada. Para ello inserta (insert) el primer elemento de dicha lista enuna posicion cualquiera de una permutacion del resto de la lista:

permut [] = []permut [X|Xs] = insert X (permut Xs)

insert X [] = [X]insert X [Y|Ys] = [X,Y|Ys] // [Y|insert X Ys]

X // Y = XX // Y = Y

El indeterminismo de permut, en ultima instancia, se concentra en la funcion // que esla eleccion indeterminista entre dos valores (es otra notacion para la funcion choice quevimos en 2.3.7).

Para la funcion check utilizaremos algunas funciones auxiliares:

Page 50: Un lenguaje lógico funcional con restricciones1

50 CAPITULO 2. EL SISTEMA T OY

(.) :: (B -> C) -> (A -> B) -> (A -> C)(F . G) X = F (G X)

all:: (A -> bool) -> [A] -> boolall P = andL . (map P)

andL:: [bool] -> boolandL = foldr (/\) true

map:: (A -> B) -> [A] -> [B]map F [] = []map F [X|Xs] = [F X | map F Xs]

La primera es la composicion habitual de funciones y all comprueba si todos loselementos de una lista verifican una condicion determinada. andL comprueba que todoslos elementos de una lista de booleanos son true y map es la habitual. Con estas funcionestenemos:

check R Pts Mvs :- avoids Pts (doMoves R Mvs)

avoids Pts R = all (not.(<<- R)) Pts

doMoves R [] = RdoMoves R [Mv|Mvs] = union R (doMoves (Mv R) Mvs)

La funcion doMoves toma una region y una secuencia de movimientos, y devuelve launion de las regiones que resultan de ir aplicando los movimientos a la region dada, deforma secuencial. Y avoids toma una lista de puntos y una region, y comprueba que laregion no contiene a ninguno de tales puntos. Combinando estas dos funciones, checkcomprueba que las regiones producidas por una secuencia de movimientos dada no tocana un conjunto de puntos, tambien dados.

Un objetivo concreto para este problema:

TOY> X == solution (rectangle (0,0) (2,2)) [(1,3),(3,3),(2,5)][(homothety (0,0) 2), (homothety C 0.5),(translate (2,2)), (translate (1,1))],

C <<- (rectangle (-1,-1) (0,0))

Notese que la segunda homotecia de la lista de posibles movimientos tiene como centrouna variable C, y que la ultima restriccion impone que dicho centro este en un cuadradoconcreto del plano. Una de las tres soluciones que encuentra el sistema es la siguiente:

yesX == [ (homothety (0, _A) 0.5), (translate (2, 2)),

(homothety (0, 0) 2), (translate (1, 1)) ]C == (0, _A)

{ _A>= -1.0 }{ _A< -0.0 }

La secuencia que propone el sistema es una solucion para el objetivo, supuesto que elcentro de la homotecia esta en el segmento (0,−1], (0, 0). En la figura 2.5 se ha represen-tado graficamente esta solucion: sobre el cuadrado inicial se aplica la primera homotecia,

Page 51: Un lenguaje lógico funcional con restricciones1

2.4. EJEMPLO 1. REGIONES EN EL PLANO 51

obteniendo el cuadrado (a); por traslacion de este se obtiene (b); por la segunda homotecia(c) y por la ultima traslacion (d). Ninguno de los cuadrados intermedios contiene ningunode los puntos que se querıan evitar.

(2,5)

(1,3)(3,3)

(0,A)

inicial

(a)

(b)

(c)

(d)

Figura 2.5: Solucion al laberinto

Para concluir este ejemplo, veamos un objetivo con variables logicas de orden superior,es decir, variables de tipo funcional, como (0,0) <<- R. Aquı se le esta pidiendo al sistemaque calcule las regiones que contienen al punto (0, 0). Algunas de las respuestas obtenidasson:

R == (’(==)’ (0, 0))

R == (’(/=)’ _A){ _A /= (0, 0) }

R == thePlane

R == (point (0, 0))

R == (rectangle (_A, _B) (_C, _D)){ _C>=0.0 }{ _A=<0.0 }{ _B=<0.0 }{ _D>=0.0 }

R == (circle (_A, _B) _C){ _B== -(_H) }{ _G==_H }{ _D==_E }

Page 52: Un lenguaje lógico funcional con restricciones1

52 CAPITULO 2. EL SISTEMA T OY

{ _A== -(_E) }{ -(_C^2.0)+_J==0.0 }{ _F-_H*_G==0.0 }{ _I-_D*_E==0.0 }

R == (outside (’(==)’ _A)){ _A /= (0, 0) }

R == (outside emptyReg)

R == (outside (outside (’(==)’ (0, 0))))

En este caso la funcion outside proporciona un amplio espectro de posibilidades (ha-ciendo dobles negaciones como en la ultima respuesta) y T OY continuarıa produciendomuchas mas soluciones.

Sin embargo, las variables de orden superior pueden provocar fenomenos inesperadosen el sistema en determinadas circunstancias. Por ejemplo, si lanzamos el objetivo:

TOY> map F [true] == [false]

el sistema produce entre otras, las siguientes respuestas:

F == (’(==)’ false)

F == (’(/=)’ true)

F == emptyReg

F == (point false)

Las dos primeras son correctas, pero la tercera liga la variable F con emptyReg, quetiene tipo point → bool. Por la forma del objetivo es facil apreciar que F debe ser detipo bool → bool. En la ultima respuesta el problema es mas acusado, ya que la expresionpoint false carece de sentido (la funcion point tiene tipo point → region). Estas dosultimas respuestas estan mal tipadas.

El problema podrıa solucionarse haciendo que T OY manejase tipos en tiempo de eje-cucion y comprobando que cuando una variable se liga, lo hace con una expresion delmismo tipo. Sin embargo, para hacer esto es necesario arrastrar informacion de tiposen los computos, lo que (previsiblemente) afectarıa seriamente al sistema en cuanto a laeficiencia.

Otra posible solucion es dejar que el sistema opere como hasta ahora, pero comproban-do tipos a la hora de presentar las respuestas. En el ejemplo anterior, se dejarıa anotado eltipo de F; una vez calculada la respuesta F == emptyReg, se utilizarıa el inferidor de tipospasandole ademas el tipo anotado para F. El inferidor producirıa un fallo, la respuesta nose mostrarıa y se solicitarıa una nueva respuesta. Este es un mecanismo que simula unfallo en ejecucion (es simulado porque no se produce en ejecucion realmente).

Esta ultima solucion no es completamente satisfactoria porque basicamente se trata dedesestimar determinadas respuestas que han sido calculadas por el sistema. En determina-dos pasos de computo se pueden producir inconsistencias de tipo que no serıan detectadas

Page 53: Un lenguaje lógico funcional con restricciones1

2.5. EJEMPLO 2. PUZLE ARITMETICO 53

hasta el final. Por este motivo hemos optado por dejar que se presenten respuestas mal ti-padas que, esporadicamente, nos serviran para recordar que aquı hay un problema abierto.Remarquemos que esta anomalıa solo se produce en determinados computos queutilizan variables logicas de orden superior.

2.5. Ejemplo 2. Puzle aritmetico

En el archivo martians.toy que se incluye entre los ejemplos del sistema, se encuentrael programa correspondiente a un problema clasico en programacion logica. El enunciadode este problema es el siguiente:

Hay dos numeros, M y N , tales que 1 < M < 100, 1 < N < 100 y dos hombres muylistos, Mr. S y Mr. P. A Mr. S se le dice la suma de los dos numeros y a Mr. P se ledice el producto. Ademas Mr. S sabe que Mr. P conoce el producto y Mr. P sabe que Mr.S conoce la suma. Ambos hombres mantienen el siguiente dialogo:

Mr. P: No se cuales son los numeros.Mr. S: Ya sabıa que tu no lo sabıas; yo tampoco los se.Mr. P: Ahora se cuales son los numeros!.Mr. S: Ahora yo tambien lo se!.

¿Cuales son los numeros?

A primera vista, el dialogo anterior resulta sorprendente, sin embargo, la solucion aldilema es solo cuestion de logica y de hacer algunas comprobaciones.

Estudiemos detenidamente la informacion del enunciado: buscamos dos numeros M yN , siendo 1 < M ≤ N < 100 (con M ≤ N descartamos posibles soluciones simetricas como(23, 56) y (56, 23)); ası pues, los candidatos seran parejas de numeros entre 2 y 99, cuyoprimer elemento es menor o igual que el segundo. Sabiendo que Mr. P conoce el productoP = M ∗N y Mr. S conoce la suma S = M + N , de cada una de las intervenciones de laconversacion pueden hacerse las siguientes deducciones:

si conociendo el producto P , Mr. P desconoce los numeros es porque “debe existirmas de una pareja de candidatos cuyo producto sea P”;

Mr. S sabe que Mr. P conoce el producto P y ademas sabıa (antes de que Mr. Plo dijese) que Mr. P no conocıa los numeros. Esto debe ser porque para todas lasparejas de candidatos cuya suma es S, el producto de tales parejas puede obtenersecomo producto de los elementos de mas de una pareja de candidatos. Ademas Mr.S tambien desconoce los numeros, lo que indica que hay mas de una posible des-composicion en sumandos. Esta ultima informacion en realidad es redundante, yaque para que solo hubiese una posible descomposicion en sumandos, nuestras parejascandidatas tendrıan que ser (2, 2) o (99, 99) cuyos productos son 4 y 9801, que, enambos casos, solo admiten una descomposicion para Mr. P. Es decir, Mr. P conocerıalos numeros M y N , pero anteriormente dijo que no los conocıa;

ahora Mr. P conoce los numeros. Esto es porque entre las posibles parejas candidatassolo hay una cuya suma harıa que Mr. S dijese lo anterior;

Mr. S tambien tiene los numeros. Del mismo modo que antes, solo hay una parejacandidata que le permite Mr. P conocer los numeros con la informacion que tenıahace un momento.

Page 54: Un lenguaje lógico funcional con restricciones1

54 CAPITULO 2. EL SISTEMA T OY

Desde el punto de vista logico el problema esta casi resuelto (no es difıcil formalizarlogicamente el problema), pero seguimos sin conocer efectivamente los numeros (en el casode que existan). Traslademos el razonamiento anterior a un programa T OY que haga labusqueda de la solucion (o soluciones) por generate & test: se generan candidatos y secomprueba si el dialogo del enunciado tiene sentido:

solution X :-candidates X,doesntKnowP X, knowsSthatPdoesntKnow X, nowPknows X, nowSknows X

Para la generacion de candidatos utilizaremos una funcion indeterminista:

candidates :: (int,int) -> boolcandidates (M,N) :- between 2 99 M, between M 99 N

between :: int -> int -> int -> boolbetween X Y X :- X <= Ybetween X Y Z :- X < Y, between (X+1) Y Z

Primero se genera un candidato M entre 2 y 99, y luego otro N entre M y 99. Ası te-nemos 1 < M ≤ N < 100 y se evitan las simetrıas. La funcion between devuelve indeter-ministamente un numero perteneciente a un intervalo dado.

Para los predicados correspondientes al test necesitaremos dos funciones auxiliares.Una de ellas, summands, debe generar todas las posibles descomposiciones de un numerocomo suma de otros dos:

summands :: int -> [(int,int)]summands N = summands2 (firstSummands N)

firstSummands N = if N <= 101 then (2,(N-2)) else ((N-99),99)

summands2 :: (int,int) -> [(int,int)]summands2 (N,M) = if N>M then []

else [(N,M)|summands2 ((N+1),(M-1))]

La idea es generar un par inicial mediante firstSummands, cuyo segundo elementosea maximo (dos casos). Por ejemplo, si el numero a descomponer es 145 el primer parsera (46, 99) y si es 56 el par sera (2, 54). Los siguientes pares se forman “quitando uno alsegundo elemento y sumandolo al primero” (la suma del par permanece invariable) y estoes lo que hace summands2.

La segunda funcion auxiliar factors sirve para generar todas las posibles descompo-siciones de un numero N como producto de otros dos. Para ello se utiliza un contador queinicialmente vale 2, y se comprueba si existe otro numero tal que al multiplicarlo por elcontador sea N . En factors se llama a factors2 (con el contador inicializado a 2), quehara las comprobaciones e incrementara el contador:

factors :: int -> [(int,int)]factors N = factors2 N 2

factors2 :: int -> int -> [(int,int)]factors2 N M = if M > (trunc (sqrt N)) then []

Page 55: Un lenguaje lógico funcional con restricciones1

2.5. EJEMPLO 2. PUZLE ARITMETICO 55

else if (((N ‘mod‘ M) /= 0 ) \/ ((N ‘div‘ M) > fin))then factors2 N (M+1)else [(M,(N ‘div‘ M)) | (factors2 N (M+1))]

Los cuatro predicados del test correspondientes a cada una de las sentencias del dialogoahora se pueden programar con bastante naturalidad como funciones booleanas, que ope-raran una pareja (A,B) candidata a solucion. La primera de ellas afirma que deben existiral menos dos descomposiciones como producto de dos numeros para el numero A ∗B:

doesntKnowP (A,B) = atLeastTwo (factors (A*B))

atLeastTwo [] = falseatLeastTwo [X] = falseatLeastTwo [X,Y|Z] = true

La segunda funcion debe comprobar que todas las posibles descomposiciones de A+Ben sumandos verifican el test anterior. Utilizamos la funcion all que vimos en el ejemploanterior (tambien esta en el archivo de utilidades misc.toy). Esta funcion es:

knowsSthatPdoesntKnow (A,B) = all doesntKnowP (summands (A+B))

El tercer test comprueba que solo existe un posible candidato que verifique el segundotest. Y el ultimo test comprueba que solo hay un candidato que cumpla el tercer test. Deeste modo estamos propagando la informacion de un test al siguiente, que restringira aunmas el conjunto de posibles soluciones. El tercer y el cuarto test quedan de este modo:

nowPknows (A,B) = existsOne (factors (A*B)) knowsSthatPdoesntKnow

nowSknows (A,B) = existsOne (summands (A + B)) nowPknows

existsOne :: [A] -> (A -> bool) -> boolexistsOne [] F = falseexistsOne [P|R] F = if (F P == false) then existsOne R F

else noOne R F

noOne :: [A] -> (A -> bool) -> boolnoOne [] F = truenoOne [P|R] F = if (F P == false) then noOne R F

else false

Con esto concluye el programa. Notese que se han utilizado varias funciones booleanasen vez de predicados. La diferencia entre unas y otros es que las funciones pueden devolverel valor false, mientras que los predicados solo pueden tener exito o fallar. En nuestroprograma nos interesan las funciones porque el hecho de que un test no se verifique sobreuna pareja determinada es una informacion util. Por ejemplo, existOne comprueba quede una lista de candidatos uno y solo uno satisface un determinado test, es decir, el test“toma el valor false sobre todos los elementos de la lista excepto uno”. El hecho de “nosatisfacer un test”, para un predicado supone un fallo que finaliza el computo.

El resultado de la ejecucion produce una unica solucion:

Page 56: Un lenguaje lógico funcional con restricciones1

56 CAPITULO 2. EL SISTEMA T OY

TOY> solution X

yesX == (4, 13)

Reconstruyamos algunos pasos del razonamiento que hacen los dos hombres: Mr. Sconoce el dato S = 17 y Mr. P sabe P = 52. Mr. P razona sobre los posibles candidatosM y N entre 2 y 99 tal que M ∗N = 52. Hay dos posibles pares:

(2, 26), (4, 13)

Pero no sabe cual de las parejas es la solucion. Mr. S hace un razonamiento similar yobtiene los pares:

(2, 15), (3, 14), (4, 13), (5, 12), (6, 11), (7, 10), (8, 9)

Mr. S sabıa que Mr. P tampoco tenıa el resultado porque los productos que obtienecon estos candidatos tienen todos mas de una factorizacion posible (la unica posibilidadpara que esto no ocurriese serıa que hubiese un par de numeros primos).

Mr. P ahora conoce el resultado, porque en el supuesto de fuese el par (2, 26), Mr.S habrıa operado con la suma, 28, obteniendo entre otras, las parejas (5, 23) y (11, 17)(pares de primos). Pero Mr. S estaba seguro de que Mr. P no conocıa el resultado, y estoes porque el producto no podıa descomponerse en producto de dos primos. Luego, la parejabuscada debe ser (4, 13).

Mr. S intenta reconstruir todo el razonamiento con cada uno de sus candidatos (no lodetallamos) y concluye que para que Mr. P conozca el resultado los numeros deben ser 4y 13.

2.6. Comparacion con otros estilos de programacion

En el codigo que produce T OY hemos procurado obtener el mayor rendimiento comoveremos en el proximo capıtulo. Sin embargo, no debemos olvidar que T OY hace unatraduccion a Prolog. Para tratar seriamente el tema de la eficiencia y poder hacer com-paraciones equitativas con otros sistemas (en especial logicos y funcionales) serıa deseablecontar con una implementacion de T OY que generase un codigo objeto especializado paraeste tipo de lenguajes (maquina abstracta). De hecho, la comparacion con Prolog puedeparecer paradojica (realmente se comparan dos programas Prolog), pero es posible comoveremos en un ejemplo.

Este apartado no pretende, por tanto, hacer una comparativa exhaustiva con otroslenguajes en cuanto al rendimiento. Con respecto a los lenguajes funcionales simplementediremos que se ha podido apreciar que en determinados ejemplos T OY hace un mejoraprovechamiento de la memoria debido al indeterminismo y, en consecuencia, es capazde completar computos en los que otros sistemas como Hugs [JJ97] agotan los recursos.En cuanto al estilo de programacion o expresividad, las principales diferencias vienendadas por las restricciones de igualdad, desigualdad y aritmeticas, el indeterminismo y lospatrones de orden superior. En [CR98] puede encontrarse una comparativa mas extensaentre los estilos logico funcional y funcional puro, que se centra en el diseno de parsersde reconocimiento/generacion de lenguaje y que saca especial provecho de los patrones deorden superior.

Page 57: Un lenguaje lógico funcional con restricciones1

2.6. COMPARACION CON OTROS ESTILOS DE PROGRAMACION 57

Con respecto a Prolog, en la practica, T OY saca especial partido de la pereza en cuantoa la eficiencia. En cuanto a la expresividad, el potencial de las funciones en general y de lasfunciones de orden superior en particular es enorme. Por otro lado, el hecho de disponer deun solido sistema de tipos confiere al lenguaje un estilo de programacion preciso y limpio.

A continuacion vamos a explorar dos ejemplos en los que se compara T OY con Prolog,en cuanto a la eficiencia en el primero, y en cuanto al estilo en el segundo.

2.6.1. Ordenacion de listas

En este apartado compararemos dos programas para ordenacion de listas por generate& test: se generan permutaciones de la lista dada y se comprueba si estan ordenadas. Enla tabla 2.1 se muestra la version T OY del programa y la version analoga en Prolog.

VERSION T OYsort lst Xs = sort aux (permut Xs)

sort aux Xs = Xs <== sorted Xs

permut [] = []permut [X| Xs] = insert X (permut Xs)

insert X Ys = [X| Ys]insert X [Y| Ys] = [Y| insert X Ys]

sorted [] = truesorted [X] = truesorted [X,Y|Ys] = sorted [Y|Ys]

<== X<=Y

VERSION PROLOG

sort lst(Xs,Ys):-permut(Xs,Ys),sorted(Ys).

permut([],[]).permut([X|Xs],Ys):-permut(Xs,Zs),

insert(X,Zs,Ys).insert(X,Ys,[X|Ys]).insert(X,[Y|Ys],[Y|Zs]):-insert(X,Ys,Zs).

sorted([]).sorted([ ]).sorted([X,Y|Ys]):-X=<Y,

sorted([Y|Ys]).

Cuadro 2.1: Ordenacion de una lista por generate & test

En la tabla 2.2 se encuentran los tiempos de ejecucion13 de dos objetivos medidos enmilisegundos. En el primero de ellos han de calcularse todas las permutaciones de unalista con 8 elementos (40320 en total). En este caso Sicstus es unas 22 veces mas rapidoque T OY. En el segundo ejemplo se trata de ordenar una lista con 10 elementos, y en estecaso, T OY es unas 77 veces mas rapido que Sicstus.

Objetivo Sicstus T OYTodas las permutaciones de [0,1,2,3,4,5,6,7] 990 21750

Ordenacion de [4,6,2,8,1,3,9,0,5,7] 5400 70

Cuadro 2.2: Tiempos de ejecucion

La explicacion del fenomeno, aunque sencilla, no deja de sorprender. En el primer casohay que calcular todas las permutaciones sin posibilidad de poda, y Sicstus saca partido desu maquina abstracta. En el segundo ejemplo, sin embargo, T OY aprovecha la pereza parahacer un computo mas inteligente. Genera poco a poco las permutaciones, comprobandoen cada paso si se mantiene el orden. En caso afirmativo sigue generando y si no, deja deconstruir la permutacion actual y vuelve atras. De este modo la unica permutacion que

13Para las medidas de tiempo se ha utilizado una maquina con procesador Pentium 100, bajo el sistemaoperativo Linux.

Page 58: Un lenguaje lógico funcional con restricciones1

58 CAPITULO 2. EL SISTEMA T OY

genera completamente es precisamente la que esta ordenada, mientras que Sicstus generarealmente todas las permutaciones y posteriormente hace la comprobacion.

Por supuesto, en Prolog se podrıa simular la pereza (de hecho es lo que hace T OY),pero el programa resultante no serıa tan sencillo como el que presentamos. En T OY elprogramador no se preocupa de programar explıcitamente este comportamiento. Es elsistema el que se encarga de propiciarlo y ahı esta su elegancia.

Resaltemos que en un programa funcional no se podrıa hacer una generacion inde-terminista de las permutaciones, precisamente porque no se admiten funciones indeter-ministas. Es decir, el programa T OY de la tabla 2.1, aunque tiene aspecto funcional, noes ejecutable por un lenguaje como Hugs o Haskell. Es cierto que es posible simular estecomportamiento utilizando la tecnica de la “lista de exitos” propuesta en [Wad85].

Aunque este ejemplo es un ejemplo “escogido” para mostrar las bondades de la pereza,debemos recordar que la cantidad de problemas interesantes que pueden resolverse porgenerate & test no es en absoluto despreciable. Este es solo un ejemplo ilustrativo escogidopor su sencillez.

2.6.2. El problema del laberinto revisitado

Nuestro objetivo ahora es mostrar la riqueza expresiva que ofrece la programacionlogico funcional frente a la programacion logica, cuando se utilizan restricciones sobre losreales. Para ello nos apoyaremos en el Problema del Laberinto que se trato en 2.4. Para esteproblema se propuso una solucion por generate & test, pero ahora, mas que la eficiencia,nos interesa comparar los estilos de programacion. Construiremos una version Prolog (conrestricciones) para resolver la misma cuestion.

Antes de abordar el problema en sı debemos abordar otro aspecto: ¿como representarlas regiones en Prolog? (en T OY eran funciones caracterısticas, pero ahora no hay funcio-nes). La primera idea que puede surgir es: una region es un par de variables restringidas(X, Y ). Por ejemplo, el rectangulo definido por las esquinas (0, 0), (2, 2) podrıa represen-tarse mediante el par de variables (A,B), con las restricciones 0 ≤ A ≤ 2, 0 ≤ B ≤ 2.Pero esto plantea problemas, ya que si ahora deseamos saber si los puntos (0, 0), (1, 1)pertenecen a dicho rectangulo (cuya respuesta debe ser afirmativa), debemos verificar queambos satisfacen las restricciones pertinentes. Para verificar la primera, el par (A,B) debeunificarse con (0, 0) y para la segunda con (1, 1), lo cual no es posible y harıa fallar elobjetivo. Esta representacion no funciona de forma adecuada.

Vamos a representar las regiones mediante terminos Prolog. Por ejemplo, el rectanguloanterior se representarıa con el termino rectangle((0, 0), (2, 2)). Ahora necesitaremos unpredicado explıcito de pertenencia a una region. Definimos el predicado belongs, comunpara todas las regiones y otros especıficos para las regiones particulares (para nuestroejemplo solo utilizaremos rectangulos):

belongs(Bool,(X,Y),R):-R =..[Name | Args],R1=..[Name,Bool,(X,Y) | Args],call(R1).

rectangle(true,(X,Y),(A,B),(C,D)):-{ A=<X, X=<C, B=<Y, Y=<D }.

rectangle(false,(X,Y),(A,B),(C,D)):-{ X<A;

Page 59: Un lenguaje lógico funcional con restricciones1

2.6. COMPARACION CON OTROS ESTILOS DE PROGRAMACION 59

A=<X, C<X;A=<X, X=<C, Y<B;A=<X, X=<C, B=<Y, D<Y }.

El primer argumento de estos predicados indicara la pertenencia o no de un puntoa una region (true o false). Es importante que ocupe el primer lugar para evitar laconcatenacion de listas en la construccion de las llamadas en el predicado belongs.

Las definiciones de traslaciones y homotecias no necesitan muchas explicaciones:

translate(Bool,(X,Y),R,(V1,V2)):-{ X=X1+V1, Y=Y1+V2 },belongs(Bool,(X1,Y1),R).

homothety(Bool,(X,Y),R,(C1,C2),K):-{ X=C1+K*(X1-C1), Y=C2+K*(Y1-C2) \},belongs(Bool,(X1,Y1),R).

En este contexto la solucion por generate & test al Problema del Laberinto puede serla siguiente:

solution(R,Pts,Mvs,Mvs1):- permut(Mvs,Mvs1), check(R,Pts,Mvs1).

permut([],[]).permut([X | Xs],Ys):- permut(Xs,Xs1), insert(X,Xs1,Ys).

insert(X,[],[X]).insert(X,[ Y | Ys],[X,Y | Ys]).insert(X,[Y | Ys],[Y | Zs]):- insert(X,Ys,Zs).

check(R,Pts,Mvs):- doMoves(R,Mvs,R1), avoids(Pts,R1).

doMoves(R,[],R).doMoves(R,[M | Mvs], union(R,R2)):-

M=..[N | Args],R1=..[N,R | Args], doMoves(R1,Mvs,R2).

avoids([],_).avoids([P | Pts],R):- belongs(false,P,R), avoids(Pts,R).

union(Bool,(X,Y),R1,R2):-belongs(B1,(X,Y),R1),belongs(B2,(X,Y),R2),or(B1,B2,Bool).

or(true,_,true).or(_,true,true).or(false,false,false).

Las versiones T OY y Prolog son bastante similares, pero debemos hacer algunoscomentarios. En Prolog hemos tenido que utilizar caracterısticas impuras del lenguaje,

Page 60: Un lenguaje lógico funcional con restricciones1

60 CAPITULO 2. EL SISTEMA T OY

como son el predicado de descomposicion = .. y el de llamada call. Dejando de lado losinconvenientes teoricos derivados del uso de tales predicados, el hecho es que en la practi-ca afectan negativamente a la legibilidad del programa. En T OY estos artificios no sonnecesarios; las funciones de orden superior son una forma elegante de prescindir de ellos.

Objetivo Sicstus TOYTodas las soluciones al laberinto 2320 1770

Cuadro 2.3: Tiempos de ejecucion para el laberinto

En el predicado rectangle ha sido necesario incluir una clausula explıcita (la segunda)para detectar la no pertenencia de un punto al rectangulo en cuestion. Y ademas en dichaclausula se ha tenido que tomar la precaucion de que las restricciones sean mutuamenteexcluyentes para evitar respuestas redundantes. Este problema ni siquiera se plantea enla version T OY.

Por ultimo, y aunque no era la meta de este apartado, T OY nuevamente es masrapido debido a la pereza. En la figura 2.3 se muestran los tiempos de busqueda de todaslas soluciones al objetivo concreto que se planteo para este problema en 2.4.

Page 61: Un lenguaje lógico funcional con restricciones1

Capıtulo 3

Mecanismo de computo

3.1. Introduccion

El mecanismo clasico de computo de los lenguajes funcionales esta basado en reescri-tura, mientras que en los lenguajes logicos, la unificacion es la operacion fundamental.Una de las capacidades mas notables de los lenguajes logico-funcionales, quiza la masimportante, es la posibilidad de utilizar las definiciones de funciones de forma reversiblede modo analogo al uso de predicados en Prolog. Esta habilidad exige un mecanismo decomputo que, de algun modo, sea capaz de integrar la reescritura y la unificacion. En estesentido se han presentado distintas propuestas, algunas de ellas basadas en reescritura,con la que se simula la unificacion como ESCHER ([Llo94, Llo95]). Pero la mas extendi-da y estudiada es el narrowing o estrechamiento que surge como extension natural de lareescritura y que utilizan lenguajes como BABEL ([MR89, MR92]) , BABLOG, ([AG94])Curry ([HKM95, He97]), y el propio T OY. Informalmente podemos definir el narrowingcomo reescritura con unificacion. El trabajo [Han94] es una referencia bastante completay actual del narrowing como mecanismo operacional de los lenguajes logico funcionales.

El narrowing tiene, en general, un alto grado de indeterminismo (don’t know) debidoa la eleccion del redex y de la regla de reescritura a utilizar. En consecuencia, los espaciosde busqueda generados pueden ser enormes, lo que sugiere la necesidad de encontrar unaestrategia de narrowing que reduzca este espacio, para trasladar la tecnica a un sistemareal. En [LLR93] se estudia una estrategia de narrowing perezoso guiada por la demanda,cuya especificacion se presenta como una traduccion de las reglas de las funciones a codigoProlog. El mecanismo operacional de T OY esta basado inicialmente en el que se proponeen este trabajo. Las desigualdades de T OY se apoyan en [L92, L94] y [AGL94], si bienla implementacion final difiere bastante de estas propuestas. El tratamiento del orden su-perior esta fundamentado en [G94] y [AGL94]. Acerca de la incorporacion de restriccionessobre los numeros reales en programacion logico funcional puede consultarse [AHL+96] ysobre este tipo de restricciones en el caso concreto de T OY, vease [HLS+97].

En este capıtulo nos ocuparemos del mecanismo operacional de T OY. Estudiaremosla traduccion de funciones y predicados T OY a predicados Prolog, ası como el codigo(tambien Prolog) para resolver restricciones (igualdad, desigualdad y aritmeticas). Comovimos en 2.3.10, en realidad un predicado en T OY no es mas que una funcion que devuelveel valor true y la notacion clausal es sencillamente un azucar sintactico. Por lo tanto, enadelante hablaremos unicamente de traduccion de funciones.

Los predicados correspondientes a la traduccion de un programa T OY y los de re-solucion de restricciones tienen un alto grado de interdependencia, que en el codigo se

61

Page 62: Un lenguaje lógico funcional con restricciones1

62 CAPITULO 3. MECANISMO DE COMPUTO

manifiesta en forma de recursion mutua. Por lo tanto, las referencias a apartados tantoanteriores como posteriores son inevitables en la exposicion y sera necesario un cierto nivelde abstraccion en algunos puntos de la misma. El orden en la presentacion de los contenidospretende, en la medida de lo posible, facilitar una comprension incremental. Quiza resultellamativo que el orden superior sea uno de los primeros puntos que se abordan, pero conello quedaran explicados algunos conceptos que apareceran (a veces de forma implıcita)en diversos apartados del resto del capıtulo.

Por otro lado, en esta parte suponemos que el lector esta familiarizado con el lenguajeProlog, aunque trataremos de explicar brevemente los detalles mas crıticos o de caractertecnico que puedan aparecer, ası como las caracterısticas propias del sistema Sicstus Prologque utilicemos.

3.2. Vision general del proceso de compilacion y ejecucionde objetivos

El proceso de compilacion de un programa en T OY sigue el esquema de la figura 3.1.Sobre el programa de usuario se lleva a cabo, en primer lugar, un analisis lexico (automatasfinitos) y sintactico. El analizador sintactico es basicamente una DCG ([SS86, O’K90]),aunque no se utiliza el traductor de DCG’s incorporado en Sicstus Prolog, sino que seha desarrollado un traductor propio basado en [CM87], con bastantes modificaciones;la mayorıa de ellas para incluir nuevos parametros (tablas de compilacion y gestion deerrores). En esta fase se hace ademas un analisis de dependencia funcional en el que seestablece el conjunto de funciones que utiliza cada funcion en su definicion. El inferidorutiliza estas dependencias para construir el grafo de dependencias, sobre el que se hacela inferencia de tipos (basada en [AJ93]). Si en todo este proceso no se detectan erroressintacticos ni de tipos se genera el codigo intermdedio (3.7).

PROGRAMA FUENTE

CODIGO INTERMEDIO

CODIGOOBJETOgeneracion de codigo

analisis lexico analisis sintactico

inferencia de tiposanalisis de dependenciasanalisis de dependencias

Figura 3.1: Proceso de compilacion

Sobre el codigo intermedio se lleva a cabo la generacion de codigo. Para cada una delas funciones, T OY hace un sofisticado analisis de demanda sobre las reglas que la defineny genera, como paso intermedio, un arbol definicional ([Ant92]). En este arbol se recogetoda la informacion necesaria para la evaluacion de la funcion estructurada de acuerdo conla demanda de patrones de la misma. De hecho, describe la secuencia de computos quese han de realizar para evaluar una llamada a dicha funcion. El orden implıcito en estasecuencia trata de minimizar el numero de pasos de computo e intenta no reevaluar lasexpresiones. La idea responde a la nocion de pereza tıpica en programacion funcional, queen nuestro caso puede recogerse en el siguiente principio: “evaluar en cada momento soloaquello que es estrictamente necesario para continuar el computo”. El arbol es construidoexplıcitamente (puede mostrarse con el comando /tree como se explico en 2.2.4) y sobreel se apoyara la generacion del codigo Prolog correspondiente. En la seccion 3.10 veremoslos algoritmo de construccion de arboles y de generacion de codigo.

Page 63: Un lenguaje lógico funcional con restricciones1

3.2. VISION GENERAL DEL PROCESO DE COMPILACION Y EJECUCION DE OBJETIVOS63

Una vez compilado el programa fuente, la resolucion de objetivos utiliza tres bloquesde codigo separados en tres archivos distintos, como muestra la figura 3.2.

CODIGO OBJETO <file.pl>

toycomm.pl

+

+primitives.pl

OBJETIVO RESPUESTAa. lexico, a. sintactico, tipos

Figura 3.2: Resolucion de objetivos

El contenido de estos archivos es el siguiente:

<file>.pl contiene propiamente el codigo producido por la traduccion de un pro-grama, es decir, los predicados especıficos para la evaluacion de las funciones delprograma de usuario y otra informacion sobre las mismas. El nombre de este archivosera el mismo que utilizo el usuario para el archivo fuente, pero con extension .pl(vease 2.2.3). En la seccion 3.10 trataremos en profundidad los predicados corres-pondientes a la traduccion de funciones y otros que contiene este archivo.

toycomm.pl contiene, entre otros, los predicados de resolucion de restricciones ycomputo de formas normales de cabeza. Todos los predicados que se estudiaran eneste capıtulo, a excepcion de los de la seccion 3.10, estan incluidos en este archivo.De hecho, trataremos exhaustivamente su contenido. En el apendice D se muestra elarchivo toycomm.pl tal y como esta en el sistema.

primitives.pl almacena el codigo de las funciones primitivas del sistema. Este ar-chivo sera reemplazado por primitivesClpr.pl (nunca estan los dos en memoriasimultaneamente), cuando se activan las restricciones sobre reales. Ambos contienenexactamente las mismas funciones, pero con definiciones diferentes. El apendice E esel codigo del archivo primitives.pl y el F corresponde a primitivesClpr.pl.

El archivo toycomm.pl y los de primitivas son comunes en la ejecucion de objetivos.Una observacion de caracter global es que en los predicados del archivo toycomm.pl se usafrecuentemente el predicado Prolog de corte ‘!’, en algunos casos por razones de eficienciapara evitar hacer tentativas que fallaran con certeza y provocar el fallo cuanto antes. Estostipos de corte son en principio prescindibles. Sin embargo, en otras ocasiones, se utilizanpara establecer alternativas mutuamente excluyentes y son fundamentales, ası como el or-den de las clausulas. Un caso particular de este ultimo uso es el de aquellos predicados cuyaultima clausula representa el caso “si no es ninguno de los anteriores” (el predicado hnfde la seccion 3.9 es un ejemplo), y es fundamental que las clausulas anteriores contenganel corte.

Nota: Algunos de los sımbolos que maneja internamente el sistema como los de funcion,susp, apply y otros, en el codigo real del sistema aparecen precedidos de uno o varios ‘$’.Este es un detalle tecnico para evitar colisiones entre los nombres internos que utiliza elcompilador y los definidos por el usuario. A lo largo de la exposicion se han omitido todoslos sımbolos ‘$’ para facilitar la legibilidad de la traduccion (esta observacion debe tenersepresente al editar archivos generados por T OY).

Page 64: Un lenguaje lógico funcional con restricciones1

64 CAPITULO 3. MECANISMO DE COMPUTO

3.3. Preliminares

En lo sucesivo suponemos que un programa T OY tiene asociada de forma implıcitauna signatura Σ = DC ∪ FS, siendo DC el conjunto de sımbolos de constructora y FSel conjunto de sımbolos de funcion. Suponemos ademas que cada uno de estos sımboloslleva asociada una aridad; en el caso de las funciones esta aridad se refiere a la aridad deprograma (numero de argumentos que tienen las reglas que la definen). Con DCn y FSn

denotaremos a los conjuntos de constructoras y funciones de aridad n respectivamente. Enalgunas ocasiones utilizaremos la notacion l/n que representa el sımbolo l (constructora ofuncion) de aridad n. Por ejemplo, : /2 es la constructora de listas de aridad 2.

Asumimos tambien un conjunto infinito de variables V. Las expresiones se construyensobre los conjuntos V, DC y FS de acuerdo con la regla de formacion que vimos en 2.3.4:

E = X|num|(E1, ..., En)|c|f |(E1 E2) (i)

siendo X ∈ V, num un numero entero o real, c ∈ DC, f ∈ FS y E1, E2 expresiones.Como en la sintaxis de T OY, utilizaremos cadenas que comienzan por mayuscula para

las variables, y cadenas que comienzan por minuscula para constructoras y funciones.Para las sustituciones utilizaremos en algunas ocasiones la notacion t[X/s] que representael termino resultante de reemplazar todas las ocurrencias de la variable X por s en t,donde s y t son terminos.

3.4. Los objetos sintacticos de T OY y su representacion Pro-log

En la traduccion a Prolog que hace el sistema cada expresion T OY tiene una repre-sentacion concreta como termino Prolog (tanto en el codigo intermedio como en el final).Por ejemplo, T OY utiliza notacion currificada (sin parentesis en las aplicacion de sımbo-los de funcion y constructora), mientras que en la representacion Prolog no es posibletal representacion (la expresion suc X que tiene perfecto sentido en T OY, en Prologdebera representarse como suc(X)).

En gran medida esta representacion viene dada por la transformacion a primer ordenque realiza el sistema y que veremos en 3.5. Pero esta transformacion es relativamenteabstracta y algunos aspectos no quedaran reflejados. En particular, conviene aclarar comose tratan los casos especiales de los numeros, los caracteres y las tuplas, ası como sus tipos(vease 2.3.2).

Los numeros son constructoras de aridad 0 cuya representacion depende del estado delproceso de compilacion. Antes de que el inferidor haya verificado la correccion de los tiposen el programa, los enteros se presentan en el formato int(3) y los reales como float(4,3).De este modo el inferidor puede distinguir enteros y reales. En el codigo intermedio yen la traduccion final ambos tipos se presentan en coma flotante (3 se representa como3.0), ya que una vez que se sabe que los tipos son correctos, no importa la distincion. Larepresentacion del tipo de un numero depende de la informacion que se tenga del mismo:

Si es un entero el tipo se anotara como num(int).

Si es un real, num(float)

Y si puede ser tanto un entero, como un real, tendremos num(A), siendo A unavariable. Esta notacion esta ıntimamente relacionada con la sobrecarga de algunas

Page 65: Un lenguaje lógico funcional con restricciones1

3.4. LOS OBJETOS SINTACTICOS DE T OY Y SU REPRESENTACION PROLOG65

primitivas que se trato en 2.3.15. Por ejemplo, la funcion ‘+’ esta declarada contipo real → real → real, pero como se explico, puede funcionar con enteros, encuyo caso tomara el tipo int → int → int. Esta sobrecarga, se consigue a bajonivel con la declaracion num(A) → num(A) → num(A), de modo que si el inferidortiene suficiente informacion para decidir que en una llamada a la funcion el tipo dealguno de los argumentos o el resultado es de tipo entero, anotara el tipo num(int)para ese tipo. Como la variable A es compartida por las tres expresiones num(A),automaticamente el tipo de la llamada se instanciara a num(int) → num(int) →num(int), que representa el tipo int → int → int como se pretendıa.

Los caracteres tambien son constructoras de aridad 0. Tanto en las fases intermediascomo en la traduccion final se representan en el formato char(97), donde el numero es elcodigo ASCII correspondiente. Y el tipo sera sencillamente char.

Para las tuplas se utiliza internamente la constructora de aridad 2 ‘,’, pero aquı surgeun problema tecnico ya que Sicstus tiene definido este operador con asociatividad por laderecha lo cual tiene un efecto indeseado. Por ejemplo, en Sicstus la expresion (1, 2, 3) esequivalente a (1, (2, 3)), mientras que en T OY estas expresiones son distintas: la primeraes una terna y la segunda un par, cuyo segundo elemento es a su vez un par. T OY resuelveeste problema obviando la asociatividad del operador ’,’ mediante la introduccion de lanueva constructora tup de aridad 1 por encima de ‘,’. Por ejemplo, la tupla (1, 2, 3) serepresenta como tup(1, 2, 3), mientras que (1, (2, 3)) se traduce a tup((1, tup((2, 3)))). Deeste modo, ambas expresiones son claramente diferenciables por el sistema. El tipo asociadoa una tupla de la forma (e1, ..., en) se escribira como tuple(T1, ..., Tn) donde Ti es el tipode ei.

Una peculiaridad de las tuplas es que no admiten aplicacion parcial porque esto plan-tearıa un problema de ambiguedad. Como la misma constructora representa un numeroinfinito de constructoras (una para cada aridad, segun se vio en 2.3.2), no serıa posibledistinguir entre aplicaciones totales y parciales. Por ejemplo, si utilizasemos ‘,’ como cons-tructora de tuplas, la expresion (, ) 1 2 se puede interpretar como una aplicacion total de laconstructora ‘,’ entendida como constructora de pares, pero tambien como una aplicacionparcial de la misma constructora entendida como la constructora de ternas (en general den-tuplas). Este problema en realidad no se plantea porque el usuario puede utilizar tuplasen sus programas de acuerdo con la sintaxis de la regla de formacion, pero no tiene acce-sible directamente ningun sımbolo de constructora de tuplas. En contraste, porejemplo para las listas tiene accesible el sımbolo ‘:’ que admite aplicacion parcial como enel siguiente computo:

TOY> (:) 1 == F, F [2] == L

yesF == (1:)L == [ 1, 2 ]

Observese que F (pone 1 como cabeza de la lista que se le pasa como argumento) es unafuncion que se define utilizando la constructora de listas ‘:’. No es posible hacer algo similarcon las tuplas. Lo analogo serıa (, ) 1 == F , que no esta permitido porque ’,’ (ni ningunotro sımbolo) se reconoce como constructora de tuplas.

La representacion del resto de constructoras quedara establecida en la siguiente seccion.Unicamente destacaremos que todas las expresiones que hacen uso de operadores infijos

Page 66: Un lenguaje lógico funcional con restricciones1

66 CAPITULO 3. MECANISMO DE COMPUTO

(tanto constructoras como funciones) en la traduccion final aparecen en forma prefija. Porejemplo, la lista [1, 2, 3] (equivalente a 1 : (2 : (3 : [ ]))) en la traduccion aparecera como′ :′ (1,′ :′ (2,′ :′ (3, [ ]))). Los tipos de estas constructoras se representan de manera natural;el tipo de la lista anterior es sencillamente [int].

En cuanto a las llamadas a funcion (aplicacion total) la representacion tambien que-dara establecida en el siguiente apartado, salvo en el caso de las llamadas que aparecencomo argumento de otra funcion. En este caso, se representara en forma suspendida debidoal sharing que trataremos en 3.6.

3.5. Orden superior

En el capıtulo anterior (2.3.6) vimos que T OY es un lenguaje que admite funcionesde orden superior. En esta seccion explicaremos como se trata el orden superior. La ideaes hacer una transformacion a primer orden siguiendo las ideas de [G94] (con ligerasmodificaciones). En el codigo producido por el sistema el orden superior se simula confunciones de primer orden. Esta es una transformacion sintactica que produce un programaT OY a partir de un programa T OY, y no debe confundirse con la traduccion que hace elsistema y que produce codigo Prolog a partir de un programa T OY. La transformacionpuede entenderse como un paso intermedio previo a la traduccion.

Antes de formalizar el mecanismo de transformacion vamos a explorar la intuicion queencierra. Para ello recordemos la funcion map que se definio en 2.3.6:

map :: (A -> B) -> [A] -> [B]map F [] = []map F [X|Xs] = [F X|map F Xs]

Supongamos que contamos con una funcion apply que toma dos argumentos (el primerode tipo funcional) y produce como resultado la aplicacion del primero a segundo, sinpreocuparnos por el momento de como podrıa definirse esta funcion apply. Por ejemplo,apply add1 zero se evaluarıa a add1 zero (que a su vez se evaluarıa a suc zero). Utilizandoapply, la funcion map podrıa definirse como:

map F [] = []map F [X|Xs] = [apply F X|map F Xs]

Aparentemente no se ha hecho un gran avance con la introduccion de esta funcion,puesto que ella misma es de orden superior. Sin embargo, no es difıcil intuir que todaslas aplicaciones de orden superior pueden representarse usando apply en esta forma. Enconsecuencia hemos acotado o reducido el problema original, ya que el orden superior se ha“concentrado” en apply y nuestro problema ahora es definir apply. En 3.5.1, veremos estadefinicion, pero antes vamos a definir la transformacion a primer orden de modo preciso.Un primer efecto, a la vista de lo anterior, es que el conjunto de sımbolos de funcion FSde la signatura original del programa se incrementa con el nuevo sımbolo apply de aridad2.

Vamos a desglosar la transformacion en dos pasos: en el primero se introduciran losapply’s necesarios (como en el caso de map) y en el segundo haremos propiamente latraduccion a primer orden. Para explicar como se introducen los apply’s en un programaanalizaremos las distintas expresiones del lenguaje, de acuerdo con la regla de formacion

Page 67: Un lenguaje lógico funcional con restricciones1

3.5. ORDEN SUPERIOR 67

(i) que vimos en 3.3. Las variables, los numeros y las tuplas (primera, segunda y terceraalternativas) no son afectados por la introduccion de apply’s y quedan igual.

Los sımbolos de constructora y funcion (cuarta y quinta alternativas) tampoco sufrenmodificaciones; sin embargo, cada sımbolo de constructora de aridad n y cada sımbolode funcion de aridad m, en este primer paso van a generar n − 1 y m − 1 nuevas cons-tructoras respectivamente, correspondientes a todas las aplicaciones parciales de dichossımbolos. Esto quiere decir que el conjunto de sımbolos de la signatura original se incre-menta con nuevas constructoras. De esta forma, con el nuevo sımbolo de funcion apply ylas nuevas constructoras la signatura original del programa, Σ = DC ∪ FS, se amplıa enla transformacion a Σ′ = DC ′ ∪ FS′, donde:

DC ′ = DC ∪ {φi | φ ∈ DC ∪ FS, 0 ≤ i < aridad(φ)}FS′ = FS ∪ {apply/2}

En DC ′, cada φi es una constructora de aridad i, que se utilizara para representar la apli-cacion parcial de la constructora o funcion φ con aridad i. Por ejemplo, para una funcionf de aridad 3, ademas del sımbolo de funcion f de aridad 3, la nueva signatura inclura lasconstructoras f0, f1 y f2 (para la funcion map, se incluiran las nuevas constructoras map/0y map/1).

Las tuplas son una excepcion como se vio en 3.4 y no producen ninguna constructoraadicional.

Una primera consecuencia que debe tenerse presente es que una aplicacion parcialde una funcion es en la traduccion, a todos los efectos, una constructora. Even-tualmente, si esta aplicacion parcial recibe mas argumentos hasta convertirse en aplicaciontotal, las reglas de apply se encargaran de hacer la llamada a la funcion correspondiente.Informalmente podemos decir que esta constructora “se convierte” en funcion al recibirtodos sus argumentos.

La ultima alternativa de la regla de formacion de expresiones es (E1, E2), que permite laconstruccion de aplicaciones. A continuacion se presentan las distintas formas que puedentener las aplicaciones y la introduccion de apply’s correspondiente:

c e1...em, con c ∈ DCn y m ≤ n, se reemplaza por c re1...rem siendo rei el resultadode introducir apply’s en ei.

f e1...em, con f ∈ FSn y m ≤ n, se reemplaza por f re1...rem siendo rei el resultadode introducir apply’s en ei.

f e1...en en+1, con f ∈ FSn, se reemplaza por apply (f re1...ren) ren+1, siendo rei

el resultado de introducir apply’s en ei. En este caso, la funcion f esta totalmenteaplicada a los n primeros argumentos y el resultado se aplica a su vez al n+1-esimoargumento. Este mecanismo es facilmente generalizable al caso f e1...en en+1...en+k

(f ∈ FSn) que se reemplaza por apply (...(apply(f re1...ren) ren+1)...) ren+k.

X e, con X ∈ V, se reemplaza por apply X re, siendo re el resultado de introducirapply’s en e. La generalizacion de este caso corresponde a la expresion X e1 e2...en,traducida a apply (...(apply (apply X re1) re2)...) ren.

Los efectos de la introduccion de apply’s son, por tanto: se amplıa la signatura delprograma con nuevos sımbolos de constructora y el sımbolo de funcion apply, se intro-ducen llamadas a la funcion apply en determinados puntos del programa segun hemos

Page 68: Un lenguaje lógico funcional con restricciones1

68 CAPITULO 3. MECANISMO DE COMPUTO

@ : ExpΣ −→ ExpΣ′

@(X) = X X ∈ V@(num) = num (numeros)@((e1, ..., em)) = (@(e1), ..., @(en)) (tuplas)@(c e1...em) = cm @(e1)...@(em) c ∈ DC@(f e1...em) = fm @(e1)...@(em) f ∈ FSn,m < n@(f e1...en en+1) = apply (f @(e1)...@(en)) @(en+1) f ∈ FSn

@(X e) = apply X @(e) X ∈ VCuadro 3.1: Introduccion de apply’s

visto y se generan las reglas para apply como veremos en el siguiente apartado. For-malmente podemos definir una funcion de transformacion para la introduccion de apply’s@ : ExpΣ → ExpΣ′ tal y como aparece en la tabla 3.1.

El resultado de esta primera transformacion ha generado expresiones en la signaturaextendida Σ′. Podemos considerar el rango de esta transformacion como un subconjuntode @ ExpΣ′ cuya regla de formacion es:

e = X|num|(e1, ..., en)|c e1...en|f e1...en

donde ei ∈ @ ExpΣ′ , i = 1..n; c ∈ DC ′n y f ∈ FS′n. Notese que en estas expresionestodas las aplicaciones son totales y lo unico que falta para convertirlas en expresionesde primer orden es eliminar la sintaxis currificada, esto es, introducir los parentesis y lascomas apropiadas. Este es el cometido de la funcion fo (first order) de la tabla 3.2.

fo : @ ExpΣ′ −→ TermΣ′

fo(X) = X X ∈ Vfo(num) = num (numeros)fo((e1, ..., em)) = (fo(e1), ..., fo(en)) (tuplas)fo(c e1...em) = c(fo(e1), ..., fo(em) c ∈ DC ′

fo(f e1...em) = f(fo(e1), ..., fo(em) f ∈ FS′

Cuadro 3.2: Transformacion de la notacion currificada

Componiendo @ con fo tenemos que fo · @ : ExpΣ −→ TermΣ′ es una traduccion aprimer orden de ExpΣ. El conjunto TermΣ′ es el conjunto terminos de primer orden sobreΣ′.

La funcion map con la que iniciamos la seccion traducida a primer orden queda de estemodo:

map(F, []) = []map(F, [X|Xs]) = [apply(F, X)|map(F,Xs)]

Estudiemos ahora la funcion apply.

3.5.1. Reglas de apply

La funcion funcion apply existe en el sistema, pero ni su definicion ni su tipo, sonaccesibles para el usuario (no esta declarada como primitiva). De hecho el usuario no

Page 69: Un lenguaje lógico funcional con restricciones1

3.5. ORDEN SUPERIOR 69

podra hacer uso de ella directamente; no se puede por ejemplo, intentar evaluar unaexpresion como apply add1 zero. No obstante, esto no limita el poder expresivo en modoalguno; la expresion anterior es equivalente a add1 zero. El hecho de que apply sea unafuncion tan especial se debe a que no admite tipo en nuestro sistema de tipos1. Esto quieredecir que no es una funcion T OY en sentido estricto, pero no que su incorporacion puedaabrir agujeros en el sistema de tipos, sino que debe ser tratada de forma especial. Por esose trata a bajo nivel y no es visible al usuario. Por lo demas, apply puede traducirse comouna funcion mas como se vera en 3.10.6.

Como curiosidad, aunque el usuario no tiene acceso directo a la funcion interna applynada impide que la defina el, incluso con el mismo nombre:

apply :: (A -> B) -> A -> Bapply F X = F X

El sistema se encargara de evitar las colisiones de nombres y ahora el usuario dispone desu propio apply con el tipo correspondiente. No debe sorprender que en la traduccion deesta funcion T OY utilice su apply interno. En lo que sigue nos referimos al apply de bajonivel de T OY y no al que se acaba de definir.

Las reglas de apply no son fijas en el sistema, sino que dependen de las funciones yconstructoras que contenga el programa fuente de usuario. Por lo tanto estas reglas segeneran en tiempo de compilacion del modo siguiente:

Para cada constructora c ∈ DCn se generan n reglas de apply correspondientes atodas las posibles aplicaciones parciales y totales de la misma.

Para cada funcion f ∈ FSn se generan n reglas de apply, donde n− 1 correspondena las aplicaciones parciales y una a la aplicacion total.

apply(ck(X1, ..., Xk), Xk+1) = ck+1(X1, ..., Xk, Xk+1) c ∈ DCn, k + 1 < napply(cn−1(X1, ..., Xn−1), Xn) = c(X1, ..., Xn) c ∈ DCn

apply(fk(X1, ..., Xk), Xk+1) = fk+1(X1, ..., Xk, Xk+1) f ∈ FSn, k + 1 < napply(fn−1(X1, ..., Xn−1), Xn) = f(X1, ..., Xn−1, Xn) f ∈ FSn

Cuadro 3.3: Reglas para apply

En la tabla 3.3 se encuentran, en notacion de primer orden, las reglas de apply pro-ducidas para un programa sobre una signatura determinada Σ = DC ∪ FS. Observeseque estas reglas utilizan sımbolos de constructora de la signatura extendida Σ′. La ultimaregla corresponde a la aplicacion de la constructora fn−1 a un argumento, siendo f unafuncion de aridad n. En este caso el resultado es una aplicacion (total) de la funcion f .Observese que en las reglas de apply no hay ninguna aplicacion parcial, todas son totales.Este es un conjunto de reglas de primer orden que, junto con la transformacion a primerorden del apartado anterior, nos permiten expresar cualquier programa T OY en notacionde primer orden.

Si tenemos un programa con la declaracion de naturales:

data nat = zero | suc nat

1No admite tipo en el sistema de tipos de Hindley-Milner.

Page 70: Un lenguaje lógico funcional con restricciones1

70 CAPITULO 3. MECANISMO DE COMPUTO

y la (ya conocida) funcion map, las reglas para apply producidas son las siguientes:

apply(suc0, X) = suc(X)apply(map0, F ) = map1(F )apply(map1(F ), X) = map(F,X)

Notese que la primera regla produce como resultado la constructora map1(F ) parala que se utiliza el sımbolo de constructora map1, mientras que la segunda devuelve unallamada a la funcion map (de aridad 2). Esta distincion sera fundamental en la traduccionque se hara de la funcion apply en 3.10.6.

Con las reglas primera y tercera del ejemplo anterior es sencillo ver que apply noadmite tipo. La primera de ellas devuelve suc(X) que es de tipo nat, mientras que laultima devuelve map(F, X) cuyo tipo sera de la forma [ ]. Ambos tipos son incompatibles.

Una observacion importante es que se generan las reglas para apply correspondientes atodas las constructoras, funciones primitivas y funciones definidas por el usuario, pero nolas del propio apply. Estas reglas no seran necesarias puesto que, como se vio, el usuariono puede hacer uso directamente de la funcion apply y en la transformacion nunca seproducira una llamada a apply que utilice apply en alguno de sus argumentos.

3.5.2. Informacion sobre constructoras y funciones

En tiempo de ejecucion sera necesario conocer la aridad y los tipos de los distintossımbolos de constructora y funcion. Por ejemplo, cuando se lanza un objetivo, para pro-cesarlo es necesario conocer el significado de cada sımbolo ası como los tipos asociados,para verificar la consistencia (sintactica y de tipos) del mismo. Por este motivo durante elproceso de traduccion, T OY genera toda esta informacion que formara parte del codigoobjeto producido.

Para cada sımbolo de constructora del programa de usuario (no de la signatura exten-dida) se genera una clausula de la forma:

const(< Nombre >,< Aridad >, < Tipo >,< Tipo destino >).

donde < Nombre > es el nombre de la constructora, < Aridad > representa la aridad dela aplicacion total de la misma, < Tipo > es el tipo que tiene asociado y el argumento< Tipo destino > es el tipo de la constructora aplicada totalmente. Este ultimo tipo esdeducible de < Tipo > y se incluye expresamente por motivos de eficiencia. Por ejemplo,para la constructora de naturales suc se genera la clausula:

const(suc, 1, ->(nat, nat), nat)

Observese que para escribir el tipo se utiliza el mismo operador -> que en T OY y siempreen forma prefija. Esta clausula representa realmente dos constructoras (de la signaturaextendida): suc0 y suc. En general, si la aridad es n, la clausula representara n + 1 cons-tructoras como veremos en breve, al estudiar el predicado constructor.

De forma analoga, para cada sımbolo de funcion del programa de usuario se generauna clausula (que representara todas las constructoras correspondientes a las aplicacionesparciales de la misma, ası como la funcion correspondiente a la aplicacion total). Estaclausula es de la forma:

funct(< Nombre >,< Aridad de programa >,< Aridad del tipo >, < Tipo >,< Tipo destino >).

Page 71: Un lenguaje lógico funcional con restricciones1

3.5. ORDEN SUPERIOR 71

cuyos argumentos tienen un significado similar a los de las clausulas const. Por ejemplo,para la funcion map se genera la clausula:

funct(map, 2, 2, ->(->(A, B), ->(:(A, []), :(B, []))), :(B, [])).

Para los tipos predefinidos del sistema se generan tambien las clausulas correspondien-tes a sus constructoras. Las siguientes clausulas aparecen siempre en la traduccion:

const(’,’, 2, ->(A, ->(B, ’,’(A, B))), ’,’(A, B)).const(tup, 1, ->(’,’(A, B),

tuple(’,’(A, B))), tuple(’,’(A, B))).const([], 0, :(A, []), :(A, [])).const(:, 2, ->(A, ->(:(A, []), :(A, []))), :(A, [])).const(char, 1, ->(A, char), char).const(true, 0, bool, bool).const(false, 0, bool, bool).

De manera analoga, para las funciones primitivas se generan las clausulas apropiadas.Las siguientes, son un ejemplo (hay muchas mas):

funct(+, 2, 2, ->(num(A), ->(num(A), num(A))), num(A)).funct(<, 2, 2, ->(num(A), ->(num(A), bool)), bool).funct(==, 2, 2, ->(A, ->(A, bool)), bool).funct(flip, 3, 3, ->(->(A, ->(B, C)), ->(B, ->(A, C))), C).

La propia funcion apply produce la siguiente clausula:

funct(apply, 2, 2, ->(->(->(A, B), A), B), B).

Para el procesamiento sintactico de los objetivos y la salida de respuestas, el sistematambien necesita conocer el nombre de los operadores infijos, ası como su asociatividady su precedencia. Esta informacion se genera en la forma de clausulas infix. Para losoperadores infijos predefinidos se generan las siguientes clausulas (que se completaran conlos definidos por el usuario en caso de existir):

infix(^, noasoc, 90).infix(**, noasoc, 90).infix(/, noasoc, 80).infix(*, left, 80).infix(+, left, 70).infix(-, left, 70).infix(<, noasoc, 50).infix(<=, noasoc, 50).infix(>, noasoc, 50).infix(>=, noasoc, 50).infix(==, noasoc, 20).infix(/=, noasoc, 20).

Con frecuencia sera necesario hacer un test en tiempo de ejecucion sobre la categorıasintactica a la que pertenece una determinada expresion del lenguaje: si es variable, terminoque comienza por constructora o llamada a funcion. En el primer caso el predicado var dearidad 1, standard en Prolog, es suficiente y el ultimo caso no plantea problemas porque

Page 72: Un lenguaje lógico funcional con restricciones1

72 CAPITULO 3. MECANISMO DE COMPUTO

todas las llamadas a funcion sobre las que se hara este test apareceran en forma suspendida,que es facilmente reconocible como veremos en 3.6. El segundo caso podrıa resolverse porexclusion, sin embargo, esto no serıa lo mas eficiente. Ademas, por regla general, en el casode un termino construido nos interesara conocer el sımbolo de constructora de la raız (elfunctor principal), su aridad y sus argumentos. Por este motivo en T OY se incluyen dospredicados especıficos para este test, definidos en la tabla 3.4.

constructor(C,C/0):-number(C),!.

constructor(T,C/N):-functor(T,C,N),!,(

const(C, , , ),!;

funct(C,Ar, , , ),!,N<Ar).

constructor(C,C/0,[]):-number(C),!.

constructor(T,C/N,Args):-functor(T,C,N),!,(

const(C, , , ),!;

funct(C,Ar, , , ),!,N<Ar),T=..[ | Args].

Cuadro 3.4: Chequeo de terminos construidos

Ambos predicados se llaman constructor, pero uno es de aridad 2 y el otro de aridad3. El primero comprueba que el functor principal es un sımbolo de constructora, o bien,un sımbolo de funcion aplicado parcialmente (que corresponde a una de las constructorasnuevas introducidas en la signatura extendida, 3.5). En ambos casos devuelve el nombredel functor y aridad con la que aparece en la forma N/A. El segundo realiza la misma tarea,pero ademas, en el tercer argumento devuelve la lista de argumentos correspondiente. Esclaro que el primer predicado “esta contenido” en el segundo, ya que este devuelve masinformacion. Sin embargo, la lista de argumentos que devuelve el segundo esta construidapor descomposicion (= ..), que es una operacion costosa en Prolog y esta lista no es siemprenecesaria. Por razones de eficiencia se mantienen ambas versiones.

3.6. El sharing o comparticion de variables

En general, el sharing es un mecanismo que pretende evitar la reevaluacion de lasexpresiones que se pasan como argumentos a las funciones. No obstante, en T OY elsharing es imprescindible si se adopta una semantica por call-time choice (como esnuestro caso) debido al uso de funciones indeterministas. En 2.3.7 se justifico esta necesidadcon un ejemplo (funciones coin y double).

En cuanto al uso del sharing para evitar reevaluaciones, consideremos la funcion doubledefinida en 2.3.7 por la regla double X = X+X, y supongamos que queremos evaluar double3*4. Si simplemente hacemos la sustitucion de X por 3∗4 al aplicar la regla de la funcion,

Page 73: Un lenguaje lógico funcional con restricciones1

3.7. EL CODIGO INTERMEDIO 73

la evaluacion se reducirıa a la expresion (3∗4)+(3∗4). Y ahora, para evaluar esta expresiones necesario evaluar la expresion 3 ∗ 4 dos veces.

Esta reevaluacion podrıa evitarse evaluando primero los argumentos de la llamada yluego el cuerpo de la funcion. En el ejemplo anterior efectivamente el problema quedaresuelto: si en la llamada double 3*4 primero reducimos 3 ∗ 4 a 12 y despues evaluamosel cuerpo de la funcion con el resultado 12, la expresion que resulta es 12 + 12, en la queno hay nada que se reevalue. Sin embargo, esta solucion destruye la pereza del lenguaje:supongamos la funcion tres definida por la regla tres X = 3 y la llamada tres 5*4. Clara-mente en este ejemplo no es necesario evaluar el argumento 5 ∗ 4 para evaluar la llamada.En un lenguaje perezoso la llamada tres 5*4 deberıa reducirse directamente a 3 sin evaluarel argumento 5 ∗ 4.

La solucion a este dilema consiste en pasar las expresiones sin evaluar a las funcionescomo en el primer ejemplo de double, pero de modo que cuando se reduzca una expresionel resultado pase a todas las apariciones de es expresion. En los lenguajes funcionalesesto se consigue mediante grafos de evaluacion. En T OY la implementacion del sharingesta basada en la tecnica de las suspensiones ([Che90]) para representar llamadas a funcion,y que se apoya a su vez, en el uso de variables logicas. Una suspension es un termino Prologque representa una llamada a funcion. Tiene la forma

susp(Fun,Args,R,S)

donde Fun es el nombre de la funcion, Args es la lista de argumentos de la misma, R esel resultado de la evaluacion (si ha sido evaluada, variable en otro caso) y S es un flag queindica si la llamada ha sido evaluada (S = hnf) o no (S variable). La evaluacion de unafuncion puede dar como resultado una variable, por lo que el hecho de que R sea variableno implica que la evaluacion no se haya realizado (ni lo contrario); de ahı la necesidaddel flag S. Todas las llamadas a funciones que aparecen en argumentos de alguna funcionaparecen en forma suspendida, puesto que son susceptibles de tener multiples ocurrenciasen el cuerpo o las restricciones de esta segunda funcion.

En el ejemplo anterior, el argumento que le pasamos a double es susp(∗, [3, 4], R, S)con R y S variables nuevas. La expresion a evaluar queda entonces susp(∗, [3, 4], R, S) +susp(∗, [3, 4], R, S). Para evaluar una suma es necesario reducir sus argumentos. Al evaluarel primero de ellos, que es la primera suspension, resulta susp(∗, [3, 4], 12, hnf), es decir,las variables R y S se instancian y la expresion original queda susp(∗, [3, 4], 12, hnf) +susp(∗, [3, 4], 12, hnf). El segundo argumento ha quedado reducido automaticamente sinevaluarlo expresamente y ahora la suma tiene sus dos argumentos evaluados y puedereducirse a 24.

3.7. El codigo intermedio

En 3.5 detallamos el mecanismo para transformar un programa T OY a sintaxis deprimer orden. El codigo intermedio es basicamente el resultado de introducir formas sus-pendidas sobre esta transformacion. En T OY todas las suspensiones se generan en tiempode compilacion y aparecen de forma explıcita en la traduccion final.

Para introducir suspensiones en las funciones partimos de sintaxis general de una reglade funcion traducida a primer orden :

f(t1, ..., tn) = e <== e13e′1, ..., em3e′m

Page 74: Un lenguaje lógico funcional con restricciones1

74 CAPITULO 3. MECANISMO DE COMPUTO

donde f ∈ FS′ y t1, ..., tn, e, e1, e′1, ..., em, e′m ∈ Term′

Σ .Sobre estas reglas transformadas T OY incorpora las suspensiones en todas las lla-

madas a funcion que aparecen en el cuerpo o las restricciones ([Che90, LLR93]), ya quepotencialmente pueden aparecer a su vez en otra llamada a funcion. En la tabla 3.5 sedefine la funcion sf , que introduce las suspensiones sobre los terminos de primer orden.Utilizando esta funcion definimos sfr que introduce suspensiones en los lados derechos yrestricciones de las reglas de programa (tambien en primer orden). En tiempo de ejecucionel sistema incorpora mecanismos de evaluacion o activacion de estas suspensiones.

Forma suspendida de los terminos de primer orden:sf(X) = X X ∈ Vsf(num) = num (numeros)sf((e1, ..., en)) = (sf(e1), ..., sf(en)) (tuplas)sf(c(e1, ..., en)) = c(sf(e1), ..., sf(en)) c ∈ DC ′

sf(f(e1, ..., en)) = susp(f, [sf(e1), ..., sf(en), R, S]) f ∈ FS′

Forma suspendida de las reglas de programa:

sfr(f(t1, ..., tn) = e <== e13e′1, ..., em3e′m) ≡f(t1, ..., tn) = sf(e) <== sf(e1)3sf(e′1), ..., sf(em)3sf(e′m)

Cuadro 3.5: Introduccion de suspensiones

Por ejemplo, el codigo intermedio producido para las reglas de la funcion map (3.5),es el siguiente:

map(F, [ ]) = [ ]map(F, [X|Xs]) = [susp(apply, [F, X], R, S) | susp(map, [F, Xs], R′, S′]

El proceso de traduccion (3.10) trabajara sobre este codigo intermedio para producirel codigo Prolog correspondiente a la traduccion.

3.8. Las restricciones de desigualdad estricta

Las variables T OY son variables logicas pero no son exactamente iguales que las va-riables Prolog, fundamentalmente porque sobre una variable Prolog no se pueden imponerrestricciones de desigualdad estricta, mientras que sobre las de T OY sı. En Prolog existeel predicado \== de aridad 2 que examina la desigualdad de dos terminos, pero no imponela restriccion de que sean distintos. Por ejemplo, el siguiente objetivo tiene exito en PrologX \== Y, X = Y . La primera parte del objetivo chequea que la variables X e Y no son elmismo objeto sintactico y tiene exito; la segunda parte simplemente unifica las variables.En T OY el objetivo analogo X /= Y, X == Y falla porque la desigualdad /= impone larestriccion de que X e Y sean distintas y la segunda parte intenta resolver una restriccionde igualdad sobre ellas2. Por otro lado, nuestro lenguaje admite funciones, cuya evaluacionpuede ser necesaria para resolver una desigualdad. Por ejemplo, la desigualdad 3 /= 1 + 2

2En el sistema Sicstus Prolog existe el predicado dif de aridad 2 que aproxima nuestra semantica dela desigualdad. Existe una version experimental de T OY que lo utiliza, pero no forma parte de la versionfinal porque nuestra nocion de respuesta para un objetivo se ve afectada; en concreto altera nuestra nocionde desigualdad en forma resuelta (en breve veremos lo que son las formas resueltas). Por otra parte, elpredicado dif no es un predicado standard en los sistemas Prolog.

Page 75: Un lenguaje lógico funcional con restricciones1

3.8. LAS RESTRICCIONES DE DESIGUALDAD ESTRICTA 75

falla porque la llamada a la funcion (primitiva) ‘+’ se evalua a 3, mientras que en Prologla desigualdad 3 \ == 1+2 tiene exito porque los miembros son sintacticamente distintos.

Ası pues, aunque las variables T OY se representen como variables Prolog, la existenciade desigualdades enriquece el significado de variable logica en T OY. Esta “informacionextra” exige unas labores de mantenimiento. En particular, el sistema tiene que ser capazde almacenar restricciones. Realmente, con nuestra semantica para las desigualdades, solosera necesario almacenar las formas resueltas que tienen el aspecto X /= t, donde X es unavariable y t es una forma normal (variable o termino construido sin llamadas a funcion).El resto de desigualdades se procesan hasta obtener formas resueltas.

Sobre el modo de almacenamiento hay varias alternativas, como la que implementaBABLOG ([AG94, AGL94]). La idea es que una variable X sin restricciones se representacomo la variable Prolog X; cuando se anade la restriccion X /= t1, X se unifica con untermino de la forma neq(RX, [t1|L]), donde la variable nueva RX representa la variable Xy el segundo argumento es una lista abierta que almacena las desigualdades asociadas aX. Esta lista abierta permite anadir nuevas restricciones. Por ejemplo, con la restriccionX /= t2 tendrıamos neq(RX, [t1, t2|L′]) (se unifica L con [t2|L′]). Este metodo tiene laventaja de que la informacion sobre las restricciones de una variable se localiza junto conla propia variable. Pero si tenemos una restriccion como X /= Y el termino al que se ligaX serıa de la forma neq(RX, [Y |L]) y el de Y serıa neq(RY, [X|L′]). Pero entonces, enneq(RX, [Y |L]) en lugar de Y tendriamos su representacion neq(RY, [X|L′]) que incluyea la propia variable X. El resultado es una ligadura cıclica (en Prolog se admiten por laausencia del occurs-check) que complica notablemente el manejo de las desigualdades.

Las versiones distribuidas de T OY utilizan un almacen explıcito para mantener lasdesigualdades. Este almacen consiste en una lista de la forma

[X : [t1, ..., tn], Y : [s1, ..., sm], ...]

que representa las desigualdades X /= t1, ..., X /= tn, Y /= s1, ..., Y /= sm, siendo t1, ..., tn,s1, ..., sn formas normales. Utilizando esta representacion la unificacion de dos variablesT OY X,Y con conjuntos de restricciones CX , CY solo es posible si sus restricciones dedesigualdad lo permiten, en cuyo caso se hara la unificacion Prolog X = Y que las convierteen una misma variable, a la que asociaremos el conjunto de restricciones CX ∪CY . Si en elalmacen existiese la restriccion X /= Y , por ejemplo, si tuviese la forma [X : [Y ], Y : [X]],la unificacion no serıa posible. Observese que, como variables Prolog, esta unificacion sı quees posible y es T OY el que debe examinar las restricciones para impedir que se lleve acabo. En el siguiente apartado se detallara la operacion de unificacion. En lo sucesivohablaremos de unificacion T OY para la referirnos a la unificacion de varia-bles T OY (que tiene en cuenta las restricciones de desigualdad). Para la unificacion devariables Prolog diremos simplemente unificacion o ligadura.

Veamos mediante un ejemplo el funcionamiento de los almacenes:

Ejemplo:Supongamos que queremos resolver el objetivo:

X /= Y, Y /= Z︸ ︷︷ ︸(1)

, Y == suc zero︸ ︷︷ ︸(2)

, X == Z︸ ︷︷ ︸(3)

Tras evaluar (1), en el almacen de desigualdades tendremos:

[X : [Y ], Y : [X, Z], Z : [Y ]]

Page 76: Un lenguaje lógico funcional con restricciones1

76 CAPITULO 3. MECANISMO DE COMPUTO

La informacion del almacen no provoca fallo en la resolucion de (2) que produce:

[X : [suc zero], Z : [suc zero]]

Y por ultimo al evaluar (3) obtenemos en el almacen:

[X : [suc zero, suc zero]]

(se unen los conjuntos (listas) de desigualdades asociadas a X e Y ).La respuesta que produce el sistema es:

X == Z, Y == suc zero, {X /= suc zero}que es la que cabrıa esperar. ¥

Como se puede apreciar esta representacion almacena para cada variable todas lasrestricciones que le afectan, lo que implica cierta redundancia en desigualdades entre va-riables (X /= Y se almacena como [X : [Y ], Y : [X]]). Con esta redundancia, sin embargo,se mejora notablemente la eficiencia en el acceso a las desigualdades asociadas a unavariable. Y por otro lado, el usuario nunca advertira estas repeticiones, puesto que elproceso de salida de respuestas se encarga de minimizar la informacion en las respuestas,y en particular elimina la redundancia (vease el apendice I).

Para mantener el almacen de desigualdades, los predicados de la traduccion contienendos argumentos extra que notaremos por: Cin (constraints in) y Cout (constraints out),que representan el almacen de restricciones entrada y el de salida respectivamente. Conesta representacion las restricciones asociadas a una variable no se encuentran junto conla misma variable, sino en la estructura almacen. Sin embargo, la ventaja de este metodoes que no se necesita desreferenciaciacion porque las variables T OY se representan direc-tamente mediante variables Prolog y esto supone, en general, una ganancia en la eficienciadel sistema, como se ha probado experimentalmente.

3.8.1. Gestion de las desigualdades

El mantenimiento de las desigualdades requiere algunas operaciones, que han sidopensadas como interface de comunicacion con el almacen para conseguir un cierto nivelde ocultamiento y de independencia de la estructura concreta del mismo. Este interfaceconsta de tres operaciones:

anadir una restriccion,

extraer las restricciones asociadas a una variable y

unificar dos variables.

Con estas operaciones la estructura del almacen es transparente al resto del codigo,ya que solo ellas acceden directamente a la representacion concreta del mismo. Por estemotivo son ellas las encargadas de realizar de forma implıcita otra labor: mantener laconsistencia de las desigualdades. Por ejemplo, si X es una variable que tiene asociada larestriccion X /= [ ], la operacion de unificacion debe fallar al intentar unificar X con [ ]y entre las desigualdades asociadas a una variable X no debe encontrarse la desigualdadX /= X.

La especificacion de las operaciones de insercion y extraccion es la siguiente:

Page 77: Un lenguaje lógico funcional con restricciones1

3.8. LAS RESTRICCIONES DE DESIGUALDAD ESTRICTA 77

addCtr(X, Term,Cin, Cout) ⇔ Cout es el almacen resultante de anadir la desigualdadX /= Term al almacen de entrada Cin, donde X es una variable y Term una forma normal.

extractCtr(X, Cin, Cout, CX) ⇔ CX es la lista de restricciones asociadas a la variableX en el almacen Cin, y Cout es el almacen resultante de eliminar X : CX de Cin.

El codigo se muestra en la tabla 3.6.

addCtr(X,Term,[ ],[X:[Term]]):- !.

addCtr(X,Term,[Y:CY|RCin],[Y:[Term|CY]|RCin]):- X==Y,!.

addCtr(X,Term,[C|RCin],[C|RCout]):- addCtr(X,Term,RCin,RCout).

extractCtr( ,[ ],[ ],[ ]):- !.

extractCtr(X,[Y:CY|R],R,CY):- X==Y,!.

extractCtr(X,[Y:CY|RCin],[Y:CY|RCout],CX):- extractCtr(X,RCin,RCout,CX).

Cuadro 3.6: Insercion y extraccion de desigualdades

La unificacion de variables es algo mas compleja. La especificacion es:

unifyV ar(X, Y, Cin,Cout) ⇔ la restriccion X /= Y no esta presente en Cin, liga Xcon Y convirtiendolas en la misma variable a la que asociamos en Cout la union de lasdesigualdades asociadas a las antiguas X e Y .

unifyVar(X,Y,Cin,Cout):-X==Y,!,Cout=Cin.

unifyVar(X,Y,Cin,Cout):-extractTwoCtr(X,Y,Cin,Cout1,CX,CY),X=Y,!,update(X,CX,CY,Cout1,Cout).

Cuadro 3.7: Unificacion de variables

En la tabla 3.7 se muestra el codigo del predicado unifyV ar. En la primera clausula,si las variables a unificar son la misma no hay nada que hacer excepto devolver intactaslas desigualdades de entrada. El caso interesante es el que se trata en la segunda clausula.Aquı se utilizan los predicados auxiliares extractTwo y update cuyo codigo aparece en latabla 3.8. El primero hace la extraccion simultanea (en un solo recorrido de la lista) de lasrestricciones asociadas a dos variables X e Y . Serıa igualmente correcto utilizar dos veces elpredicado extractCtr, pero menos eficiente. update genera la nueva lista de desigualdadesasociada a una variable X que se ha ligado con otra Y . Esta lista es el resultado de unirlas desigualdades de X con las de Y (concatenacion de listas), pero ademas en el recorridose comprueba que no existıa la restriccion X /= Y , que tras la ligadura X = Y aparecerıacomo X /= X. El orden de los predicados en el cuerpo de la segunda clausula de unifyV ares especialmente crıtico para que todo funcione correctamente.

Las operaciones de manejo de los almacenes de desigualdades que acabamos de exponermantienen la consistencia en el sentido que se indicaba al principio de este apartado. Sonplanteables algunas ideas mas en cuanto a consistencia de restricciones. Supongamos, porejemplo, el siguiente tipo de datos:

Page 78: Un lenguaje lógico funcional con restricciones1

78 CAPITULO 3. MECANISMO DE COMPUTO

extractTwoCtr( , ,[ ],[ ],[ ],[ ]).

extractTwoCtr(X,Y,[Z:CZ|R],Cout,CX,CY):-(

X==Z,!,CX=CZ,extractCtr(Y,R,Cout,CY);

Y==Z,!,CY=CZ,extractCtr(X,R,Cout,CX)).

extractTwoCtr(X,Y,[Ctr|R],[Ctr|R1],CX,CY):-extractTwoCtr(X,Y,R,R1,CX,CY).

update(Y,[ ],CY,Cin,Cout):- insertCtrs(Y,CY,Cin,Cout).

update(Y,[T|Ts],CY,Cin,Cout):-Y \ ==T,!,update(Y,Ts,[T|CY],Cin,Cout).

insertCtrs( ,[ ],Cin,Cin):-!.

insertCtrs(Y,CY,Cin,[Y:CY|Cin]).

Cuadro 3.8: Extraccion simultanea de restricciones y union de restricciones

data colour = red | green | blue

El objetivo X /= red, X /= green tiene exito y en la respuesta muestra como restric-ciones X /= red y X /= green. Esta respuesta es correcta, pero intuitivamente “el sistemapodrıa llegar mas lejos” y devolver como respuesta X == blue. El razonamiento infor-malmente es sencillo: X es una variable del tipo colour (esto lo deduce el inferidor), porlo que puede tomar los valores red, green o blue (y solo estos); como ademas no es red nigreen, tiene que ser blue.

Otro ejemplo mas llamativo puede ser el objetivo X /= red, X /= green, X /= blue parael que T OY obtiene un exito devolviendo como como restricciones las mismas tres que se leplantean. Por un razonamiento similar al anterior, este objetivo deberıa provocar un fallo.Lo que tiene de particular este tipo de datos es que es finito, es decir, hay un numero finitode valores de este tipo (tres en este caso): representa un dominio finito. Las restriccionesde dominio finito estan ampliamente estudiadas y son las que ofrecen una justificacionformal a los argumentos intuitivos de consistencia que veıamos ([V89, FHK+93, Car95]).Sin embargo, T OY no incorpora restricciones de dominio finito3, por lo que engeneral el tratamiento de la desigualdad en T OY solo es adecuado para dominios infinitos.Para dominios finitos puede ser incluso incorrecto4.

La excepcion a este tratamiento de la desigualdad en el caso de dominios finitos es eltipo de los booleanos para el que T OY, debido a lo frecuente de su uso, da un tratamientoespecial. Este es tambien un tipo finito con dos valores: true y false. Las restricciones eneste caso son parcialmente tratadas como restricciones de dominio finito. Ası por ejemplo,el objetivo X /= true produce la respuesta X == false, y el objetivo X /= true, X /=false produce un fallo5. El sistema incorpora codigo especıfico para el tratamiento de este

3Existen versiones no distribuidas del sistema, en las que se han incluido restricciones de dominio finitode forma experimental, pero por el momento no forman parte de la version definitiva.

4Por ejemplo, dado el predicado p : − X /= red, X /= blue, X /= green, el objetivo p devuelve exitosin mas (sin restricciones), lo cual es incorrecto.

5Desde el punto de vista logico este modo de operar corresponde al principio del tercio excluido.

Page 79: Un lenguaje lógico funcional con restricciones1

3.9. COMPUTO DE FORMAS NORMALES DE CABEZA (HNF) 79

tipo de restricciones que veremos en 3.12.

3.9. Computo de formas normales de cabeza (hnf)

Una forma normal de cabeza (f.n.c., en lo sucesivo) es cualquier expresion del lenguajeque no sea una llamada a funcion, es decir, una variable o una expresion que comienza porconstructora (aunque sus argumentos contengan llamadas a funcion). En las respuestasa un objetivo, T OY presenta formas normales (nunca aparece una llamada a funcionsin evaluar). Sin embargo T OY no incorpora ninguna operacion explıcita de reduccion aforma normal, sino que dichas formas se consiguen mediante sucesivas reducciones a f.n.c.de las expresiones, los argumentos de las expresiones, los argumentos de los argumentos,etc. El mecanismo operacional del sistema hace que estas reducciones se realizan a medidaque avanza el computo y solo cuando son necesarias (en general es posible evaluar unallamada a funcion sin evaluar completamente sus argumentos). Y esta es la idea de lapereza, que esta estrechamente relacionada con el computo de formas normales de cabeza.

La reduccion a f.n.c. es una de las operaciones fundamentales y mas frecuentes querealiza el sistema. Es similar a la operacion de reduccion de los lenguajes funcionalespuros. De hecho, las diferencias vienen dadas por la reversibilidad de las funciones enprogramacion logico-funcional, el indeterminismo y las desigualdades de nuestro sistema.En un programa que no haga uso de las restricciones y en el que las funciones seandeterministas la evaluacion a f.n.c. tiene un comportamiento funcional.

La especificacion del predicado hnf (head normal form) es la siguiente:

hnf(E, H, Cin,Cout) ⇔ H es el resultado de evaluar una forma normal de cabeza paraE tomando como restricciones de entrada Cin. Cout son las restricciones resultantes de estecomputo. Hay una condicion adicional en el modo de uso: H es una variable nueva o untermino de la forma c(X1, ..., Xn) con X1, ..., Xn variables nuevas (lo importante es que lasvariables de H no tengan asociadas desigualdades y que el propio H sea una forma normal decabeza).

A diferencia de los lenguajes funcionales en los que la reduccion de una expresion af.n.c. produce un resultado, en nuestro lenguaje esta reduccion es indeterminista y puede,en general, producir mas de un resultado. En concreto, al admitir definiciones de funcionesindeterministas, una llamada a una de estas funciones puede tener mas de una reduccionposible a f.n.c.. Los distintos resultados se iran calculando por backtracking.

hnf esta definido para los tres tipos de objetos sintacticos que manejamos: variables,terminos construidos (con un sımbolo de constructora en la raız) y funciones (que seransiempre suspensiones). Las variables y los terminos construidos son f.n.c.’s (no hay quehacer nada con ellos); la reduccion a f.n.c. de una funcion corresponde a la evaluacion dela misma (debido a la pereza la evaluacion de una funcion no devuelve una forma normal,sino una f.n.c.).

En la tabla 3.9 se muestra el codigo para la evaluacion de f.n.c.’s, que realmente es mascomplicado de lo que cabrıa esperar a la vista de la especificacion. En algunas ocasiones,como al resolver determinadas igualdades, es util hacer una reduccion a f.n.c. “predictiva”o “acotada”: solicitamos la evaluacion a f.n.c. de una expresion sabiendo que el resultadodebe tener una forma determinada c(...) (c sımbolo de constructora), por lo que se orienta(instancia) el resultado a dicha forma. En el apartado de igualdad se abordara este puntoen detalle. Por el momento basta con tener presente que el resultado H de la reducciona f.n.c. puede venir instanciado con una expresion de la forma c(X1, ..., Xn), siendo c

Page 80: Un lenguaje lógico funcional con restricciones1

80 CAPITULO 3. MECANISMO DE COMPUTO

hnf(E,H,Cin,Cout):-var(E),!,(

var(H),!,H=E,Cin=Cout;

extractCtr(E,Cin,Cout1,CE),H=E,propagate(H,CE,Cout1,Cout)

).

hnf(susp(Fun,Args,R,S),H,Cin,Cout):-!,(

S==hnf,!,hnf(R,H,Cin,Cout);

H=R,S=hnf,hnf susp(Fun,Args,H,Cin,Cout)).

hnf(T,H,Cin,Cin):- H=T.

Cuadro 3.9: Codigo del predicado hnf

constructora de aridad n y X1, ..., Xn variables nuevas. Conviene tener presente que enuna llamada a hnf de la forma hnf(e,H), H puede no ser variable, pero en cualquier casolos argumentos e y H no tienen ninguna variable en comun (no sera necesario haceroccurs-check, 3.11.1).

Las tres clausulas del predicado corresponden a los casos de variable, funcion y terminosque comienzan por sımbolo de constructora respectivamente. En el caso de una variable(primera clausula), su f.n.c. es ella misma (H = E). Si el resultado no viene orientado(primera parte de la disyuncion), se hace la unificacion H = E que convierte H y E enla misma variable y las restricciones de desigualdad de H seran las mismas que tuvieseE; en otro caso, si el resultado esta orientado (segunda parte de la disyuncion), hay queresolver las nuevas desigualdades que se generan. Supongamos, por ejemplo, que X es unavariable sobre la que se que se tienen las restricciones X /= [1], X /= Y y que se tiene quereducir a una f.n.c. de la forma [A]. Evidentemente debemos hacer la ligadura X = [A],pero tambien hay que “propagar” o resolver las desigualdades [A] /= [1], [A] /= Y (queen este caso se transformaran en las formas resueltas A /= 1, Y /= [A]). El predicadopropagate es el encargado de transformar estas desigualdades a forma resuelta. Como seaprecia en el codigo de hnf , lo primero que se hace es extraer del almacen las desigualdadesde E, porque si se hiciese la ligadura H = E antes de la extraccion (H tiene la formac(X1, ..., Xn)) en el almacen quedarıan desigualdades en forma no resuelta y por tanto,habrıa una inconsistencia que no se detectarıa. Despues propagate se encarga de introducirlas formas resueltas que produce la resolucion de las desigualdades generadas.

La especificacion de propagate es la siguiente:

propagate(H,Ls, Cin,Cout) ⇔ Cout es el almacen de desigualdades resultante de resol-ver todas las desigualdades entre H y los elementos de Ls, tomando como almacen de entradaCin.

Veamos como se utiliza la informacion de la especificacion volviendo al ejemplo anterior.Tenıamos la variable X con las restricciones X /= [1], X /= Y , lo que en el almacen derestricciones tendra la forma [X : [[1], Y ], Y : [X]]. Una vez realizada la unificacion Prolog

Page 81: Un lenguaje lógico funcional con restricciones1

3.9. COMPUTO DE FORMAS NORMALES DE CABEZA (HNF) 81

X = [A]6, para completar la unificacion T OY habra que resolver las desigualdades [A] /=[1], [A] /= Y , mediante la llamada propagate([A], [[1], Y ]). La primera de ellas produce laforma resuelta A/= 1 y la segunda Y : [[A]].

propagate( ,[ ],Cin,Cin):-!.

propagate(Y,[C|R],Cin,Cout):-notEqual(Y,C,Cin,Cout1)),propagate(Y,R,Cout1,Cout).

Cuadro 3.10: Codigo del predicado propagate

El codigo para propagate se muestra en la tabla 3.10. La primera clausula recoge elcaso en el que no hay desigualdades que resolver. La segunda resuelve las desigualdadesentre el primer argumento y todos los de la lista mediante el predicado notEqual. Estepredicado resuelve una desigualdad entre dos expresiones cualesquiera como estudiaremosen 3.12.

Sobre este codigo es posible hacer algunas optimizaciones sutiles. Volvamos una vezmas a nuestro ejemplo X /= [1], X /= Y . Veıamos que habıa que resolver las desigualdades[A] /= [1], [A] /= Y . La primera se resuelve y produce A/= 1, pero no es necesario resolverla segunda. En realidad, ya esta resuelta porque cuando se hizo la unificacion X = [A], enel almacen de restricciones para Y quedo Y : [[A]], que ya esta en forma resuelta. En estepunto es fundamental el hecho de que “Ls sean las desigualdades asociadas a la variablecon la que se acaba de unificar H”.

Podemos reemplazar la segunda clausula de propagate para hacer esta optimizacion,tal como aparece en la tabla 3.11.7

propagate( ,[ ],Cin,Cin):-!.

propagate(Y,[C|R],Cin,Cout):-(

var(C),!,Cout1=Cin;

notEqualTerm(Y,C,Cin,Cout1)),propagate(Y,R,Cout1,Cout).

Cuadro 3.11: Optimizacion de propagate

En la primera parte de la disyuncion comprueba si la cabeza de la lista es una va-riable, en cuyo caso la desigualdad correspondiente ya esta en forma resuelta, comoveıamos en el ejemplo. En otro caso, para resolver dicha desigualdad utilizamos el pre-dicado notEqualTerm. Las desigualdades a resolver aquı estan en forma “casi resuelta”,esto es, son desigualdades entre formas normales y sin variables en comun (no es necesarioel occurs-check, 3.11.1). Naturalmente, puede utilizarse el predicado generico notEqual eneste caso, pero la resolucion de dichas desigualdades no necesita ninguna reduccion, ya

6El test de ocurrencia (vease 3.11.1) no es necesario por la propia especificacion de hnf7La especificacion se complica bastante: propagate(H, Ls, Cin, Cout) ⇔ H es una forma normal no

variable, Ls la lista de desigualdades asociadas a una variable que acaba de ligarse a H y tal que H y Lsno tienen ninguna variable en comun; Cout es el almacen de desigualdades resultante de resolver todas lasdesigualdades entre H y los elementos de Ls, tomando como almacen de entrada Cin.

Page 82: Un lenguaje lógico funcional con restricciones1

82 CAPITULO 3. MECANISMO DE COMPUTO

que no hay ninguna llamada a funcion, y esta informacion puede aprovecharse para hacerun computo mas eficiente. Esto es lo que hace notEqualTerm (en 3.12.1 se estudiara estepredicado en detalle).

La segunda clausula para hnf se encarga de calcular una f.n.c. para una llamada afuncion. Segun vimos en 3.7 las llamadas a funcion pueden aparecer en la forma suspendidasusp(Fun, Args, R, S), siendo Fun el nombre de la funcion, Args los argumentos, S unflag que toma el valor hnf si la llamada ya ha sido evaluada previamente y variable encaso contrario, y R es el resultado de la evaluacion en caso de que se haya producido. Loprimero que tiene que hacer hnf es comprobar si la llamada ha sido evaluada, en cuyocaso devolvera el resultado de tal evaluacion. Este resultado esta en R pero no podemosdevolverlo directamente porque H puede no ser variable (ver la especificacion), en cuyocaso habra que propagar desigualdades. Invocando de nuevo a hnf , pero esta vez con elargumento R, la primera clausula se encargara de hacer esta propagacion si es necesaria.

En otro caso (segunda parte de la disyuncion), hay que evaluar la llamada y paraello utilizamos el predicado hnf susp. Este predicado se genera en la traduccion (unaclausula por cada funcion definida) y se encarga de “construir” la llamada a la funciony de invocarla. Las llamadas se construyen con el nombre de la funcion como functorprincipal, los argumentos de la misma, un argumento extra que recoge el resultado de laevaluacion (al que normalmente llamaremos H) y los almacenes de restricciones Cin yCout (el codigo para las funciones se estudiara en 3.10). De acuerdo con lo anterior, porejemplo, para la primitiva ‘+’ se genera la clausula:

hnf_susp(+, [A,B], H, Cin, Cout):-+(A, B, H, Cin, Cout).

que puede leerse como: el resultado de evaluar ‘+’ sobre los argumentos A y B es H sila llamada a la funcion ‘+’ con esos mismos argumentos produce H, siempre teniendo encuenta los almacenes de restricciones.

El predicado hnf susp podrıa suprimirse construyendo la llamada a la funcion corres-pondiente en la segunda clausula de hnf y haciendo dicha llamada con el predicado call8.Sin embargo, se comprobo experimentalmente que la version que hemos presentado tieneuna eficiencia notablemente superior.

En la tercera clausula se trata el caso que falta, la reduccion a f.n.c. de una expresionde la forma c(e1, ..., en) que ya esta en f.n.c. y se reduce a sı misma. Las restricciones nosufren modificaciones, por lo que el almacen de salida se unifica con el de entrada en lacabeza de la clausula. La correccion de esta clausula esta garantizada por la restriccionadicional en el modo de uso que se indicaba en la especificacion de hnf , por la que H esuna variable o un termino plano de la forma c(X1, ..., Xn) siendo X1, ..., Xn variables sindesigualdades asociadas.

3.10. Generacion de codigo para las funciones

El mecanismo operacional de T OY esta basado en narrowing, pero como ya apunta-bamos al principio del capıtulo, los espacios de busqueda generados por este mecanismo

8La llamada a hnf susp se reemplaza por el siguiente codigo: Call = ..[Fun, H, Cin, Cout |Args], call(Call). Notese que se han reordenado los argumentos en la llamada a la funcion (el resultado Hy los almacenes Cin, Cout aparecen al principio) para evitar hacer concatenaciones de listas. Logicamenteen el codigo de las funciones tambien habrıa que reflejar este cambio.

Page 83: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 83

pueden ser muy grandes. Al explorar estos espacios es frecuente ademas que algunas ex-presiones se reevaluen varias veces, lo que empeora aun mas el problema. No es necesarioplantear situaciones muy complejas para que se produzcan varias reevaluaciones de unamisma expresion. Consideremos el siguiente programa (leq es la funcion “menor o igual”y “add” es la suma, ambas para naturales):

data nat = zero | suc nat

leq zero Y = trueleq (suc X) zero = falseleq (suc X) (suc Y) = leq X Y

add zero Y = Yadd (suc X) Y = suc (add X Y)

El computo que describimos a continuacion corresponde a una estrategia ingenua.Supongamos que lanzamos el objetivo

leq (add zero (suc zero)) (add (suc zero) (suc zero)) == B+

Probando con las reglas para leq en el orden textual, por la primera se intenta reducir laexpresion add zero (suc zero) a zero (paso de parametros para el primer argumento).Para ello se evalua la expresion add zero (suc zero) y se obtiene el valor suc zero.Como este valor no encaja con el de la primera regla de leq se produce un fallo. Entoncesse prueba con la segunda regla de leq y add zero (suc zero) vuelve a evaluarse desdeel principio, porque el resultado de la evaluacion anterior se ha perdido. El resultadosuc zero ahora sı encaja con el primer argumento de la segunda regla de leq y se procedea operar con los segundos argumentos.

A continuacion es necesario evaluar la expresion add (suc zero) (suc zero) queproduce suc (suc zero), que no encaja con el segundo argumento de la segunda reglade leq y se produce otro fallo. Los resultados de los computos anteriores se han perdidoy al probar con la tercera regla de leq hay que hacer las evaluaciones desde el principio.Con esta ultima regla se obtiene la respuesta B == true.

En el computo que acabamos de describir la expresion add zero (suc zero) se haevaluado 3 veces y add (suc zero) (suc zero) 2 veces. Pero, ¿son estas reevaluacionesnecesarias realmente?. La respuesta es negativa: observese que todas las reglas de leqtienen una constructora como primer argumento, lo que significa que el primer parametrode cualquier llamada a leq debe reducirse a una de esas constructoras para que el computono produzca fallo. Entonces no estaremos perdiendo nada si hacemos algunos pasos dereduccion sobre el primer argumento, add zero (suc zero), para obtener una f.n.c. antesde aplicar ninguna regla de leq. Esta f.n.c. tendra la forma suc Z. Ahora, la primera reglade leq fallara, pero la f.n.c. que hemos calculado se puede reutilizar para probar con elresto de reglas.

De hecho no es necesario probar la primera regla porque con seguridad producira unfallo (incompatibilidad en el primer argumento). En las dos que quedan, cuyos primerosargumentos encajan con la f.n.c.calculada, vuelve a suceder algo similar: ambas tienenuna constructora como segundo argumento. Procedemos como antes, calculando una f.n.c.para el segundo argumento que tendra la forma suc U, que solo encaja en la tercera regla(la primera ya quedo excluida). De este modo, no se ha reevaluado ninguna expresion yse aplica la tercera regla, que es la unica que tiene exito.

Page 84: Un lenguaje lógico funcional con restricciones1

84 CAPITULO 3. MECANISMO DE COMPUTO

El ultimo computo que hemos descrito es el correspondiente a la Estrategia Guiadapor la Demanda que implementa T OY. Esta estrategia queda reflejada en la traduccion (acodigo Prolog) de las funciones. Para generar el codigo, previamente T OY construye unarbol de decision para cada cada funcion al que llamaremos arbol definicional ([Ant92]).Tanto el algoritmo de construccion de arboles definicionales como el de generacion decodigo estan basados en los que se presentan en [LLR93]. En la generacion se han incor-porado los cambios pertinentes para el tratamiento de desigualdades, ası como algunasoptimizaciones. En la presentacion que se hace aquı, primero se introduce el algoritmo sinconsiderar las desigualdades y las optimizaciones para facilitar la comprension; a conti-nuacion, sobre esta version se realizan los cambios oportunos para llegar a la version finalque utiliza T OY.

Los algoritmos que exponemos a continuacion operan sobre el codigo intermedio quese preciso en 3.7 (transformacion a primer orden + suspensiones).

3.10.1. Preliminares

En este apartado introducimos las nociones fundamentales sobre demanda que guiaranla construccion del arbol definicional.

Sea f una funcion definida en un programa R:

• al conjunto de reglas que definen f o reglas de f lo denotamos con Rf .

• si f tiene aridad de programa n, un patron de llamada para f es cualquierexpresion lineal (sin variables repetidas) de la forma f(t1, ..., tn), siendo t1, ..., tnpatrones (expresiones irreducibles). Un patron generico de llamada para lafuncion f es f(X1, ..., Xn), siendo X1, ..., Xn variables distintas.

Una posicion es una secuencia de enteros positivos de la forma p1 · ... · pm. Unaposicion u en un termino e identifica tanto el subtermino que contiene e en la posicionu, como el sımbolo (de variable, constructora o funcion) que aparece en e en laposicion u. Por ejemplo, si e ≡ f(g(X), c(Y, d)), la posicion 2 en e identifica elsubtermino c(Y, d), ası como el sımbolo c.

Denotamos con V P (e) al conjunto de posiciones de variable de la expresion e. En elejemplo anterior V P (e) = {1 · 1, 2 · 1} (son las posiciones que ocupan X e Y ). Esteconjunto puede construirse inductivamente como:V P (X) = {ε}V P (l(e1, ..., en)) =

⋃i=1..n{i · u | u ∈ V P (ei)}, si l ∈ DCn ∪ FSn

donde ε representa la secuencia vacıa y u · ε = u.

Sea f una funcion definida en el programa R y u una posicion:

• diremos que u es una posicion demandada por una regla f(t1, ..., tn) = t <==C si el lado izquierdo f(t1, ..., tn) contiene una constructora c en la posicion u.

• Diremos que u es una posicion uniformemente demandada por un con-junto de reglas S ⊆ Rf si todas las reglas de S demandan una constructora(posiblemente distinta para cada regla) en la posicion u.

Ejemplo:Supongamos un programa R que contiene la funcion append (2.3.4), cuyas reglas son

(en representacion intermedia):

Page 85: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 85

R1 ≡ append([ ], Y s) = Y sR2 ≡ append([X|Xs], Y s) = [X|susp(append, [Xs, Y s], R, S)]

De acuerdo con las definiciones anteriores tenemos:

Rappend = {R1, R2}el patron generico de llamada es append(X,Y )

V P (append(A,B)) = {1, 2}la posicion 1 es demandada por ambas reglas, ya que ambas tienen una constructoracomo primer argumento, luego esta posicion es uniformemente demandada. ¥

3.10.2. Construccion del arbol definicional

Para una funcion f definida en un programa R el arbol definicional se construye conel el algoritmo que se presenta a continuacion.

La llamada inicial al algoritmo se hace con un patron de llamada generico y conel conjunto de reglas que definen la funcion f y tendra la forma: dt(f(X1, ..., Xn),Rf ),siendo n la aridad de programa de f .

El algoritmo es recursivo y una llamada generica tendra la forma: dt(pat,S), siendopat un patron de llamada y S un subconjunto de reglas de f . Ademas, por la construcciondel algoritmo, cada lado izquierdo de las reglas de S es una instancia de pat.

Algoritmo:Si S = ∅ devolver ∅

En otro caso, aplicar una de las siguientes alternativas (solo una es aplicable)

Alguna posicion en V P (pat) es uniformemente demandada por S.

Sea u la menor de dichas posiciones en el orden lexicografico usual (esta eleccion esarbitraria) y X la variable que aparece en pat en la posicion u.

Sean c1, ..., cm las constructoras demandadas por las reglas de S en la posicion utomadas en orden textual. Hacemos una particion de S de la forma:

Sea S1u el subconjunto de reglas de S que demandan c1 en la posicion u.

...

Sea Smu el subconjunto de reglas de S que demandan cm en la posicion u.

Para cada ci construimos el patron:

pati = pat[X/ci(X1, ..., Xki)]

donde ki es la aridad de ci y X1, ..., Xki son variables frescas.

devolver:

pat− case X ofc1 : dt(pat1,S1

u);c2 : dt(pat2,S2

u);...cm : dt(patm,Sm

u )

Page 86: Un lenguaje lógico funcional con restricciones1

86 CAPITULO 3. MECANISMO DE COMPUTO

Alguna posicion en V P (pat) es demandada, pero ninguna es uniformemen-te demandada.

Sean u1, ..., uk el conjunto de posiciones demandadas tomadas segun el orden textualde las reglas y en orden lexicografico dentro de cada regla9.

Hacer la siguiente particion de S:

Sea Su1 el conjunto de reglas de S que demandan la posicion u1, Q1 = S − Su1 .

Sea Su2 el conjunto de reglas de Q1 que demandan la posicion u2, Q2 = Q1−Su2 .

...

Sea Sukel conjunto de reglas de Qk−1 que demandan la posicion uk.

Y sea S0 el conjunto de reglas de S que no demandan ninguna posicion (esteconjunto puede ser vacıo).

devolver:

pat− ordt(pat,S0);dt(pat,Su1);...dt(pat,Suk

)

Ninguna posicion en V P (pat) es demandada.

Todos los lados izquierdos de las reglas de S deben ser variantes de pat. Entoncespodemos hacer un renombramiento de estas m reglas de foma que los lados izquierdossean identicos a pat. Las reglas (tomadas en orden textual) seran ahora:

pat = ei <== Ci, i = 1..m

devolver:

pat− try〈e1 <== C1

|e2 <== C2

...|em <== Cm〉

¥

En 4.5.2 veremos un algoritmo similar a este para el que demostraremos terminacion.La idea es que en cada llamada recursiva disminuye el numero de reglas que se pasan, obien, el numero de posiciones que contienen un sımbolo de constructora en el patron dellamada se incrementa. Este numero esta acotado por el numero maximo de posiciones deconstructora en las reglas de S.

El codigo Prolog que implementa este algoritmo puede verse en el apendice G.

Ejemplo:Sea R el siguiente programa:

9Esta eleccion es arbitraria, el algoritmo funcionarıa igual con cualquier otro orden

Page 87: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 87

data nat = zero | suc nat

leq :: nat -> nat -> boolleq zero Y = trueleq (suc X) zero = falseleq (suc X) (suc Y) = leq X Y

below :: nat -> [nat] -> boolbelow X [] = truebelow zero Y = truebelow (suc X) [zero | _ ] = falsebelow (suc X) [suc Y | _ ] = false <== leq X Y == falsebelow (suc X) [suc Y | Ys] = below (suc X) Ys <== leq X Y

En este programa se definen el tipo nat de los numeros naturales del modo habitualy dos funciones que utilizan este tipo. La primera, leq (less or equal), segun vimos alprincipio de la seccion, define la operacion “menor o igual”. Y below toma un natural yuna lista de naturales, y devuelve true si el primer numero es menor o igual que todos losde la lista, false en otro caso. Denotaremos con LEQ1, LEQ2 y LEQ3 a las reglas de leqtomadas en orden textual y con BELOW1, ..., BELOW5 a las de below.

La representacion intermedia de ambas funciones es:

leq(zero, Y ) = trueleq(suc(X), zero) = falseleq(suc(X), suc(Y )) = susp(leq, [X,Y ], R, S)

below(X, [ ]) = falsebelow(zero, Y ) = truebelow(suc(X), [zero| ]) = falsebelow(suc(X), [suc(Y )| ]) = false <== susp(leq, [X,Y ], R, S) == falsebelow(suc(X), [suc(Y )|Y s]) = susp(below, [suc(X), Y s], R, S)

<== susp(leq, [X,Y ], R′, S′) == true

(Notese que la restriccion de la ultima regla de below se ha interpretado como unaigualdad). La construccion del arbol definicional para leq de acuerdo con el algoritmoserıa:

Llamada inicial:

dt(leq(A,B), {LEQ1, LEQ2, LEQ3})El patron de llamada inicial es pat ≡ leq(A,B) y V P (pat) = {1, 2}. La posicion 1 es

uniformemente demandada por lo que el algoritmo seleccionara la primera alternativa ytenemos:

dt(leq(A,B),Rleq) = leq(A,B)− case A ofzero : dt(leq(zero, B), {LEQ1})suc(X) : dt(leq(suc(X), B), {LEQ2, LEQ3})

Para la primera llamada recursiva, tenemos el patron pat1 = leq(zero, B) y V P (pat1) ={2}. Como 2 es la unica posicion variable de pat y no es uniformemente demandada seaplica la tercera alternativa del algoritmo y se obtiene:

Page 88: Un lenguaje lógico funcional con restricciones1

88 CAPITULO 3. MECANISMO DE COMPUTO

dt(leq(zero, B), {LEQ1}) = leq(zero, B)− try〈true〉Para la segunda llamada tenemos pat2 = dt(leq(suc(X), B) y V P (pat2) = {1 ·1, 2}. La

posicion 2 es uniformemente demandada por las reglas LEQ2 y LEQ3, luego el algoritmoaplicara de nuevo la primera alternativa:

dt(leq(suc(X), B), {LEQ2, LEQ3}) = leq(suc(X), B)− case B ofzero : dt(leq(suc(X), zero), {LEQ2})suc(Y ) : dt(leq(suc(X), suc(Y )), {LEQ3})

Ahora la primera rama, por la tercera alternativa produce:

dt(leq(suc(X), zero), {LEQ2}) = leq(suc(X), zero)− try 〈false〉y la segunda:

dt(leq(suc(X), suc(Y )), {LEQ3}) = leq(suc(X), suc(Y ))− try 〈susp(leq, [X, Y ], R, S)〉El algoritmo termina produciendo el arbol (uniendo los resultados anteriores):

dt(leq(A,B), {LEQ1, LEQ2, LEQ3}) = leq(A,B)− case A ofzero : leq(zero,B)− try 〈true〉suc(X) : leq(suc(X), B)− case B of

zero : leq(suc(X), zero)− try 〈false〉suc(Y ) : leq(suc(X), suc(Y ))− try 〈susp(leq, [X, Y ], R, S)〉

leq(A,B)

leq(zero,B) leq(suc(X),B)

leq(suc(X),zero) leq(suc(X),suc(Y))

true false

A/zero A/suc(X)

B/zero B/suc(Y)

try

try

case

case

try

susp(leq,[X,Y],R,S)

Figura 3.3: Arbol definicional de leq

La figura 3.3 representa graficamente este arbol. Las ramas case recogen las alternativaso formas que pueden tener los argumentos y las ramas try corresponden a la aplicacionde reglas de funcion. En las ramas try, cuando solo hay una alternativa omitimos lossımbolos ‘<’ y ‘>’. La lectura de este arbol podrıa ser: “para evaluar una llamada a leqestudiar la forma del primer argumento; si este argumento es (reducible a) zero entoncesdevolver true; si es (reducible a) una expresion de la forma suc(X), estudiar la forma delsegundo argumento; si este segundo argumento es (reducible a) zero devolver false y si es(reducible a) suc(Y ) devolver el resultado de evaluar leq(X,Y )”. Cuando se dice que unargumento es de una forma determinada, esta forma es siempre una expresion que tieneun sımbolo de constructora en la raız y la reduccion a f.n.c. de dicho argumento debe tener

Page 89: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 89

esa misma constructora en su raız. Esto quiere decir que las ramas case, en la traducciona Prolog produciran llamadas a hnf como se vera en 3.10.3.

Las ramas try corresponden a la aplicacion de una regla de la funcion, una vez que setiene suficiente informacion sobre los parametros de llamada y estos encajan con los de laregla.

below(A,B)

below(suc(X),B)

below(A,B)

below(zero,B)

A/zero A/suc(X)

Y/suc(Z)

true

try

try

try

or

case

case

case

false

try

Y/zero

below(A,[ ])

below(A,B)

B/[ ]

{BELOW }1 2{BELOW , BELOW , BELOW , BELOW }

B/[Y | Ys]

4 53

below(suc(X),[Y | Ys])

below(suc(X),[suc(Z) | Ys])below(suc(X),[zero | Ys])

true < true <== susp(leq,[X,Z],R,S)==false |

susp(below,[suc(X),Ys],R,S) <== susp(leq,[X,Z],R’,S’)==true >

Figura 3.4: Arbol definicional de below

La construccion detallada del arbol definicional para la funcion below es algo masextensa y se ha omitido. Presenta en forma grafica dicho arbol en la figura 3.4. La funcionbelow no demanda uniformemente ninguna posicion, por lo que la primera ramificaciondel arbol es un or que hace una particion del conjunto de reglas. La rama izquierda deeste or opera unicamente sobre la primera regla de la funcion; a partir de ahı el algoritmoopera como si la funcion estuviese definida solo por esa regla, lo que significa que laprimera posicion es uniformemente demandada y produce una rama case (con una solaalternativa). Despues ya se puede aplicar la regla (rama try).

Para la segunda rama del or el algoritmo opera como si la funcion estuviese definidaunicamente por las reglas R2 a R5. Una rama or en la traduccion producira un predicadocon varias clausulas (tantas como ramas), como se veremos en 3.10.3.

Observese que las hojas de los arboles definicionales siempre corresponden a ramastry y que despues de una rama try no pueden aparecer mas ramificaciones. Y por otrolado, una rama try puede contener varias alternativas o reglas de funcion, como la ultimade below; esto ocurre cuando hay varias reglas de funcion con la misma cabeza (salvorenombramientos de variables), como ocurre con las reglas BELOW4 y BELOW5. Eneste caso, mediante analisis de demanda, no se puede determinar cual de estas reglasdebe aplicarse; en realidad, es posible que ambas se puedan aplicar. Por ejemplo, el arboldefinicional de la funcion choice definida por las reglas (esta funcion se trato en 2.3.7):

choice X Y = Xchoice X Y = Y

tendra un try en la raız con dos alternativas correspondientes a las dos reglas. Es decir, la

Page 90: Un lenguaje lógico funcional con restricciones1

90 CAPITULO 3. MECANISMO DE COMPUTO

demanda de patrones no determina la regla a aplicar, que es lo que se pretende para estafuncion indeterminista: aplicar ambas reglas (por backtracking) para hacer una eleccionindeterminista de uno de los argumentos.

En [AGL94] la construccion de los arboles definicionales tambien contempla el caso deque ramas try con varias alternativas. Sin embargo, a las reglas que definen una funcionse les impone una condicion de no ambiguedad, que basicamente, excluye las funcionesindeterministas: dos reglas de una misma funcion con cabezas compatibles deben contenerrestricciones proposicionalmente insatisfactibles, o bien, devolver el mismo resultado (tenerla misma expresion en el cuerpo). En T OY tal condicion no se exige, porque de hecho, seadmiten funciones indeterministas como choice y, en consecuencia, las ramas try puedencontener alternativas que produzcan distintos valores con los mismos argumentos. En otraspalabras, no se exigen condiciones de confluencia para las reglas de funcion.

3.10.3. Generacion de codigo para las funciones. Primera aproximacion

A partir de los arboles definicionales del apartado anterior T OY produce el codigo Pro-log correspondiente a cada una de las funciones del programa. En esta seccion presentamosuna primera version de la generacion de codigo, que no tiene en cuenta las restriccionesde desigualdad. En consecuencia, tampoco se toman en cuenta los almacenes de las desi-gualdades, por lo que en las llamadas al predicado hnf hemos omitido dichos almacenes(puede asumirse que ambos son vacıos). En las siguientes secciones, sobre esta version seharan los cambios necesarios para el tratamiento de las desigualdades y se detallaran lasoptimizaciones de codigo que lleva a cabo el sistema.

La traduccion de una funcion produce, en general, varios predicados. Uno de “entrada”(al que se invocara para evaluar una llamada a la funcion) que tiene el mismo nombre quela funcion y otros, cuyos nombres se construyen de acuerdo con las posiciones para lasque se ha obtenido una f.n.c. anteriormente. Las posiciones se notaran como secuencias deenteros separados por ‘.’ y formamos secuencias de posiciones separandolas mediante ‘,’.Para formar un nuevo nombre de predicado a partir de uno dado se concatenaran por laderecha a dicho nombre. Por ejemplo, el nombre f 1 2 2,1 hace referencia a un predicadocorrespondiente a la funcion f que ya cuenta con las f.n.c.’s para las posiciones 1, 2 y 2 · 1(en realidad, si la posicion 2 ·1 esta en f.n.c., la posicion 2 tambien debe estarlo). Ademas,todos estos predicados llevan como ultimo argumento un parametro extra que representael resultado de la evaluacion de la funcion (que normalmente se notara por H).

El algoritmo de generacion de codigo para una funcion f trabaja por analisis decasos sobre la forma que tiene el arbol definicional de la misma. En la llamada ini-cial, se le pasara el arbol completo dt(f(X1, ..., Xn),Rf ), siendo n la aridad de progra-ma de f y la secuencia de posiciones vacıa ε, de modo que dicha llamada tiene la for-ma gen code(dt(f(X1, ..., Xn),Rf ), ε). El algoritmo opera recursivamente y una llamadagenerica tendra la forma gen code(tree, positions), siendo tree un arbol definicional ypositions una secuencia de posiciones.

Algoritmo:

Supongamos

Page 91: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 91

tree ≡ pat− case X ofc1 : tree1;c2 : tree2;...cm : treem

donde pat = f(t1, ..., tn) y u es la posicion de X en f(t1, ..., tn). Sea HX una variablenueva y sea (t′1, ..., t

′n) construido como (t1, ..., tn)[X/HX]. Entonces se genera la

clausula:

f positions(t1, ..., tn, H) : −hnf(X, HX), f positions u(t′1, ..., t′n,H).

seguido del codigo producido por:

gen code(tree1, positions u)gen code(tree2, positions u)

...gen code(treen, positions u)

Supongamos

tree ≡ pat− ortree0;tree1;...treek

Entonces el codigo generado es el generado por las llamadas:

gen code(tree0, positions)gen code(tree1, positions)

...gen code(treek, positions)

Supongamos

tree ≡ pat− try〈e1 <== C1

|e2 <== C2

...|em <== Cm

donde pat = f(t1, ..., tn). Entonces, para cada una de las alternativas se genera unaclausula de la forma:

f positions(t1, ..., tn,H) : −solve(Ci), hnf(ei, H).

Estas clausulas se generan en el mismo orden en el que aparecen las alternativas, quecorresponde al orden textual de las reglas de f . El predicado solve se utiliza para laresolucion de restricciones y esta definido por las clausulas:

Page 92: Un lenguaje lógico funcional con restricciones1

92 CAPITULO 3. MECANISMO DE COMPUTO

solve([e == e′|R]) : −equal(e, e′), solve(R).solve([ ]).

Cuando la regla no tiene restricciones se omitira la llamada a solve (si no se omitieseserıa solve([ ]), que tiene exito automaticamente). equal es el predicado de resolucionde igualdades que se estudiaran en detalle en 3.11.5.

Si tree = ∅, entonces no se genera ningun codigo. ¥

Ejemplo:Para la funcion leq definida anteriormente, apoyandose en el arbol definicional de la

figura 3.3, el algoritmo de generacion de codigo opera del siguiente modo:Inicialmente la secuencia de posiciones es vacıa (positions = ε). El primer case produce

la clausula:

leq(A,B,H) :- hnf(A,HA), leq_1(HA,B,H).

Para las dos ramas del case, el algoritmo opera recursivamente sobre ambas conpositions = 1. La primera rama try produce la clausula:

leq_1(zero,B,H) :- hnf(true,H).

Para la segunda rama, que vuelve a ser un case se genera:

leq_1(suc(X),B,H) :- hnf(B,HB), leq_1_2(suc(X),HB,H).

y se invoca de nuevo al algoritmo con las dos ramas try y position = 1 2. Estas ramasproducen las clausulas:

leq_1_2(suc(X),zero,H) :- hnf(false,H).leq_1_2(suc(X),suc(Y),H) :- hnf(susp(leq,[X,Y],R,S),H).

¥Ejemplo:

Para la funcion below, el codigo producido serıa:

below(A,B,H) :- hnf(B,HB), below_2(A,HB,H).below(A,B,H) :- hnf(A,HA), below_1(HA,B,H).

below_2(A,[],H) :- hnf(true,H).

below_1(zero,B,H) :- hnf(true,H).below_1(suc(X),B,H) :- hnf(B,HB), below_1_2(suc(X),HB,H).

below_1_2(suc(X),[Y|Ys],H) :- hnf(Y,HY),below_1_2_2.1(suc(X),[HY|Ys],H).

below_1_2_2.1(suc(X),[zero|Ys],H) :- hnf(false,H).below_1_2_2.1(suc(X),[suc(Z)|Ys],H) :- solve([susp(leq,[X,Z],R,S)==false]),

hnf(true,H).below_1_2_2.1(suc(X),[suc(Z)|Ys],H) :- solve([susp(leq,[X,Z],R,S)==true]),

hnf(susp(below,[suc(X),Ys],R’,S’),H).

Page 93: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 93

En este ejemplo, la primera rama es or, lo que provoca que el predicado de entradabelow tenga dos clausulas. La primera de ellas es un case que opera sobre la segundaposicion. La segunda es otro case sobre la primera posicion y que produce las dos clausulasde below 1. Observese que las dos ultimas clausulas de below 1 2 2,1 corresponden a lasdos alternativas del ultimo try del arbol. Estas dos clausulas tienen la misma cabeza,pero en este caso no se trata de una funcion indeterminista, ya que las restricciones (losargumentos de solve) de ambas clausulas son logicamente incompatibles. ¥

3.10.4. Incorporacion de desigualdades en la traduccion de funciones

En este apartado vamos a modificar la especificacion Prolog de la seccion anteriorpara tratar las desigualdades. Ahora todos los predicados de la traduccion deben llevarlos dos argumentos extra Cin y Cout correspondientes al almacen de restricciones deentrada y salida respectivamente. Los predicados hnf , solve, equal tambien llevar estosdos argumentos. Por otro lado, debemos ampliar el predicado solve con una nueva clausula(la segunda) para que pueda tratar tambien desigualdades, que ahora quedara:

solve([e == e′|R], Cin, Cout) : −equal(e, e′, Cin,Cout1), solve(R, Cout1, Cout).solve([e /= e′|R], Cin, Cout) : −notEqual(e, e′, Cin,Cout1), solve(R, Cout, Cout).solve([ ], Cin, Cin).

El predicado notEqual de resolucion de desigualdades se estudiara en 3.12.El ultimo cambio es mas sutil. En la traduccion propuesta en el apartado anterior, hay

unificaciones Prolog que se hacen (de forma implıcita) al unificar el predicado de llamadacon la cabeza de las clausulas. Por ejemplo, en el codigo generado para la funcion leq delapartado anterior las dos clausulas para leq 1 discriminan en el primer argumento los casoszero y suc(X) respectivamente, una vez que se ha evaluado una f.n.c. para esta posicionen el predicado leq. Sin embargo, en este argumento pueden aparecer variables T OY conrestricciones de desigualdad asociadas, es decir, no basta la unificacion Prolog, sino quedebe hacerse unificacion T OY. Por ejemplo, es posible hacer una llamada de la formaleq(X, suc(zero),H), siendo X una variable que tiene asociada la desigualdad X /= zero;si se hiciese simplemente unificacion logica, serıa aplicable la primera clausula de leq 1que instanciarıa X a zero. Esto generarıa una inconsistencia en los almacenes que no serıadetectada.

Debemos hacer una unificacion T OY que tenga en cuenta las desigualdades asociadasa las variables que aparecen en ambas expresiones. Pero observese que de las expresionesque debemos unificar sabemos con certeza que estan en f.n.c.: la primera acaba de cal-cularse (en el ejemplo se calcula en el predicado leq) y la segunda es una expresion quecomienza por constructora (por construccion de la rama case del arbol definicional, es dehecho una forma normal). Ademas se sabe que ambas expresiones no comparten ningunavariable (no es necesario hacer el occurs-check) porque todas las variables de la expresionque introduce el case son nuevas. Para aprovechar esta informacion, T OY incorpora elpredicado especializado unifyHnfs, encargado de unificar formas normales de cabeza sinoccurs-check. El codigo se muestra en la tabla 3.12.

La primera clausula de unifyHnfs es la que trata el caso que podrıa causar problemas:cuando la f.n.c. que se acaba de evaluar es una variable que puede tener restricciones dedesigualdad. En esta situacion se hace exactamente lo mismo que en la segunda clausulade hnf (3.9). Se extraen las restricciones asociadas a dicha variable en CH antes de hacerla unificacion y despues se resuelven todas las restricciones de desigualdad provocadas porla unificacion.

Page 94: Un lenguaje lógico funcional con restricciones1

94 CAPITULO 3. MECANISMO DE COMPUTO

unifyHnfs(H,L,Cin,Cout) :-var(H),!,extractCtr(H,Cin,Cout1,CH),H=L,propagate(H,CH,Cout1,Cout).

unifyHnfs(H,H,Cin,Cin).

Cuadro 3.12: Unificacion de formas normales de cabeza (unificacion T OY)

En la segunda clausula, cuando la f.n.c. que se ha calculado no es una variable launificacion es sencillamente la unificacion Prolog. No hay que resolver ninguna restriccionde desigualdad porque el segundo argumento de unifyHnfs es siempre una expresion dela forma c(X1, ..., Xn), con c ∈ DCn y X1, ..., Xn, que proviene de una rama case; portanto los argumentos de la constructora c seran variables nuevas, sobre las que no puedehaber ninguna restriccion (los case solo estudian la constructora mas externa).

En el algoritmo de generacion de codigo del apartado anterior, al incluir las desigualda-des, debe tenerse en cuenta en las ramas case, que tras evaluar una f.n.c., las clausulas quese producen a continuacion deben utilizar este predicado para hacer la unificacion. Estealgoritmo funciona esencialmente como el anterior, pero ahora lleva el parametro adicionaloldpat en el que se pasa el patron que se ha utilizado en la ultima rama case. Este patronsera el que se utilice para construir la cabeza del predicado Prolog correspondiente y en lallamada inicial sera identico a pat (no hay case anterior).

El algoritmo utiliza el hecho de que pat y oldpat son identicos, o bien, difieren en unasola posicion en la que oldpat contiene una variable Y y pat una expresion que comienzapor constructora de la forma d(Z). Esto es ası por la construccion del arbol definicional.

La llamada inicial tiene la forma gen code(dt(f(X1, ..., Xn),Rf ), ε, f(X1, ..., Xn)),siendo n la aridad de programa de f y ε la secuencia de posiciones vacıa. Una llamadagenerica tendra la forma gen code(tree, positions, oldpat).

Algoritmo:

Supongamos

tree ≡ pat− case X ofc1 : tree1;c2 : tree2;...cm : treem

Sea u la posicion de X en pat y HX una variable nueva.

Si oldpat = pat ≡ f(t1, ..., tn) entonces sea (t′1, ..., t′n) ≡ (t1, ..., tn)[X/HX]. La

clausula generada es:

f positions(t1, ..., tn,H,Cin,Cout) : −hnf(X,HX,Cin,Cout1),f positions u(t′1, ..., t

′n,H,Cout1, Cout).

Page 95: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 95

y si no son identicos, tendremos oldpat = f(t1, ..., tn) y pat = oldpat[Y/d(Z)](ademas X 6≡ Y ). Sea (t′1, ..., t

′n) ≡ (t1, ..., tn)[Y/d(Z)][X/HX]. Entonces se gene-

ra la clausula:

f positions(t1, ..., tn,H,Cin, Cout) : −unifyHnfs(Y, d(Z), Cin, Cout1),hnf(X, HX, Cout1, Cout2),f positions u(t′1, ..., t

′n,H, Cout2, Cout).

En ambos casos se generan las clausulas correspondientes a las siguientes llamadas:

gen code(tree1, positions u, pat)gen code(tree2, positions u, pat)

...gen code(treen, positions u, pat)

Supongamos

tree ≡ pat− ortree0;tree1;...treek

Entonces el codigo generado es el generado por las llamadas:

gen code(tree0, positions, oldpat)gen code(tree1, positions, oldpat)

...gen code(treek, positions, oldpat)

Supongamos

tree ≡ pat− try〈e1 <== C1

|e2 <== C2

...|em <== Cm

Si oldpat = pat ≡ f(t1, ..., tn) entonces para cada una de las alternativas se generauna clausula de la forma:

f positions(t1, ..., tn,H,Cin, Cout) : −solve(Ci, Cin,Cout1),hnf(ei, H, Cout1, Cout).

y si no son identicos, sera oldpat = f(t1, ..., tn) y pat = oldpat[Y/d(Z)]. Sea e′i =ei[Y/d(Z)]. Para cada una de las alternativas se genera una clausula de la forma:

Page 96: Un lenguaje lógico funcional con restricciones1

96 CAPITULO 3. MECANISMO DE COMPUTO

f positions(t1, ..., tn,H, Cin,Cout) : −unifyHnfs(Y, d(Z), Cin,Cout1),solve(Ci, Cout1, Cout2),hnf(e′i,H, Cout2, Cout).

Si tree = ∅, entonces no se genera ningun codigo. ¥

Por ejemplo, para la funcion leq el codigo producido es:

leq(A,B,H,Cin,Cout) :- hnf(A,HA,Cin,Cout1), leq_1(HA,B,H,Cout1,Cout).

leq_1(A,B,H,Cin,Cout) :- unifyHnfs(A,zero,Cin,Cout1),hnf(true,H,Cout2,Cout).

leq_1(A,B,H,Cin,Cout) :- unifyHnfs(A,suc(X),Cin,Cout1),hnf(B,HB,Cout1,Cout2),leq_1_2(suc(X),HB,H,Cout2,Cout).

leq_1_2(suc(X),B,H,Cin,Cout) :- unifyHnfs(B,zero,Cin,Cout1),hnf(false,H,Cout1,Cout).

leq_1_2(suc(X),B,H,Cin,Cout) :- unifyHnfs(B,suc(Y),Cin,Cout1),hnf(susp(leq,[X,Y],R,S),H,Cout1,Cout).

Ahora, tras evaluar una f.n.c. para el primer argumento en la primera clausula, las dosclausulas de leq 1 hacen la distincion de casos mediante el predicado unifyHnfs, y noen la cabeza como se hacıa anteriormente (sin desigualdades). Se puede observar tambiencomo se pasan los almacenes de entrada y salida de unos predicados a otros con los dosargumentos extra. Puede entenderse el almacen como un acumulador de restricciones. Elprimer predicado al que se llama en el cuerpo de una clausula toma como almacen deentrada Cin y genera otro de salida Cout1, que se le pasara al siguiente como almacende entrada. Utilizaremos tantas variables auxiliares Cout1, Cout2... como sea necesario,teniendo en cuenta que la ultima llamada debe producir como almacen de salida Cout (elmismo que en la cabeza).

Para below tenemos:

below(A,B,H,Cin,Cout) :- hnf(B,HB,Cin,Cout1),below_2(A,HB,H,Cout1,Cout).

below(A,B,H,Cin,Cout) :- hnf(A,HA,Cin,Cout1),below_1(HA,B,H,Cout1,Cout).

below_2(A,B,H,Cin,Cout) :- unifyHnfs(B,[],Cin,Cout1),hnf(true,H,Cout1,Cout).

below_1(A,B,H,Cin,Cout) :- unifyHnfs(A,zero,Cin,Cout1),hnf(true,H,Cout1,Cout).

below_1(A,B,H,Cin,Cout) :- unifyHnfs(A,suc(X),Cin,Cout1),hnf(B,HB,Cout1,Cout2),below_1_2(suc(X),HB,H,Cout2,Cout).

below_1_2(suc(X),B,H,Cin,Cout) :-

Page 97: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 97

unifyHnfs(B,[Y|Ys],Cin,Cout1),hnf(Y,HY,Cout1,Cout2),below_1_2_2.1(suc(X),[HY|Ys],H,Cout2,Cout).

below_1_2_2.1(suc(X),[Y|Ys],H,Cin,Cout) :-unifyHnfs(Y,zero,Cin,Cout1),hnf(false,H,Cout1,Cout).

below_1_2_2.1(suc(X),[Y|Ys],H,Cin,Cout) :-unifyHnfs(Y,suc(Z),Cin,Cout1),solve([susp(leq,[X,Z],R,S)==false],Cout1,Cout2),hnf(true,H,Cout2,Cout).

below_1_2_2.1(suc(X),[Y|Ys],H,Cin,Cout) :-unifyHnfs(Y,suc(Z),Cin,Cout1),solve([susp(leq,[X,Z],R,S)==true],Cout1,Cout2),hnf(susp(below,[suc(X),Ys],R’,S’),H,Cout2,Cout).

Con esta traduccion queda completamente resuelto el asunto de las desigualdades. Sinembargo, tambien se ha perdido la posibilidad de hacer una optimizacion de codigo basadaen la indexacion de argumentos ([Gro96]). Por ejemplo, en las dos clausulas de below 1 dela traduccion anterior, los primeros argumentos del predicado eran zero y suc(X) respec-tivamente. Sicstus Prolog es capaz de aprovechar esta informacion para utilizar la clausulaapropiada sin intentar otra alternativa, cuando el primer argumento de la llamada esta su-ficientemente instanciado, es decir, cuando es de la forma zero o suc(Y ). De esta formaevita intentar hacer unificaciones que con seguridad provocaran fallo. En la traduccionque se acaba de presentar, tal optimizacion no sera posible porque las unificaciones so-bre las que Sicstus podrıa optimizar se hacen explıcitas en el cuerpo de las clausulas.Observese que ahora las cabezas de todas las clausulas para un mismo predicado tienencabezas identicas. No obstante, el rendimiento del sistema no se vera seriamente afectadopor la ausencia de dicha optimizacion. Son de mayor relevancia las que se proponen en lasiguiente seccion.

3.10.5. Optimizaciones de codigo

Sobre la traduccion anterior, T OY realiza, de forma automatica, algunas optimiza-ciones que incrementan notablemente el rendimiento del sistema. Todas estas mejoras decodigo se realizan en el proceso de traduccion, no en una etapa posterior, por lo queT OY utilizara unicamente la informacion del arbol definicional para llevarlas a cabo (nohay etapa de postproceso). Las optimizaciones son:

En muchos casos, en el codigo generado aparecen llamadas a hnf que tienen comoprimer argumento una suspension (ultimas clausulas de la traduccion leq y below).La forma general de estas llamadas es hnf(susp(F, Ls,R, S),H,Cin, Cout) y en estecaso se sabe con seguridad que la suspension no ha sido evaluada aun porque lasvariables R y S son locales a la clausula (nuevas). En esta situacion la segundaclausula de hnf siempre evaluarıa la llamada invocando a hnf susp, por lo que elsistema puede hacer un unfolding reemplazando la llamada a hnf por la llamadaconcreta a la funcion. Por ejemplo, la ultima clausula de leq quedara de esta forma:

leq_1_2(suc(X),B,H,Cin,Cout) :- unifyHnfs(B,suc(Y),Cin,Cout1),

Page 98: Un lenguaje lógico funcional con restricciones1

98 CAPITULO 3. MECANISMO DE COMPUTO

leq(X,Y,H,Cout1,Cout).

Sobre el predicado solve de la traduccion se hace un unfolding, es decir, cada restric-cion de la forma e == e′ se traduce por equal(e, e′, Cin,Cout) y cada desigualdade /= e′ a notEqual(e, e′, Cin, Cout). De hecho, el predicado solve no existe en elsistema y en su lugar aparecera un secuencia de igualdades y desigualdades.

Hay otro posible unfolding cuando se tiene una llamada a un predicado definido poruna sola clausula. En este caso, T OY reemplaza dicha llamada por el cuerpo delpredicado al que se llama, y esto lo hace para todas las llamadas a dicho predicado.De este modo, la clausula que definıa dicho predicado queda obsoleta y no se genera.Por ejemplo, en la traduccion de la funcion below, el predicado below 2 esta definidopor una sola clausula; entonces T OY reemplaza la llamada que tiene en la primeraclausula de below por el cuerpo de below 2, con lo que la primera clausula queda:

below(A,B,H,Cin,Cout) :-hnf(B,HB,Cin,Cout1),unifyHnfs(HB,[],Cout1,Cout2),hnf(true,H,Cout2,Cout).

y desaparece el predicado below 2. En el arbol definicional T OY detecta esta situa-cion cuando se encuentra con una rama case seguida de otro case que, a su vez, osolo tiene una rama, o bien, va seguida de un try con una sola alternativa. En elejemplo, se trata de un case seguido de un try con una sola alternativa.

Cuando en una rama try el valor que devuelve la funcion es una f.n.c., no es nece-sario recalcular dicha forma llamando al predicado hnf . Por ejemplo, en la primeraclausula de below sobre la que se ha hecho un unfolding en el punto anterior, elvalor que devolvera la funcion es false, que es una f.n.c. (de hecho, una forma nor-mal). La ultima llamada a hnf es redundante y lo unico que hara sera ligar falseal valor de retorno H. Esta ultima llamada a hnf puede reemplazarse por una sen-cilla unificacion H = false, que puede hacerse incluso en la cabeza del predicadoobteniendo:

below(A,B,true,Cin,Cout) :-hnf(B,HB,Cin,Cout1),unifyHnfs(HB,[],Cout1,Cout).

Como resultado de esta optimizacion tendremos en muchos casos el calculo de unaf.n.c. seguido de una unificacion de f.n.c.’s, como en el ejemplo que acabamos de ver.Puesto que hnf hace una unificacion teniendo en cuenta las desigualdades asociadasa las variables, ambos predicados se pueden fusionar en una llamada a hnf donde sepasa como segundo argumento el segundo argumento de unifyHnfs. De este modose aprovecha la unificacion que hace hnf . La clausula anterior para below quedarıareducida a:

below(A,B,true,Cin,Cout) :- hnf(B,[],Cin,Cout).

Page 99: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 99

La optimizacion del punto anterior puede generalizarse aun mas mediante subida deconstructoras, cuando en el arbol definicional se tiene un nodo tal que todas las hojasque dependen de el tienen un esqueleto o cascara comun: el valor que devuelve lafuncion es una expresion cuya parte construida es siempre la misma para todas lashojas que dependen del mencionado nodo. Esto ocurre, por ejemplo para la siguientefuncion, que comprueba si una lista de naturales es no vacıa:

not_empty [zero | R] = truenot_empty [suc X | R] = true

A/[X|Xs] case

case

not_empty([X|Xs])

not_empty(A)

not_empty([zero|Xs]) not_empty([suc(Y)|Xs])

true true

trytry

X/suc(Y)X/zero

Figura 3.5: Arbol definicional de not empty

El arbol definicional correspondiente se muestra en la figura 3.5. La traduccion corres-pondiente serıa:

not_empty(A,H,Cin,Cout) :-hnf(A,HA,Cin,Cout1),not_empty_1(HA,H,Cout1,Cout).

not_empty_1(A,H,Cin,Cout) :-unifyHnfs(A,[X|Xs],Cin,Cout1),hnf(X,HX,Cout1,Cout2),not_empty_1_1.1([HX|Xs],H,Cout2,Cout).

not_empty_1_1.1([X|Xs],H,Cin,Cout) :-unifyHnfs(X,zero,Cin,Cout1),hnf(true,H,Cout1,Cout).

not_empty_1_1.1([X|Xs],H,Cin,Cout) :-unifyHnfs(X,suc(Y),Cin,Cout1),hnf(true,H,Cout1,Cout).

Aplicando unfolding sobre not empty 1 y la optimizacion del punto anterior tendrıamos:

not_empty(A,H,Cin,Cout) :-hnf(A,HA,Cin,Cout1),unifyHnfs(A,[X|Xs],Cout1,Cout2),hnf(X,HX,Cout2,Cout3),

Page 100: Un lenguaje lógico funcional con restricciones1

100 CAPITULO 3. MECANISMO DE COMPUTO

not_empty_1_1.1([HX|Xs],H,Cout3,Cout).

not_empty_1_1.1([X|Xs],true,Cin,Cout) :-unifyHnfs(X,zero,Cin,Cout).

not_empty_1_1.1([X|Xs],true,Cin,Cout) :-unifyHnfs(X,suc(Y),Cin,Cout).

Observemos que esta funcion solo puede devolver el valor true. Si hiciesemos unallamada de la forma not empty(A, false, Cin, Cout) el computo fallarıa, pero antesevaluarıa una f.n.c. para A que serıa el propio A, la unificarıa con [X|Xs], evaluarıauna f.n.c. para X que serıa el propio X y luego producirıa el fallo en la llamadanot empty 1 1,1([X|Xs], false, Cout3, Cout), al no poder unificarlo con ninguna ca-beza. No obstante este fallo podrıa haberse anticipado sin evaluar las dos f.n.c.’s yla unificacion: puesto que la funcion, en caso de tener exito devolvera el valor truese puede colocar este valor en la clausula para not empty en lugar de la variable H.De este modo, la clausula para not empty serıa:

not_empty(A,true,Cin,Cout) :-hnf(A,HA,Cin,Cout1),unifyHnfs(A,[X|Xs],Cout1,Cout2),hnf(X,HX,Cout2,Cout3),not_empty_1_1.1([HX|Xs],H,Cout3,Cout).

De este modo, la llamada que proponıamos, not empty(A, false, Cin,Cout), fa-llara automaticamente sin evaluar ninguna f.n.c. ni hacer ninguna unificacion. Estasituacion es facilmente detectable en el arbol definicional, ya que todas las hojas (quedependen del primer nodo, en este caso) devuelven el valor true. En general, puedendevolver distintos valores, pero con una parte del esqueleto comun. Por ejemplo, siun nodo tiene dos ramificaciones que devuelven [zero, zero] y [zero, suc(X)], la parteconstruida que se podrıa subir al nodo es [zero, Y ].

Otra optimizacion consiste en eliminar cierta informacion redundante de algunasclausulas como se propone en [Han95b]. Por ejemplo, en la traduccion de leq, las dosclausulas del predicado leq 1 2 llevan como primer argumento suc(X). La variableX es relevante para el computo que se va realizar, sin embargo, no es necesaria laexpresion completa suc(X) (la constructora suc no se utiliza). Esto sugiere que enla llamada a leq 1 2 que se hace en el cuerpo de la segunda clausula de leq 1 sepuede prescindir de dicha constructora y pasar solo la variable X. En el nombredel predicado concatenaremos ademas, el nombre de la constructora inutil que seeliminado para dejar constancia de este hecho. La segunda clausula de leq 1 queda:

leq_1(A,B,H,Cin,Cout) :- unifyHnfs(A,suc(X),Cin,Cout1),hnf(B,HB,Cout1,Cout2),leq_1_2_suc(X,HB,H,Cout2,Cout).

y las clausulas para leq 1 2:

leq_1_2_suc(X,B,false,Cin,Cout) :- unifyHnfs(B,zero,Cin,Cout).leq_1_2_suc(X,B,H,Cin,Cout) :- unifyHnfs(B,suc(Y),Cin,Cout1),

leq(X,Y,H,Cout1,Cout).

Page 101: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 101

Eliminando estas constructoras inutiles de los argumentos, las unificaciones son me-nos costosas y se gana en eficiencia.

La ultima optimizacion que se hace es en presencia de restricciones de la formaf(t1, ..., tn)3b, con f funcion de aridad (de programa) n (la expresion f(t1, ..., tn) esuna llamada a funcion) y b ∈ {true, false}. En este caso, en vez de generar el equal oel notEqual correspondiente, directamente se genera la llamada f(t1, ..., tn, b, Cin, Cout),que es la que producirıa la resolucion de la restriccion (se ahorran llamadas inter-medias). Ademas es una llamada orientada, con el valor b, es decir, se anticipa elresultado que debe producir la evaluacion de la funcion, lo que permite anticipar elfallo, en caso de que este efectivamente vaya a producirse.

Teniendo en cuenta todas las optimizaciones que acabamos de ver, el codigo que pro-duce el sistema para las funciones leq y below es el siguiente:

% leqleq(A,B,H,Cin,Cout):-

hnf(A,HA,Cin,Cout1),leq_1’(HA,B,H,Cout1,Cout).

leq_1(A,B,true,Cin,Cout):-unifyHnfs(A,zero,Cin,Cout).

leq_1(A,B,H,Cin,Cout):-unifyHnfs(A,suc(X),Cin,Cout1),hnf(B,HB,Cout1,Cout2),leq_1_2_suc(X,HB,H,Cout2,Cout).

leq_1_2_suc’(X,B,false,Cin,Cout):-unifyHnfs(B,zero,Cin,Cout).

leq_1_2_suc(X,B,H,Cin,Cout):-unifyHnfs(B,suc(Y),Cin,Cout1),leq(X,Y,H,Cout1,Cout).

% belowbelow(A,B,true,Cin,Cout):-

hnf(B,[],Cin,Cout).below(A,B,H,Cin,Cout):-

hnf(A,HA,Cint,Cout1),below_1(HA,B,H,Cout1,Cout).

below_1(A,B,true,Cin,Cout):-unifyHnfs(A,zero,Cin,Cout).

below_1(A,B,H,Cin,Cout):-unifyHnfs(A,suc(X),Cin,Cout1),hnf(B,:(Y,Ys),Cout1,Cout2),hnf(Y,HY,Cout2,Cout3),below_1_2_suc_2.1_:(X,HY,Ys,H,Cout3,Cout).

Page 102: Un lenguaje lógico funcional con restricciones1

102 CAPITULO 3. MECANISMO DE COMPUTO

below_1_2_suc_2.1_:(X,Y,H,false,Cin,Cout):-unifyHnfs(Y,zero,Cin,Cout).

below_1_2_suc_2.1_:(X,Y,Ys,false,Cin,Cout):-unifyHnfs(Y,suc(Z),Cin,Cout1),leq(X,Z,false,Cout1,Cout).

below_1_2_suc_2.1_:(X,Y,Ys,H,Cin,Cout):-unifyHnfs(Y,suc(Z),Cin,Cout1),leq(X,Z,true,Cout1,Cout2),below(suc(X),Ys,H,Cout2,Cout).

En el apendice H se muestra el programa Prolog que se ha implementado en T OY parala genecacion de codigo y que lleva a cabo todas las optimizaciones que se han presentado.

3.10.6. Codigo para las funciones primitivas y apply

El codigo de las funciones primitivas no se genera en tiempo de compilacion, sino queha sido escrito manualmente y se encuentra en el archivo primitives.pl. No obstante, elformato de dicho codigo se ajusta al del resto de las funciones. Por ejemplo para la funcion+, se tiene el predicado:

primitiveFunct(+, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).+(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is HX + HY; errPrim).

El tipo se anota con la clausula primitiveFunct (para distinguirla de las funciones deusuario) y los argumentos tienen el significado que se explico en 3.4. Esta funcion nece-sita una f.n.c. para cada uno de sus argumentos (en este caso f.n.c. y forma normal sonsinonimos) y es lo primero que hace el predicado. A continuacion comprueba que ambosargumentos son numeros (podrıan ser variables) antes de utilizar el predicado de suma deProlog10. Si esto es ası devuelve efectivamente la suma de argumentos y en otro caso llamaal predicado errPrim que muestra el mensaje:

RUNTIME ERROR: Variables are not allowed in arithmetical operations.(/cflpr. should be active to do this)

El codigo que acabamos de presentar es el que corresponde al sistema sin restriccio-nes sobre los reales. Cuando se incorporan las restricciones este codigo cambia (archivoprimitvesClpr.pl) como se vera en 3.15. Las funciones de igualdad y desigualdad tambientienen su tipo correspondiente en primitives.pl:

primitiveFunct(==, 2, 2, (A -> (A -> bool)), bool).primitiveFunct(/=, 2, 2, (A -> (A -> bool)), bool).

Sin embargo, su codigo es especial y se encuentra en el archivo toycomm.pl.En el archivo primitives.pl tambien se encuentra la informacion sobre los operadores

infijos primitivos necesaria para el analisis sintactico de objetivos y para la salida derespuestas:

10El predicado − > es el if then else de Prolog; C− > P1; P2 tiene la lectura: si se satisface la condicionC entonces se llama al predicado P1, en otro caso a P2.

Page 103: Un lenguaje lógico funcional con restricciones1

3.10. GENERACION DE CODIGO PARA LAS FUNCIONES 103

primInfix(^, noasoc, 90).infix(**, noasoc, 90).primInfix(/, noasoc, 80).primInfix(*, left, 80).primInfix(+, left, 70).primInfix(-, left, 70).primInfix(<, noasoc, 50).primInfix(<=, noasoc, 50).primInfix(>, noasoc, 50).primInfix(>=, noasoc, 50).primInfix(==, noasoc, 20).primInfix(/=, noasoc, 20).primInfix(:, right, 12).primInfix(’,’, right, 12).

Estas clausulas se han generado a partir de las clausulas infix que se vieron en 3.5.2,excepto las dos ultimas, que corresponden a las listas y a las tuplas, y no tienen declaracionexplıcita visible al usuario (no aparecen en el archivo basic.toy).

Para la funcion apply segun la transformacion a primer orden que se estudio en 3.5.1se producen unas reglas a las que se puede aplicar el algoritmo de generacion de codigo.Sin embargo, para esta funcion no se genera explıcitamente el arbol definicional, sino quese genera directamente el codigo correspondiente. El predicado de entrada reduce a f.n.c.la expresion funcional que se va a aplicar y es siempre fijo:

apply(F, X, H, Cin, Cout):-hnf(F, HF, Cin, Cout1),apply_1(HF, X, H, Cout1, Cout).

El resto de predicados depende de las constructoras y funciones del programa y lasprimitivas. Por ejemplo, para la constructora de listas ‘:’/2 se generan dos clausulas:

apply_1(:, X, :(X), Cin, Cin).apply_1(:(X), Y, :(X, Y), Cin, Cin).

Estas dos clausulas corresponden a las constructoras ‘:0’ y ‘:1’ de la signatura extendida(vease 3.5). Prolog admite construcciones con el mismo functor y distintas aridades, porlo que representamos ambas con el mismo sımbolo ‘:’.

Para la primitiva ‘+’ tendremos:

apply_1(+, X, +(X), Cin, Cin).apply_1(+(X), Y, H, Cin, Cout):-

+(X, Y, H, Cin, Cout).

Segun las ideas expuestas en 3.5.1 la primera clausula que corresponde a la aplicacionparcial de la constructora ‘+’ a un argumento y produce otra constructora. Por el con-trario, la segunda clausula corresponde a la aplicacion total de la constructora y produceuna llamada a la funcion correspondiente. Como antes, utilizamos el mismo sımbolo pararepresentar constructoras de distintas aridades. Observese que se utiliza el mismo nombrede constructora con distintas aridades (Prolog los distingue).

Para la funcion map, en la que nos apoyamos para introducir el orden superior, seproduciran las clausulas:

Page 104: Un lenguaje lógico funcional con restricciones1

104 CAPITULO 3. MECANISMO DE COMPUTO

apply_1(map, X, map(X), Cin, Cout).apply_1(map(X), Y, H, Cin, Cout):-

map(X, Y, H, Cout, Cout).

3.11. Igualdad estricta (==)

En este apartado tratamos otra de las operaciones fundamentales del sistema: la re-solucion de restricciones de igualdad estricta. Para resolver una restriccion de la formaA == B, T OY trata de estrechar ambos miembros a formas normales unificables. Si noaparecen sımbolos de funcion ni en A ni en B, es decir, si A y B son formas normales laresolucion de la igualdad estricta consiste en unificar A y B. En el caso de que aparezcanllamadas a funcion en alguno de los miembros habra que evaluar (siempre perezosamen-te) estas llamadas para resolver la restriccion y esta es la diferencia fundamental entre laigualdad estricta y la unificacion de los lenguajes logicos, en los que no hay llamadas afuncion (y no se necesitan reducciones). Por otro lado, en Prolog es habitual prescindirdel occurs-check de variables a la hora de unificar debido al elevado coste que conlleva.Por ejemplo, en Prolog la unificacion X = [X] tiene exito produciendo una ligadura cıclicaX = [[[...[X]...]]]. En T OY la restriccion analoga X == [X] falla porque X aparece en ellado derecho (realmente en la cascara del lado derecho, como veremos).

En el mecanismo operacional que guıa la resolucion de estas restricciones estan involu-crados dos conceptos que vamos a exponer antes de abordar en detalle dicho mecanismo.El primero de ellos es la cascara o esqueleto de un termino, que es otro termino en el que seha reemplazado cada llamada a funcion mas externa por una variable nueva. Formalmen-te podemos dar una definicion de cascara sobre la estructura de los terminos de nuestrolenguaje:

casc(X) = X, para toda variable Xcasc(c(e1, ..., en)) = c(casc(e1), ..., casc(en)), si c ∈ DCn o c ∈ FSm con n ≤ mcasc(f(e1, ..., en)) = X, siendo X una variable nueva, si f ∈ FSn

(en segundo caso cubre tambien el caso de funciones aplicadas parcialmente, que sonconstructoras a todos los efectos segun vimos en 3.5).

La cascara de un termino es otro termino que recoge la parte construida del original.Por ejemplo, si a, b, c son sımbolos de constructora y f sımbolo de funcion, casc(c(a, Y )) =c(a, Y ) (Y variable), casc(c(f(a), b) = c(X, b), casc(f(f(a))) = X (X variable nueva enlos dos ultimo casos). En T OY no existe un predicado especıfico para construir la cascarade un termino. Hay algunos predicados como el de occurs-check (predicado occursNot),que hacen un estudio de la estructura del termino y simultaneamente generan su cascara.En general, el recorrido de la estructura de un termino es costoso (en tiempo), por lo queen nuestro sistema se aprovechan estos recorridos para producir otro tipo de informacionque sera de utilidad posteriormente. En los apartados siguientes se vera como y donde seconstruyen las cascaras.

El otro concepto que utilizaremos es el de frontera de dos terminos que podemos definircomo la cascara comun de ambos terminos. Por ejemplo, tomando a, b, c, f como antes, lafrontera de c(a, a) y c(a, f(b)) es c(a,X) (X variable nueva). Los terminos c(a, b) y b notienen frontera (su frontera es vacıa). Realmente, lo que nos interesa en el sistema no estanto la frontera de los terminos en sı, como el conjunto de igualdades que se desprendede la construccion de la misma, y que son las que tendremos que resolver para resolverla original. En el primero de los ejemplos anteriores, para resolver la igualdad c(a, b) ==

Page 105: Un lenguaje lógico funcional con restricciones1

3.11. IGUALDAD ESTRICTA (==) 105

c(a, f(b)), la igualdad que queda pendiente tras calcular la frontera es a == f(b). Comoes logico, cuando los terminos no tienen frontera como en el segundo ejemplo, no quedaninguna restriccion pendiente porque, de hecho, la igualdad estricta entre ellos no puederesolverse (produce fallo). Como veremos, este ultimo ejemplo ilustra la razon de ser delcalculo de fronteras: anticipar el fallo. A diferencia de las cascaras, T OY sı que incorporaun predicado especıfico para el calculo de fronteras (3.11.2).

Para la resolucion de igualdades estrictas genericas (entre expresiones cualesquiera)T OY incorpora el predicado equal. Este predicado hace uso de otros auxiliares, que estu-diaremos primero:

occursNot: hace el occurs-check de una variable en un termino y extrae la cascarade dicho termino.

binding: liga una variable a una f.n.c.

eqFrontier: produce la lista de igualdades que quedan por resolver despues de hacerla frontera de dos terminos.

equalHnf : resuelve igualdades entre f.n.c.’s

3.11.1. El occurs-check

El predicado occursNot es el responsable de garantizar que una variable no aparece (notiene ocurrencias) en la cascara de un termino. Es importante destacar el hecho de que lavariable puede aparecer en el termino siempre que sea dentro de una llamada a funcion, ypor tanto no en la cascara. Por ejemplo, si f es un sımbolo de funcion y c de constructora,y se quiere resolver la restriccion X == c(f(X)) el occurs-check tiene exito al estudiar lasapariciones de X sobre el termino c(f(X)). Aparentemente esta forma de operar puedeinducir un error si por ejemplo, la funcion f es la identidad, ya que el termino anterior,una vez evaluada f , serıa c(X) y claramente X aparece en el. Sin embargo, esta situacionno se produce porque occursNot tiene una funcionalidad “extra”: devuelve la cascara dedicho termino y genera una lista de igualdades “pendientes”. Como veremos despues deestudiar el codigo para este predicado, al resolver este tipo de igualdades se detectara laanomalıa anterior.

La especificacion de occursNot es la siguiente:

occursNot(X, T, ShT, LstEqs) ⇔ X es una variable que no aparece en la cascara deltermino T , que se devolvera en ShT . En LstEqs se devuelve la lista de igualdades pendientes.

El codigo del predicado occursNot se muestra en la tabla 3.13. Tiene como ultimoargumento una lista diferencia de la forma L/M en la que se van a recoger las igualdadesque quedan pendientes. Se procede por analisis de casos sobre la estructura del terminoque se recibe como segundo argumento. En la primera clausula, cuando este termino esuna variable Y , la variable X no aparece en el siempre que X e Y no sean identicas11. Lacascara de la variable Y es ella misma y no quedan restricciones pendientes de resolucion,por lo que la lista de igualdades pendientes es la misma de entrada, hecho que se representamediante la lista diferencia L/L.

La segunda y tercera clausulas se ocupan del caso en el que el termino del segundoargumento es una llamada a funcion, que debido al sharing aparece siempre en forma

11El predicado Prolog T\ == S tiene exito si T y S son sintacticamente distintos. En particular, parados variables X e Y tiene exito si no son la misma variable

Page 106: Un lenguaje lógico funcional con restricciones1

106 CAPITULO 3. MECANISMO DE COMPUTO

occursNot(X,Y,ShY,L/L):-var(Y),!,X \ ==Y,Y=ShY.

occursNot( ,susp(E,Args,R,S),Z,[Z==susp(E,Args,R,S)|L]/L):-var(S),!.

occursNot(X,susp( , ,R, ),ShR,L/M):-!,occursNot(X,R,ShR,L/M).

occursNot(X,T,ShT,L/M):-T=..[Name|Args],lstOccursNot(X,Args,ShArgs,L/M),ShT=..[Name|ShArgs].

lstOccursNot( ,[ ],[ ],L/L).

lstOccursNot(X,[Ar|Rest],[ShAr|RSh],L/M):-occursNot(X,Ar,ShAr,L/L1),lstOccursNot(X,Rest,RSh,L1/M).

Cuadro 3.13: Occurs-check

suspendida y puede estar evaluada o no. En el primer caso, cuando la funcion no esta eva-luada, produce como cascara del termino una variable nueva Z (tercer argumento) y generala igualdad entre esta cascara y la llamada a la funcion. En la tercera clausula, cuandola funcion ya ha sido evaluada, simplemente hace una operacion de desreferenciacion, esdecir, hace el test sobre la f.n.c. a la que se evaluo la funcion en algun computo previo.

La ultima clausula corresponde al caso de un termino construido. Descompone eltermino12 para obtener el functor principal (nombre del sımbolo de constructora) y losargumentos. Despues hace el chequeo sobre los argumentos obteniendo la lista de cascarasde los mismos y actualizando la lista de igualdades pendientes. Para ello utiliza el pre-dicado lstOccursNot que hace el mismo chequeo que occursNot pero sobre una lista determinos en vez de uno solo y devuelve, como es natural, una lista de cascaras. Por ultimose reconstruye la cascara del termino original a partir de las obtenidas para los argumentosy el nombre de la constructora original.

Retomemos ahora el ejemplo anterior: al resolver la igualdad X == c(f(X)) y hacerel test de ocurrencia de la variable X sobre el termino c(f(X)), se obtiene la cascara c(Y )(Y variable nueva), y la igualdad pendiente f(X) == Y . Entonces el sistema hace launificacion X = c(Y ), con lo que la igualdad pendiente se convierte en f(c(Y )) == Y .Si f es la funcion identidad al resolver la igualdad anterior y evaluar f(c(Y )) obtenemosc(Y ) con lo que dicha igualdad se transforma en c(Y ) == Y . Esta restriccion falla alhacer el test de ocurrencia, con lo que obtenemos el efecto esperado. Este comportamientoes debido a la pereza del sistema que no evalua la llamada a la funcion hasta que esestrictamente necesario.

3.11.2. El estudio de la frontera

Ya explicamos al principio de la seccion que la frontera de dos terminos es un nuevotermino que “contiene” la parte construida comun de los dos originales. Sin embargo, comoya comentabamos lo que realmente nos interesa del computo de la frontera es anticipar el

12El predicado Prolog T = ..Ls descompone el termino T devolviendo en Ls una lista que tiene comocabeza el functor principal de T y como resto sus argumentos. Por ejemplo c(a, f(b), d(a)) = ..X producela ligadura X = [c, a, f(b), d(a)]. Su uso es reversible, es decir, Y = ..[c, a, f(b), d(a)] produce la ligaduraX = c(a, f(b), a)

Page 107: Un lenguaje lógico funcional con restricciones1

3.11. IGUALDAD ESTRICTA (==) 107

fallo con el mınimo coste cuando la restriccion de igualdad sea efectivamente insatisfactible.Con el mınimo coste en este contexto quiere decir sin evaluar ninguna llamada a funcion.

La idea anterior se puede ilustrar con el siguiente ejemplo: sea f un sımbolo de fun-cion y c un sımbolo de constructora, y supongamos que queremos resolver la igualdadc(f(1), 2) == c(0, 3). Si el sistema operase de forma “secuencial”, como la constructoramas externa es la misma en los dos terminos, lo que harıa serıa resolver las igualdades es-trictas entre los argumentos dos a dos y del primero al ultimo. En el ejemplo esto significaque primero resolverıa f(1) == 0, para lo cual tendrıa que evaluar la llamada a f . Si larestriccion anterior se resuelve con exito despues tendrıa que resolver 2 == 3 que obvia-mente es insatisfactible y fallarıa. La evaluacion de la llamada a f puede ser en general uncomputo costoso, que no es en absoluto necesario en este caso, porque la igualdad inicialfallara de todos modos. Es mas, el computo de f(1) puede no terminar (por ejemplo si festa definida por la unica regla f X = f X). Al hacer el estudio de la frontera el sistemava a advertir el conflicto de constructoras (entre los segundos argumentos) y fallara sinmas, con lo que se mejora el rendimiento y las propiedades de terminacion.

Con este ejemplo queda justificado el buen comportamiento del estudio de la fronte-ra en el caso de restricciones que presentan algun conflicto de constructoras y son portanto insatisfactibles. Sin embargo, es deseable que en el caso de igualdades que sı sonsatisfactibles, este computo no suponga un coste adicional. Por ejemplo, supongamos c, dsımbolos de constructora de aridades 1 y 0 respectivamente y f sımbolo de funcion de ari-dad 1, y supongamos que se debe resolver la restriccion c(c(c(c(f X)))) == c(c(c(c Y ))).El computo de la frontera hace el estudio estructural de los terminos (por descomposicion= ..) y no detecta ningun conflicto de constructoras ya que ambos terminos tienen fronterac(c(c(c Z))), por lo que ahora el sistema tendrıa que resolver efectivamente la restriccionde partida. En dicha resolucion necesariamente hay que hacer un recorrido de los terminospara llegar a la parte mas interna y plantear la igualdad f X == Y . Operando de estaforma se ha hecho la descomposicion de los terminos por duplicado. El mismo plante-amiento del problema sugiere la solucion: hacer un solo recorrido de los terminos en el quese estudia la existencia de la frontera y simultaneamente ir anotando las igualdades pen-dientes como se hacıa en el predicado occursNot. Si este estudio tiene exito ya se conocenlas igualdades que han de resolverse para resolver la igualdad original, en otro caso dichaigualdad es insatisfactible.

Especificacion del predicado eqFrontier:

eqFrontier(T1, T2, Ls1, Ls2) ⇔ existe la frontera de los terminos T1 y T2 y Ls1, Ls2son las listas (diferencia) de terminos entre cuyos elementos se deben resolver las igualdades(dos a dos) para satisfacer la igualdad T1 == T2.

El codigo Prolog correspondiente al predicado eqFrontier es el que aparece en la tabla3.14. En la primera clausula se estudia el caso de dos variables, que no tienen frontera ypor tanto se genera una igualdad pendiente en las listas diferencia. En la segunda clausula,se hace el test de constructora sobre los dos terminos obteniendo, si el test es positivo,el nombre y los argumentos de dichas constructoras. A continuacion se comprueba queambos sımbolos de constructora coinciden y se hace el estudio de la frontera sobre cadapar de argumentos de las constructoras de partida, mediante el predicado eqFrontierList,cuyo codigo aparece en la misma tabla.

Las clausulas tercera y cuarta tratan el caso de llamadas a funcion, que como siempre,aparecen en forma suspendida. Si la llamada ha sido evaluada se hace una desreferencia-cion, es decir, se estudia la frontera tomando la f.n.c. que ha resultado de la evaluacion.

Page 108: Un lenguaje lógico funcional con restricciones1

108 CAPITULO 3. MECANISMO DE COMPUTO

eqFrontier(X,Y,[X | L1]/L1,[Y | L2]/L2):- (var(X);var(Y)),!.

eqFrontier(X,Y,FX,FY) :-

constructor(X,NameX,ArgsX),constructor(Y,NameY,ArgsY),!,NameX==NameY,eqFrontierList(ArgsX,ArgsY,FX,FY).

eqFrontier(susp(Fun,Args,R,S),Y,FX,FY):-!,(

S==hnf,!,eqFrontier(R,Y,FX,FY);

FX = [susp(Fun,Args,R,S) | L1]/L1,FY = [Y|L2] / L2

).

eqFrontier(X,susp(Fun,Args,R,S),FX,FY):-!,(

S==hnf,!,eqFrontier(X,R,FX,FY);

FY = [susp(Fun,Args,R,S) | L1]/L1,FX = [X | L2] / L2

).

eqFrontierList([ ],[ ],L1/L1,L2/L2).

eqFrontierList([X | Xs],[Y | Ys],LX/MX,LY/MY):-eqFrontier(X,Y,LX/L1,LY/L2),eqFrontierList(Xs,Ys,L1/MX,L2/MY).

Cuadro 3.14: Frontera de dos terminos

Page 109: Un lenguaje lógico funcional con restricciones1

3.11. IGUALDAD ESTRICTA (==) 109

En caso contrario, se genera una nueva igualdad pendiente que se almacena en las listasdiferencia.

3.11.3. Ligadura de variables a f.n.c.’s (binding)

La resolucion de igualdades en algunos casos se reducira a una igualdad entre unavariable y una f.n.c., que resolvera el predicado binding, cuya especificacion es simple:

binding(X,H, Cin, Cout) ⇔ resuelve la igualdad estricta entre la variable X y la f.n.c.H, tomando como almacen de entrada Cin y devolviendo Cout como almacen de salida.

binding(X,Y,Cin,Cout):-var(Y),!,unifyVar(X,Y,Cin,Cout).

binding(X,Y,Cin,Cout):-!,occursNot(X,Y,ShY,Lst),extractCtr(X,Cin,Cout1,CX),X=ShY,propagate(ShY,CX,Cout1,Cout2),equalList(Lst,Cout2,Cout).

Cuadro 3.15: Igualdad estricta entre una variable y una f.n.c.

En el codigo, hay que tener en cuenta algunos detalles como se aprecia en la tabla 3.15.Si la f.n.c. es una variable, la igualdad se resuelve haciendo la unificacion de variablesmediante el predicado unifyV ar, para unir las restricciones asociadas a cada una. Encaso de ser una constructora, la operacion es algo mas compleja y es crıtico el orden en lasecuencia de operaciones: hay que hacer el test de ocurrencia, lo que produce a su vez unalista de igualdades Lst que habra que resolver al final; despues, de forma similar a lo quehacıamos en el predicado hnf hay que unificar la variable X con la forma normal ShY(el esqueleto de la f.n.c. Y ), pero antes hay que hacer la extraccion de las desigualdadesasociadas a X; despues hay que propagar las restricciones y queda, por ultimo, resolver lalista de igualdades producidas por el test de ocurrencia. Esta resolucion se hace medianteel predicado equalList cuya especificacion es:

equalList(Lst1/Lst2, Cin, Cout) ⇔ Lst1 y Lst2 son listas de expresiones de la mismalongitud y todas las igualdades resultantes de emparejar los elementos de ambas son ciertas,tomando como almacen de entrada Cin y almacen de salida Cout.

El codigo se presenta en la tabla 3.16 y lo que hace es simplemente llamar al predicadoequal con cada uno de los pares.

Veamos mediante un ejemplo como operan en secuencia los predicado que acabamosde describir. Sean zero y suc las constructoras de naturales y add1 una funcion definidapor la regla:

add1 X = suc X

Supongamos que queremos resolver el objetivo X /= suc zero, X == suc (add1 Y ). Trasresolver la primera restriccion, el almacen de desigualdades sera [X : [suc(zero)]]. La igual-dad, tras algunos pasos de computo producira la llamada

Page 110: Un lenguaje lógico funcional con restricciones1

110 CAPITULO 3. MECANISMO DE COMPUTO

equalList([ ]/[ ],Cin,Cin):-!.

equalList([(Z==Y) | L]/M,Cin,Cout):-equal(Z,Y,Cin,Cout1),equalList(L/M,Cout1,Cout).

Cuadro 3.16: Resolucion de listas de igualdades

binding(X, c(susp(add1, [Y ], R, S)), [X : [suc(zero)]], Cout) que se resolvera por la segun-da clausula de este modo:

1. la llamada a occursNot tiene exito produciendo la cascara ShY = suc(Z) y la listade igualdades pendientes Lst = [Z == susp(add1, [Y ], R, S)],

2. extractCtr deja el almacen de restricciones vacıo y CX = suc(zero),

3. se hace la ligadura X = suc(Z),

4. la propagacion de restricciones genera la desigualdad Z /= zero, que en el almacenqueda Cout2 = [Z : [zero]],

5. por ultimo, se procede a la resolucion de la igualdad de Lst que quedo pendienteZ == susp(add1, [Y ], R, S). Esto se hara mediante la llamadaequal(Z, susp(add1, [Y ], R, S), [Z : [zero]], Cout).

3.11.4. Igualdad estricta entre formas normales de cabeza (equalHnf)

Las igualdades se reduciran, en algunos casos, a igualdades entre f.n.c.’s, para las queT OY utiliza el predicado equalHnf . La especificacion de este predicado es sencillamente:

equalHnf(X, Y,Cin,Cout) ⇔ la igualdad estricta entre las f.n.c.’s X e Y es satisfactibletomando como almacen de entrada Cin, y Cout es el almacen que se obtiene al resolver dichaigualdad.

equalHnf(L,R,Cin,Cout):-var(L),!,binding(L,R,Cin,Cout).

equalHnf(R,L,Cin,Cout):-var(L),!,binding(L,R,Cin,Cout).

equalHnf(R,L,Cin,Cout):-eqFrontier(R,L,FR/[ ],FL/[ ]),!,equalList(FR,FL,Cin,Cout).

Cuadro 3.17: Igualdad entre f.n.c.’s

En la tabla 3.17 se muestra el codigo correspondiente. Las dos primeras clausulas estu-dian la posibilidad de que alguno de los miembros sea variable, en cuyo caso se utilizara elpredicado binding. En otro caso, si ninguna es variable, deben comenzar por sımbolos deconstructora, por lo que se lleva a cabo el estudio de la frontera. Este estudio puede pro-vocar fallo, con lo que la igualdad no es satisfactible, o bien, tener exito devolviendo unalista de igualdades pendientes. Estas igualdades se resuelven con el predicado equalList,que a diferencia del que se presento en 3.11.3 ahora tiene cuatro argumentos (no utilizalistas diferencia), aunque la funcionalidad es basicamente la misma. El codigo se muestraen 3.18.

Page 111: Un lenguaje lógico funcional con restricciones1

3.11. IGUALDAD ESTRICTA (==) 111

equalList([ ],[ ],Cin,Cin):-!.

equalList([Ar1 | R1],[Ar2 | R2],Cin,Cout):-equal(Ar1,Ar2,Cin,Cout1),equalList(R1,R2,Cout1,Cout).

Cuadro 3.18: Resolucion de listas de igualdades

3.11.5. El predicado equal

Ahora estamos en disposicion de estudiar en detalle el predicado generico equal de re-solucion de igualdades entre expresiones cualesquiera. Una primera idea para resolver unaigualdad entre dos expresiones serıa reducir ambas a f.n.c. y despues utilizar el predicadoequalHnf tal y como se muestra en la tabla 3.19.

equal(X,Y,Cin,Cout):-hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),equalHnf(HX,HY,Cout,Cout).

Cuadro 3.19: Version Ingenua de Igualdad Estricta

Esta forma de operar es correcta, sin embargo puede hacerse bastante mas eficiente.T OY utiliza una version mas sofisticada que analiza la estructura de los miembros “insitu” para orientar el computo y reducir el espacio de busqueda. Ademas es capaz deorientar determinados reducciones, es decir, a la hora de evaluar una llamada a una funcionse fuerza la forma del resultado que debe devolver, como veremos a continuacion. El codigopara equal se muestra en la tabla 3.20.

Las dos primeras clausulas estudian el caso de que uno de los miembros sea una variable,en cuyo caso, evalua una f.n.c. para el otro y se invoca al predicado equalHnf . Si uno delos miembros es una variable, podrıa utilizarse binding directamente (vease 3.11.3), perola evaluacion de una f.n.c. para el otro puede ligar esa variable, con lo que, dejara de servariable. Por ejemplo, si tenemos la funcion f definida por la regla f 0 = 0 e intentamosresolver la restriccion X == f X, el sistema utilizara la primera clausula de equal ycalculara una f.n.c. para f X, que sera 0; pero ademas la variable X se liga al valor 0, conlo que la igualdad a resolver ahora es 0 == 0 y no se puede utilizar el predicado binding. Delo que sı estamos seguros es de que ambos miembros ahora estan en f.n.c. y podemos utilizarequalHnf . En muchas ocasiones, sin embargo, si un miembro es variable, la reduccion af.n.c. del otro miembro dejara la variable intacta, por lo que en las dos primeras clausulasde equal llaman a equalHnf con la “variable potencial” como primer argumento. Deeste modo, si efectivamente se tiene una variable, equalHnf llamara automaticamente abinding (vease 3.11.4).

Las dos clausulas siguientes para equal tratan el caso de que uno de los miembros seauna constructora. En este caso se construye una nueva expresion con el mismo nombrede constructora y con variables nuevas como argumentos, es decir, se imita la estructuraexterna de la constructora inicial. Despues se hace una reduccion orientada a f.n.c. del otromiembro: se solicita la evaluacion a f.n.c. forzando la forma del resultado. Esta orientacionsirve para anticipar un posible fallo (se poda el arbol de busqueda) con lo que se incrementala eficiencia; ademas mejora las propiedades de terminacion. Despues de esto se tiene

Page 112: Un lenguaje lógico funcional con restricciones1

112 CAPITULO 3. MECANISMO DE COMPUTO

equal(L,R,Cin,Cout):-var(L),!,hnf(R,HR,Cin,Cout1),equalHnf(L,HR,Cout1,Cout).

equal(R,L,Cin,Cout):-var(L),!,hnf(R,HR,Cin,Cout1),equalHnf(L,HR,Cout1,Cout).

equal(L,R,Cin,Cout):-constructor(L,C/N),!,functor(T,C,N),hnf(R,T,Cin,Cout1),eqFrontier(L,T,FL/[ ],FR/[ ]),equalList(FL,FR,Cout1,Cout).

equal(R,L,Cin,Cout):-constructor(L,C/N),!,functor(T,C,N),hnf(R,T,Cin,Cout1),eqFrontier(L,T,FL/[ ],FR/[ ]),equalList(FL,FR,Cout1,Cout).

equal(susp( , ,R,S),L,Cin,Cout):-S==hnf,!,equal(R,L,Cin,Cout).

equal(L,susp( , ,R,S),Cin,Cout):-S==hnf,!,equal(R,L,Cin,Cout).

equal(L,R,Cin,Cout):-hnf(L,HL,Cin,Cout1),

equal(HL,R,Cout1,Cout).

Cuadro 3.20: Igualdad Estricta

Page 113: Un lenguaje lógico funcional con restricciones1

3.12. RESTRICCIONES DE DESIGUALDAD (NOTEQUAL) 113

la certeza de que ambos miembros comienzan por constructora: uno de ellos ya tenıaesta forma y para el otro se ha forzado en la reduccion. Ademas las constructoras masexternas deben coincidir en ambos miembros, pero de las internas aun no conocemosnada. Nuevamente se puede intentar anticipar un fallo calculando la frontera (3.11.2). Sital fallo no se produce, el computo continua resolviendo la lista de igualdades que hadejado pendientes el estudio de la frontera mediante el predicado equalList que vimos en3.11.3.

Veamos con un ejemplo como funciona la orientacion. Supongamos el and logico (pa-ralelo) definido del modo siguiente:

and 0 X = 0and X 0 = 0and 1 1 = 1

Si se quiere resolver la restriccion 1 == and X Y , el sistema utilizara la terceraclausula de equal, y calculara una f.n.c. para and X Y orientada a 1, que producira unallamada a and con el resultado instanciado a 1. Entonces en el codigo generado paraand (optimizacion de subida de constructoras, 3.10.5), T OY descartara automaticamentelas dos primeras reglas de and ya que el resultado que devuelven no se ajusta al que seespera y utilizara directamente la tercera (instanciando X e Y a 1). Esta poda del arbol debusqueda llega al extremo cuando se intenta resolver una restriccion como and X Y == 2.En este caso el computo falla sin intentar ninguna regla para and.

Para ver por que mejoran las propiedades de terminacion consideremos la funcion fdefinida por la regla f 0 = 0 y otra funcion loop cuya evaluacion produce no terminacion(la definicion mas sencilla para loop es loop = loop). La restriccion f loop == 1 produceun fallo automatico debido a la orientacion en el computo de una forma normal paraf , mientras que sin esta optimizacion se producirıa no terminacion (recursion infinita alintentar reducir loop).

Las dos clausulas siguientes (quinta y sexta) estudian el caso de que uno de los miem-bros sea una suspension evaluada (una f.n.c. “escondida”). Si es ası hace una desreferen-ciacion, para acceder a la variable o constructora que se obtuvo de la evaluacion y se llamarecursivamente a equal. Esta nueva llamada se resolvera necesariamente por uno de lasclausulas vistas hasta ahora (no hay nada que evaluar).

La ultima clausula, por exclusion (notese que las anteriores tienen todas el predicadode corte !) trata el caso de suspensiones no evaluadas. Entonces se reduce una de ellasa f.n.c. y se llama recursivamente a equal. Esta nueva igualdad se resolvera por una delas clausulas anteriores. En realidad, tenemos la seguridad de que ambos miembros sonsuspensiones no evaluadas y podrıamos reducir ambas a f.n.c. antes de la llamada recursiva,pero no es necesario13.

3.12. Restricciones de desigualdad (notEqual)

Para la resolucion de desigualdades T OY incorpora el predicado generico notEqual.Este predicado utilizara a su vez otros especializados en determinados tipos de desigualdad.En la exposicion de la igualdad hemos comenzado explorando los casos particulares y las

13Puede darse el caso de que ambas suspensiones sean la misma, con lo que la reduccion de una provocaautomaticamente la reduccion de la otra.

Page 114: Un lenguaje lógico funcional con restricciones1

114 CAPITULO 3. MECANISMO DE COMPUTO

operaciones auxiliares, para terminar con el caso general. Para la desigualdad, por elcontrario, la exposicion sera mas clara comenzando directamente por el caso general.

La especificacion del predicado notEqual es:

notEqual(X, Y,Cin,Cout) ⇔ resuelve la desigualdad entre dos expresiones cualesquieraX y Y tomando como almacen de entrada Cin y devolviendo como almacen de salida Cout.

A diferencia de la igualdad, en la que es posible hacer un analisis sobre la estructurade los terminos que evite reducciones innecesarias, ahora dicho analisis no ofrece ventajasclaras. Por ello, lo primero que haremos con una desigualdad es evaluar ambos miembrosa f.n.c. y utilizar el predicado equalHnf como se muestra en la tabla 3.21.

notEqual(X,Y,Cin,Cout):-hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),notEqualHnf(HX,HY,Cout2,Cout).

Cuadro 3.21: Resolucion de desigualdades

La especificacion de notEqualHnf es:

notEqualHnf(X, Y,Cin, Cout) ⇔ resuelve la desigualdad entre dos dos f.n.c.’s X y Y ,tomando como almacen de entrada Cin y devolviendo como almacen de salida Cout.

notEqualHnf(X,Y,Cin,Cout):-var(X),!,notEqualVar(X,Y,Cin,Cout).

notEqualHnf(Y,X,Cin,Cout):-var(X),!,notEqualVar(X,Y,Cin,Cout).

notEqualHnf(R,L,Cin,Cout):-eqFrontier(R,L,FR/[ ],FL/[ ]),!,notEqualList(FR,FL,Cin,Cout)

;Cin=Cout.

Cuadro 3.22: Resolucion de desigualdades

Y el codigo se muestra en la tabla 3.2214. Ahora sı se hace un estudio de los miembros.Si alguno de ellos es una variable (dos primeras clausulas) se utiliza el predicado especia-lizado notEqualV ar (que veremos mas adelante). En otro caso (ultima clausula), ambosseran constructoras y ahora hacemos el analisis de la frontera no con el fin de anticiparel fallo, sino para anticipar el exito: si se encuentra alguna colision de constructoras (se-gunda parte de la disyuncion), la desigualdad se satisface automaticamente y el almacenpermanece invariante. Si no hay tal colision, la desigualdad debe resolverse utilizando lalista de restricciones que han quedado pendientes, mediante el predicado notEqualList.Observese que ahora interpretamos las restricciones pendientes de la frontera, no comoigualdades, sino como desigualdades. Si en la igualdad tenıamos que resolver todas lasigualdades pendientes, ahora solo tenemos que resolver alguna desigualdad para satisfacerla desigualdad original y esto es lo que hace notEqualList.

14En 3.15 se introducira una clausula adicional para este predicado, especıfica para el tratamiento dedesigualdades entre reales. Por el momento podemos obviar este hecho.

Page 115: Un lenguaje lógico funcional con restricciones1

3.12. RESTRICCIONES DE DESIGUALDAD (NOTEQUAL) 115

La especificacion de notEqualList es:

notEqualList(Lst1, Lst2, Cin,Cout) ⇔ Lst1 y Lst2 son listas de expresiones de la mis-ma longitud y existe alguna desigualdad cierta entre las que resultan de emparejar los elementosde ambas, tomando Cin como almacen de entrada Cin y almacen de salida Cout.

Esta operacion es indeterminista como muestra la especificacion cuando decimos “exis-te alguna”. El codigo es una simple clausula disyuntiva que se muestra en la tabla 3.23. Lalectura podrıa ser: para resolver una desigualdad entre dos listas resolver la desigualdadentre el primer par de elementos, o bien, resolver la desigualdad entre los restos de laslistas. Notese que no hay una clausula que recoja el caso de listas vacıas, como es natural.

notEqualList([X|R1],[Y|R2],Cin,Cout):-notEqual(X,Y,Cin,Cout)

;notEqualList(R1,R2,Cin,Cout).

Cuadro 3.23: Eleccion indeterminista de una desigualdad

El predicado notEqualV ar se especifica como:

notEqualV ar(X, Y, Cin, Cout) ⇔ resuelve la desigualdad entre la variable X y la f.n.c.Y tomando Cin y Cout como almacenes de entrada y salida respectivamente.

notEqualVar(X,Y,Cin,Cout):-var(Y),!,X \ ==Y,addCtr(X,Y,Cin,Cout1),addCtr(Y,X,Cout1,Cout).

notEqualVar(X,true,Cin,Cout):-!,hnf(X,false,Cin,Cout).

notEqualVar(X,false,Cin,Cout):-!,hnf(X,true,Cin,Cout).

notEqualVar(X,Y,Cin,Cout):-occursNot(X,Y,ShY,Lst),!,contNotEqual(X,Y,ShY,Lst,Cin,Cout).

notEqualVar( , ,Cin,Cin).

Cuadro 3.24: Eleccion indeterminista de una desigualdad

El codigo se muestra en la tabla 3.24 y en este caso son crıticos tanto los cortes queaparecen, como el orden de las clausulas. En la primera clausula, si ambos miembros sonvariables y son distintas (si son la misma la restriccion es insatisfactible), entonces bastacon anadir la restriccion al almacen de restricciones con la operacion addCtr (3.8.1). Segunse explico en 3.12, debido a la gestion de los almacenes se debe anadir la restriccion sobreambas variables (de ahı las dos llamadas a addCtr).

Las dos clausulas siguientes representan la excepcion sobre el tipo de los booleanos quese vio en 3.8.1: si una variable booleana X es distinta de true entonces debe ser false yviceversa. Para hacer la unificacion T OY se utiliza el predicado hnf (la segunda parte de

Page 116: Un lenguaje lógico funcional con restricciones1

116 CAPITULO 3. MECANISMO DE COMPUTO

la disyuncion de la primera clausula) para forzar la propagacion de restricciones (tambienpodrıa utilizarse unifyHnfs).

En la siguiente clausula se resuelve una desigualdad entre una variable X y una ex-presion que comienza por constructora (distinta de true y false). En este caso se utilizaoccursNot con un doble fin: para el occurs-check y para descubrir si el segundo miembroesta en forma normal o contiene llamadas a funcion. Si el occurs-check falla, entonces ladesigualdad se satisface automaticamente. Por ejemplo, la desigualdad X /= suc X se sa-tisface automaticamente por este hecho. En otro caso, se llama al predicado contNotEqual,que hace uso de la informacion que ha producido occursNot para descubrir si el segundomiembro es o no una forma normal.

La especificacion de contNotEqual es:

contNotEqual(X, Y, ShY, Lst, Cin, Cout) ⇔ resuelve la desigualdad entre la variable Xy la expresion Y que comienza por constructora, utilizando la cascara ShY de Y y la lista derestricciones pendientes Lst y tomando como almacenes Cin y Cout.

contNotEqual(X, ,ShY,[ ]/[ ],Cin,Cout):-!,addCtr(X,ShY,Cin,Cout).

contNotEqual(X,Y, , ,Cin,Cout):-constructor(Y,C/ ,ArgsY),!,const(C, , ,Dest),(

genConstructor(Dest,Z,C1, ),C \ ==C1,hnf(X,Z,Cin,Cout)

;genConstructor(Dest,Z,C,ArgsZ),hnf(X,Z,Cin,Cout1), notEqualList(ArgsZ,ArgsY,Cout1,Cout)

).

Cuadro 3.25: Continuacion de la resolucion de desigualdades

En el codigo de la tabla 3.25, la primera clausula estudia el caso en el que la lista(diferencia) de restricciones pendientes que se ha generado en el occurs-check es vacıa.Esto significa que el segundo miembro no contiene llamadas a funcion y por tanto es unaforma normal. Entonces la desigualdad ya esta en forma resuelta y solo hay que anadirlaal almacen de restricciones.

En la segunda clausula, cuando el segundo miembro no esta en forma normal, la cascaray la lista de restricciones pendientes no seran necesarios. Por ejemplo, la desigualdadX /= [3 + 4], tras varios pasos de computo debera ser resuelta en esta clausula. Unaprimera idea es evaluar una forma normal para el segundo miembro que sera [7], con loque la desigualdad toma la forma resuelta X /= [7]. Sin embargo, esta forma de procederexige la evaluacion de todas las llamadas a funcion de este segundo miembro, que, engeneral pueden ser costosas (o incluso no terminar). Notese, por otro lado, que puedenencontrarse algunas respuestas sin evaluar ninguna llamada a funcion. Por ejemplo larespuesta X /= [ ], con seguridad es correcta. En general, se puede ligar la variable delprimer miembro a cualquier constructora distinta, pero del mismo tipo que la del segundo

Page 117: Un lenguaje lógico funcional con restricciones1

3.12. RESTRICCIONES DE DESIGUALDAD (NOTEQUAL) 117

argumento. Para cubrir el resto de posibilidades, tambien se puede ligar la variable a lamisma constructora que la del segundo miembro y plantear una desigualdad entre algunode sus argumentos. Esta es la idea del funcionamiento de este predicado. Veamos primerocomo se generan las constructoras.

Para generar constructoras del mismo tipo se utiliza el predicado genConstructorespecificado como:

genConstructor(DestType, Cons,Name, Args) ⇔ Cons es una expresion de tipo Dest-Type construida con el nombre de constructora Name y Args es la lista de argumentos(variables nuevas) utilizados para su construccion.

El codigo de genConstructor se muestra en la tabla 3.26. En los hechos const sealmaceno en tiempo de compilacion toda la informacion referente a constructoras (3.5.2).Ahora podemos recuperar el nombre y la aridad de una constructora de un tipo dado,con los que construimos la expresion Cons utilizando el predicado functor de Prolog. Losargumentos de Cons son variables nuevas que podemos recuperar en el argumento Argsmediante el predicado Prolog de descomposicion = ...

genConstructor(TipDest,Cons,Name,Args):-const(Name,Ar, ,TipDest),functor(Cons,Name,Ar),Cons=..[Name|Args].

Cuadro 3.26: Generacion de una expresion construida de un tipo dado

Volviendo a la segunda clausula de contNotEqual, lo primero que se hace es una lla-mada a constructor (3.5.2) para recuperar el nombre y los argumentos de la expresionconstruida del segundo miembro. A continuacion se determina el tipo de esta construc-tora mediante una consulta al hecho const que tiene asociado (3.5.2). Y despues, se abreuna disyuncion que cubre los posibles modos de resolver la desigualdad: el primero esgenerar una constructora del mismo tipo, pero con distinto nombre, que se ligara a la va-riable del primer miembro por medio de hnf para hacer la propagacion (puede utilizarseunifyHnfs); el segundo modo es generar una constructora del mismo nombre, ligarla ala variable del primer miembro y, a continuacion, plantear una desigualdad entre algunapareja de argumentos de ambas constructoras (la del segundo miembro y la generada),mediante el notEqualList (3.12).

En el ejemplo que planteabamos X /= [3 + 4], la constructora del segundo miembro es’:’/2 y los argumentos 3+4 y [ ]. Por la primera parte de la disyuncion, la unica constructoradistinta de ’:’, pero del mismo tipo, es [ ], por lo que la primera respuesta del sistema esX == [ ]. La segunda parte de la disyuncion, al resolver las dos desigualdades entre losdos pares de argumentos produce las respuestas X == [A|B] con B /= 7 (cualquier listano vacıa cuya cabeza sea distinta de 7), y X == [A|B] con B /= [ ] (cualquier lista con2 o mas elementos). Estas tres soluciones cubren la respuesta X /= [7] y se han generadosiguiendo la filosofıa de la pereza que mantiene el sistema, es decir, intentando hacer elmenor numero de reducciones para obtener cada respuesta.

Es facil ver que este mecanismo afecta a las propiedades de terminacion del sistema.Por ejemplo, con la funcion from definida como from X = [X|from (X +1)], el objetivoY /= from 0 producira infinitas respuestas (Y = [ ]; Y = [A|B] con B /= 0; Y = [A]...).Si se intentase reducir a forma normal el segundo miembro se producirıa no terminacionsin obtener ninguna respuesta.

Page 118: Un lenguaje lógico funcional con restricciones1

118 CAPITULO 3. MECANISMO DE COMPUTO

3.12.1. Restricciones de desigualdad entre formas normales

En 3.9 se planteo la conveniencia de disponer de un predicado especıfico para resolverdesigualdades entre formas normales y este es el cometido de notEqualTerm. La especi-ficacion es la siguiente:

notEqualTerm(T1, T2, Cin, Cout) ⇔ resuelve la desigualdad entre dos formas normalesT1 y T2 tomando como almacen de entrada Cin y devolviendo como almacen de salida Cout.

El codigo se muestra en la tabla 3.27. Una forma normal puede ser una variable oun termino que comienza por constructora. Las dos primeras clausulas distinguen el ca-so de que alguno de los miembros sea una variable, en cuyo caso se utiliza el predicadoespecializado notEqualV arTerm (observese que en las llamadas a notEqualV arTerm elprimer argumento es siempre una variable), que veremos en breve. En la tercera clausulase tiene la seguridad de que ambos miembros comienzan por un sımbolo de constructo-ra. Entonces se utiliza el predicado constructor (3.5.2) para descubrir el nombre, aridady los argumentos de las constructoras. Si el nombre o la aridad no coinciden (primeraparte de la disyuncion), la desigualdad tiene exito automaticamente y deja intactos losalmacenes de restricciones. En otro caso, utiliza el predicado notEqualTermList para re-solver la desigualdad entre alguna pareja de argumentos de las constructoras, de maneraindeterminista.

notEqualTerm(T1,T2,Cin,Cout):-var(T1),!,notEqualVarTerm(T1,T2,Cin,Cout).

notEqualTerm(T1,T2,Cin,Cout):-var(T2),!,notEqualVarTerm(T2,T1,Cin,Cout).

notEqualTerm(T1,T2,Cin,Cout):-constructor(T1,C1/A1,Args1),constructor(T2,C2/A2,Args2),(

C1/A1 \ ==C2/A2,!,Cout=Cin;

notEqualTermList(Args1,Args2,Cin,Cout)).

notEqualVarTerm(X,Y,Cin,Cout):-var(Y),!,X \ ==Y,addCtr(X,Y,Cin,Cout1),addCtr(Y,X,Cout1,Cout).

notEqualVarTerm(X,Y,Cin,Cout):-!,addCtr(X,Y,Cin,Cout).

notEqualTermList([X | R1],[Y | R2],Cin,Cout):-(

notEqualTerm(X,Y,Cin,Cout);

notEqualTermList(R1,R2,Cin,Cout)).

Cuadro 3.27: Desigualdad entre formas normales

El predicado notEqualV arTerm anade una nueva restriccion al almacen utilizandoaddCtr (3.8.1). En la primera clausula, si ambos miembros de la desigualdad son variablesdistintas, por nuestro modo de almacenamiento debemos anadir la desigualdad asociandola

Page 119: Un lenguaje lógico funcional con restricciones1

3.13. LA FUNCION IGUALDAD (EQFUN) 119

a ambas variables. Si las variables fuesen identicas, se produce un fallo que se propaga anotEqualTerm, ya que la desigualdad, obviamente no es cierta. La segunda clausula, enel caso de que una sea variable y la otra un termino, simplemente anade la restriccion alalmacen.

3.13. La funcion igualdad (eqFun)

En 2.3.15 ya se justifico la existencia de las funciones igualdad y desigualdad. Aunqueambas pueden definirse como funciones T OY, se implementan a bajo nivel para conse-guir algunas optimizaciones. En el caso de la funcion igualdad, la definicion en sintaxisT OY que se propuso en 2.3.15 era:

X == Y = true <== X == YX == Y = false <== X /= Y

De acuerdo con esta definicion la traduccion que producirıa el sistema tendrıa el aspecto(omitimos los almacenes de restricciones):

== (X, Y, true) : −equal(X, Y ).== (X, Y, false) : −notEqual(X,Y ).

Consideremos el objetivo (2+2==3+4)==B. Por el contexto, el sistema interpreta que elprimer sımbolo de ‘==’ es una llamada a la funcion igualdad, mientras que el segundo esuna restriccion. Entonces generarıa la llamada == (2+2, 3+4, B). Por la primera regla de‘==’ se unificarıa B con true y se harıa la llamada equal(2 + 2, 3 + 4). El predicado equalreducirıa a f.n.c. ambos argumentos (en este caso una f.n.c. es tambien una forma normal),con lo que se plantea la restriccion 4 == 7, que produce un fallo. Entonces el sistemaprobara con la segunda regla, que unificara B con false y llamara a notEqual(2+2, 3+4).El predicado notEqual nuevamente debe reducir las expresiones 2+2 y 3+4 y ahora obtienela respuesta B == false. El computo descrito es correcto, pero tiene el inconveniente deque las expresiones 2 + 2 y 3 + 4 se evaluan dos veces.

Evitar todo tipo de reevaluacion en el sistema es una tarea compleja, y por otro lado, nohay garantıas de que la eficiencia del mismo fuese superior, ya que los propios mecanismospara conseguirlo tendrıan un coste computacional. Sin embargo, algunas reevaluacionescomo las del ejemplo anterior pueden evitarse con un coste relativamente pequeno. Elcodigo que vamos a presentar para la funcion igualdad, en el caso de que los argumentossean llamadas a funcion, evaluara ambas llamadas antes de continuar el computo (sininstanciar la variable B, en nuestro ejemplo). Los resultados obtenidos de estas reduccionespodran utilizarse para completar el computo de evaluacion de la funcion igualdad. Laespecificacion de la funcion igualdad es la siguiente:

eqFun(X, Y,H, Cin, Cout) ⇔ H es true si la restriccion X == Y es cierta y false si larestriccion es falsa15, tomando como almacenes Cin y Cout.

El codigo se presenta en la tabla 3.28. Las dos primeras clausulas operan cuando elresultado viene dado, en cuyo caso no hay inconveniente en resolver las restricciones co-rrespondientes. De hecho en este caso, el computo es equivalente al que se harıa utilizandola definicion T OY que proponıamos al principio. El resto de clausulas cubren el casocomplementario, cuando el resultado sea variable.

15Las funciones en T OY pueden producir fallo por lo que true y false no son los unicos posiblesresultados de una llamada a la funcion eqFun (puede quedar indefinida).

Page 120: Un lenguaje lógico funcional con restricciones1

120 CAPITULO 3. MECANISMO DE COMPUTO

eqFun(X,Y,H,Cin,Cout):-H==true,!,equal(X,Y,Cin,Cout).

eqFun(X,Y,H,Cin,Cout):-H==false,!,notEqual(X,Y,Cin,Cout).

eqFun(X,Y,H,Cin,Cout):-var(X),!,(

H=true,equal(X,Y,Cin,Cout);

H=false,notEqual(X,Y,Cin,Cout)).

eqFun(X,Y,H,Cin,Cout):-var(Y),!,(

H=true,equal(X,Y,Cin,Cout);

H=false,notEqual(Y,X,Cin,Cout)).

eqFun(X,Y,H,Cin,Cout):-hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),eqFunHnf(HX,HY,H,Cout2,Cout).

Cuadro 3.28: Funcion igualdad

Las clausulas tercera y cuarta son operativas cuando uno de los argumentos es variable,en cuyo caso se estudian los dos posibles resultados de la funcion mediante una disyuncion.En estas dos clausulas el computo vuelve a ser equivalente al que se harıa utilizando ladefinicion T OY de la funcion igualdad. Notese que primero se considera el caso de quela funcion se evalue a true por ser mas natural; no obstante, serıa igualmente correctoestudiar primero el caso complementario.

La ultima clausula es la que trata el caso de que ninguno de los argumentos sea variable(pueden ser llamadas a funcion o expresiones que comienzan por un sımbolo de construc-tora). En particular, pueden ser ambos llamadas a funcion y es aquı cuando se intentanevitar las reevaluaciones. Lo primero es reducir ambos miembros a f.n.c. (si alguno deellos comienza por constructora, hnf lo dejara intacto por la tercera clausula). Despues sellama al predicado eqFunHnf cuya especificacion es igual que la de eqFun excepto quesolo opera con f.n.c.’s.

El codigo de eqFunHnf se presenta en la tabla 3.29. Una llamada a funcion puedereducirse a una variable y la primera clausula considera este caso, en el que, nuevamentedebe ser eqFun el que opere. Ademas, es posible que la variable resultado H se hayainstanciado en alguna de las reducciones que hace eqFun, por lo que no podemos asegurarque sea una de las clausulas tercera o cuarta la que continue el computo, sino que puedeser alguna de las dos primeras.

En la segunda clausula de eqFunHnf ya tenemos la garantıa de que ambos miembrosson expresiones que comienzan por constructora. Entonces hacemos el estudio de la fron-tera. La segunda parte de la disyuncion actua si (notese el corte tras el computo de lafrontera) hay colision de constructoras (falla el calculo de la frontera), entonces la funcion

Page 121: Un lenguaje lógico funcional con restricciones1

3.13. LA FUNCION IGUALDAD (EQFUN) 121

eqFunHnf(X,Y,H,Cin,Cout):-(var(X);var(Y)),!,eqFun(X,Y,H,Cin,Cout).

eqFunHnf(X,Y,H,Cin,Cout):-eqFrontier(X,Y,FrontierX/[ ],FrontierY/[ ]),!,eqFunAnd(FrontierX,FrontierY,H,Cin,Cout)

;H=false,Cin=Cout.

Cuadro 3.29: Igualdad de f.n.c.’s

se evalua a false dejando intactos los almacenes. En el caso de que haya frontera comun,se hace una eleccion indeterminista que responde al siguiente enunciado: “una conjuncionde igualdades se evalua a true si todas ellas se evaluan a true y a false si alguna de ellas seevalua a false”. El predicado eqFunAnd es el que implementa esta idea. Su especificaciones:

eqFunAnd(Lst1, Lst2,H, Cin, Cout) ⇔ Lst1 y Lst2 son listas de expresiones de lamisma longitud; H es true si todas las parejas de igualdades entre los elementos de ambaslistas se evaluan a true y false si alguna de ellas se evalua a false.

eqFunAnd([ ],[ ],true,Cin,Cin):-!.

eqFunAnd([X1 | Rest1],[Y1 | Rest2],H,Cin,Cout):-eqFun(X1,Y1,H1,Cin,Cout1),eqFunAnd 1(Rest1,Rest2,H1,H,Cout1,Cout).

eqFunAnd([ | Rest1],[ | Rest2],false,Cin,Cout):-notEqualList(Rest1,Rest2,Cin,Cout).

eqFunAnd 1( , ,false,false,Cin,Cin).

eqFunAnd 1(Rest1,Rest2,true,true,Cin,Cout):-equalList(Rest1,Rest2,Cin,Cout).

Cuadro 3.30: Conjuncion de igualdades.

En el codigo, que se presenta en la tabla 3.30 se ha puesto especial cuidado en cubrirtodas las posibilidades sin redundancias. La primera clausula, cuando ambas listas sonvacıas, devuelve true automaticamente. En la segunda clausula, lo primero que hacemoses evaluar la igualdad entre la primera pareja de argumentos utilizando el predicado eqFun.A continuacion, eqFunAnd 1 hace una distincion de casos sobre el resultado. Si ha sidofalse, entonces el resultado global es false sin necesidad de estudiar el resto de pares. Siha sido true, entonces se intentan resolver las igualdades entre el resto de parejas paraobtener como resultado final true, utilizando el predicado equalList (3.11.3), con lo quese cubre el caso de que todas las igualdades de la conjuncion se evaluen a true.

El caso que queda por estudiar es que la igualdad entre alguna de las parejas restantes(todas menos la primera) se evalue a false, en cuyo caso la conjuncion serıa false. Y este

Page 122: Un lenguaje lógico funcional con restricciones1

122 CAPITULO 3. MECANISMO DE COMPUTO

caso es el que trata la tercera clausula de eqFunAnd, que utiliza el predicado notEqualList(3.12) sobre los restos de las listas.

Este mecanismo de evaluacion de conjunciones tambien sigue la filosofıa de la pereza ytiene buenas propiedades de terminacion. Por ejemplo, supongamos una funcion f definidapor una unica regla f 3 = 4 y el objetivo ((f 2, 0) == (3, 1)) == B. Para resolveresta restriccion debe evaluarse una llamada a la funcion igualdad con los argumentos(f 2, 0) y (3, 1), que tras varios pasos de computo se reducira a resolver la conjuncion(f 2 == 3) ∧ (0 == 1). La primera condicion de esta conjuncion produce un fallo en elcomputo, puesto que f 2 no esta definido (esto no quiere decir que la condicion sea false,sino que no puede evaluarse). Sin embargo, por la segunda clausula de eqFunAnd puedeevaluarse la segunda condicion a false y se computa la respuesta B == false.

Si el objetivo fuese ((f 2, 0) == (3, 0)) == B entonces si que se produce un fallo en elcomputo puesto que ahora el segundo elemento de la conjuncion 0 == 0 se evalua a true,pero no podemos decir nada del primero f 2 == 3 (es indefinido).

En el ejemplo que proponıamos al principio de la seccion (2 + 2 == 3 + 4) == Bcon el codigo que hemos presentado para evaluar la funcion igualdad, no se reevaluan lasexpresiones 2 + 2 y 3 + 4, ya que la ultima clausula de eqFun se encarga de reducirlasantes de instanciar la variable B. No obstante, en un objetivo como (X == 3 + 4) == Bse producira una primera respuesta B == true, X == 7 y, reevaluando la expresion3 + 4, se obtendra B == false con X /= 7. Aquı no ha evitado la reevaluacion porqueello supondrıa, en general, un analisis minucioso de la estructura de los argumentos queglobamente no repercutira de forma positiva en la eficiencia del sistema.

3.14. La funcion desigualdad (notEqFun)

La funcion desigualdad sigue la misma filosofıa que la de igualdad. De hecho su im-plementacion se apoya directamente en la de igualdad. La especificacion de notEqFunes:

notEqFun(X, Y,H, Cin, Cout) ⇔ H es false si la restriccion X == Y se evalua a truey H es false si la restriccion se evalua a true, tomando como almacenes Cin y Cout.

notEqFun(X,Y,H,Cin,Cout):- H==true, !, notEqual(X,Y,Cin,Cout).

notEqFun(X,Y,H,Cin,Cout):-H==false,!,equal(X,Y,Cin,Cout).

notEqFun(X,Y,H,Cin,Cout):- eqFun(X,Y,Z,Cin,Cout),negate(Z,H).

negate(true,false) :- !.

negate(false,true).

Cuadro 3.31: Funcion desigualdad

El codigo se presenta en la tabla 3.31. Las dos primeras clausulas son semejantes alas dos primeras de eqFun, excepto que ahora si el resultado de la funcion es true debesatisfacerse una desigualdad y si es false una igualdad.

La ultima clausula hace uso de la funcion de igualdad directamente, que se encargara dehacer las optimizaciones oportunas. Despues se niega el resultado (Z) que produce estaevaluacion mediante el predicado negate que se presenta en esta misma tabla. Notese

Page 123: Un lenguaje lógico funcional con restricciones1

3.15. LAS RESTRICCIONES SOBRE LOS NUMEROS REALES 123

que eqFun siempre devolvera uno de los valores true o false (si no se produce fallo),es decir, nunca devolvera una variable; por este motivo puede colocarse un corte en laprimera clausula de negate (en este caso, el corte no es indispensable y no supone unagran optimizacion).

3.15. Las restricciones sobre los numeros reales

La inclusion de restricciones sobre reales en el sistema ([AHL+96]) se hace de unaforma natural y es sencilla salvo por algunas cuestiones de caracter tecnico. Los proble-mas fundamentales son debidos a la carencia de algunas operaciones en el interface decomunicacion con el resolutor que ofrece Sisctus.

En Sicstus Prolog puede activarse el resolutor de restricciones sobre reales utilizandola directiva (vease [Hol95],[Gro96]):

use module(library(clpr)).

A partir de este momento Sicstus esta preparado para recibir y resolver restricciones.Para lanzar restricciones al sistema debe utilizarse una sintaxis especial (vease [Gro96]),aunque para la exposicion que vamos a hacer aquı bastara con conocer lo siguiente:

las restricciones deben ir siempre encerradas entre llaves (’{’ y ’}’) y separadas porcomas,

las ecuaciones utilizan ’=’ como sımbolo de igualdad,

las desigualdades se notaran con el sımbolo ’= \ =’

Por ejemplo, en Sicstus (con el resolutor cargado) puede resolverse el objetivo:

| ?- { X+Y=5, X=\=5 }.

que produce la respuesta:

{X=\=5.0},{Y=5.0-X}

Una de las ventajas de T OY es que, por comodidad para el ususario, no se utilizaninguna sintaxis especial para las restricciones: no utiliza llaves, las ecuaciones se planteancomo igualdades (con el sımbolo ’==’) y las desigualdades son como siempre (sımbolo’ /=’). El sistema se encargara de distinguir las restricciones numericas y de hacer lasllamadas pertinentes al resolutor utilizando su sintaxis. Todas estas llamadas se locali-zan en las primitivas aritmeticas del sistema y por ello el sistema cuenta con un juegode primitivas especiales para el manejo de restricciones, que se encuentran en el archivoprimitivesClpr.pl.

La activacion de restricciones sobre reales en T OY (mediante el comando /cflpr.)prepara al sistema del modo siguiente:

se carga en memoria el resolutor proporcionado por Sicstus (use module(library(clpr))),

tambien se carga el nuevo juego de primitivas (primitivesClpr.pl),

se activa un flag que informa al sistema de modo de uso en el que se encuentra,asertando el hecho clpr active (assert(clpr active).).

Page 124: Un lenguaje lógico funcional con restricciones1

124 CAPITULO 3. MECANISMO DE COMPUTO

Para entender el funcionamiento de las nuevas primitivas, vamos a estudiar los cambiosque han de hacerse en el codigo de la funcion ‘<’ (para el resto de funciones se hacencambios similares). El codigo de esta funcion, trabajando sin restricciones sobre reales yde acuedo con las ideas que expusimos en 3.10.6, tiene esta forma:

$<(X,Y,H,Cin,Cout):-hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> (HX<HY,H=true;HX>=HY,H=false); errPrim).

Este codigo se encuentra en el archivo primitives.pl y es el que se utiliza cuando el sistemano tiene activas las restricciones. El codigo de la funcion “menor” es simple: se evalua unaforma normal de cabeza para cada uno de los argumentos (que en este caso, seran formasnormales) y se comprueba si ambos resultados son numeros. Si alguno de ellos no es unnumero, no se puede evaluar la funcion sin el resolutor y se produce un error informandode este hecho. Si efectivamente son numeros, se comprueba si el primero es menor queel segundo, en cuyo caso la funcion devuelve true y si es mayor o igual que el segundodevuelve false.

Notese que el segundo caso (cuando la funcion devuelve false) no se resuelve porexclusion porque para ello habrıa que colocar un corte tras comprobar que se satisfacela primera alternativa. Despues de verificar que ambos son numeros el codigo tendrıaesta forma: (HX < HY, !, true; H = false). Pero este codigo es incorrecto debido alindeterminismo, ya que esta ignorando la posibilidad de que los argumentos tengan masde una posible reduccion a f.n.c.. Por ejemplo, la funcion coin (2.3.7) puede reducirse tantoa 0 como a 1 y en consecuencia, la expresion 0 < coin puede evaluarse tanto a true comoa false.

Con las restricciones aritmeticas activas, una vez calculada una f.n.c. para cada unode los argumentos, debe lanzarse al resolutor la restriccion correspondiente. Ası, el codigo(aun no definitivo) se transforma en:

$<(X,Y,H,Cin,Cout):-hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),(H=true,{HX<HY};H=false,{HX>=HY}).

Observese que ahora no se comprueba que las f.n.c.’s calculadas sean numeros (dehecho pueden ser variables). La llamada al resolutor se encargara de manejar la restricciona partir de este momento. Otro ejemplo puede ser la funcion ‘+’, que en primitivesClpr.plesta implementada como (codigo no definitivo):

+(X,Y,H,Cin,Cout):-hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),{H = HX + HY}.

Con estas modificacion habrıamos terminado si no fuese por un detalle: el sistema aho-ra cuenta con restricciones de desigualdad sobre los reales y restricciones de desigualdadestricta (las que ya tenıamos). Estos dos tipos de desigualdad son indistinguibles desde elpunto de vista sintactico (utilizan el mismo sımbolo /=); sin embargo, tienen un compor-tamiento distinto. Las de reales deben ser enviadas al resolutor, mientras que las otras songestionadas directamente por T OY (3.12).

Page 125: Un lenguaje lógico funcional con restricciones1

3.15. LAS RESTRICCIONES SOBRE LOS NUMEROS REALES 125

Para ilustrar el problema que se deriva de este solapamiento en las desigualdades, su-pongamos que lanzamos el objetivo X /= Y, X +Y == 2, X−Y == 0. La solucion (unica)a las dos ultimas restricciones es X == 1, Y == 1, pero por la primera restriccion, esteobjetivo es insatisfactible. Sin embargo, como hasta el momento T OY no hace distincionentre desigualdades estrictas y sobre reales, la primera desigualdad serıa tratada como seexplico en 3.12 quedando el almacen [X : [Y ], Y : [X]]. Las dos siguientes restricciones,de acuerdo con las nueva definiciones de ‘+’ y ‘−’ son enviadas al resolutor haciendo lasllamadas {X + Y = 2} y {X − Y = 0}. El resolutor trata ambas restricciones y pro-duce un exito haciendo las unificaciones X = 1,0 e Y = 1,0, ya que el resolutor nocuenta con la informacion de la primera desigualdad: en ningun momento se hahecho la llamada {X = \ = Y }. El resultado de este computo producirıa la respuestaX /= Y, X = 1,0, Y = 1,0, que obviamente es incorrecta.

Para que el objetivo anterior fuese resuelto correctamente (y provocase fallo), el resolu-tor deberıa contar con toda la informacion referente a las variables X e Y , y en particularcon la restriccion X /= Y . La solucion serıa enviar esta restriccion (la primera del obje-tivo) al resolutor, pero T OY no maneja tipos en tiempo de ejecucion16 y ante unarestriccion X /= Y es incapaz de distinguir a priori si se trata de una desigualdad entrereales o es una desigualdad mas.

El propio planteamiento del problema sugiere la solucion: cuando nos encontramos conla restriccion X /= Y , supondremos en principio que se trata de una restriccion de des-igualdad estricta y la almacenaremos como tal; si en el futuro se plantea alguna restriccionnumerica sobre X o sobre Y , entonces la desigualdad anterior sera transformada en unarestriccion numerica y enviada al resolutor, desapareciendo del almacen de desigualdades.En el ejemplo anterior, X /= Y se almacena como una desigualdad mas, pero cuando seplantea la primera restriccion numerica, X + Y == 2, que involucra a X (en este casotambien a Y ), entonces la desigualdad X /= Y es enviada al resolutor, en el que se delegala responsabilidad de tratar correctamente toda la informacion enviada, y en este caso dedetectar el fallo que debe producirse al lanzar la ultima restriccion del objetivo.

La cesion de restricciones al resolutor por parte de los almacenes lleva asociado unefecto de propagacion. Veamos un ejemplo: sea el objetivo X /= Y, X /= Z, Z−3 == V . Lasdos primeras restricciones, al no implicar ninguna operacion numerica, son almacenadascomo desigualdades estrictas y tendremos el almacen [X : [Y,Z], Y : [X], Z : [X]]. Laultima restriccion, sin embargo, si que implica una operacion aritmetica, y por tanto unallamada al resolutor ({Z−3 = V }). Entonces la restriccion X /= Z debe pasar al resolutor,pero tambien debe pasar X /= Y . La explicacion: si sobre Z hay una restriccion numerica,todas las desigualdades asociadas a Z deben pasarsele al resolutor, ya que Z es de tiponumerico; si Z es de tipo numerico y tenemos la restriccion Z /= X, entonces el tipode X tambien debe ser numerico y todas las desigualdades de X tienen que pasar alresolutor, y en particular, X /= Y . Este es el efecto de propagacion que mencionabamoscon anterioridad y que, en este ejemplo, hace que todas las restricciones vayan a pararfinalmente al resolutor.

Para hacer esta cesion de restricciones T OY cuenta con el predicado toSolver, cuyaespecificacion es la siguiente:

16Cuando T OY analiza el objetivo X /= Y, X + Y == 2, X − Y == 0 y hace chequeo de tipos, escapaz de inferir que tanto X como Y tienen tipo numerico, pero una vez hecho este analisis y verificadala correccion no hace ninguna anotacion sobre tipos. Esto significa que en tiempo de ejecucion sabe quelos tipos son correctos, pero no conoce los tipos concretos. Manejar tipos en tiempo de ejecucion suponearrastrar una gran cantidad de informacion que afectarıa notablemente a la eficiencia del sistema.

Page 126: Un lenguaje lógico funcional con restricciones1

126 CAPITULO 3. MECANISMO DE COMPUTO

toSolver(X, Cin, Cout) ⇔ si X es una variable entonces toda desigualdad Y /= Z de Cinpara la que Cin contiene una secuencia (posiblemente unitaria) X /= X1, X1 /= X2, ..., Xn /=Y, Y /= Z es lanzada al resolutor en la forma {Y = \ = Z}; Cout es el resultado de eliminar deCin todas las desigualdades que han pasado al resolutor. Si X no es variable deja el almacenintacto y no pasa nada al resolutor.

La especificacion implıcitamente contiene una nocion de relacion de desigualdades entrevariables: A/= B esta relacionado con todas desigualdades de la forma A/= X y B /= Y .Apoyandonos en esta relacion podrıamos especificar toSolver(X, Cin, Cout) como: si Xes una variable, todas sus restricciones asociadas y todas las que pertenezcan al cierretransitivo de la relacion anterior pasan al resolutor (y Cout es el resultado de eliminarlasde Cin).

toSolver(X,Cin,Cin):-nonvar(X),!.

toSolver(X,Cin,Cout):-extractCtr(X,Cin,Cout1,CX),passToSolver(X,CX,Cout1,Cout).

passToSolver( ,[ ],Cin,Cin).

passToSolver(X,[Y|R],Cin,Cout):-{ X = \ = Y },(

var(Y),!,toSolver(Y,Cin,Cout1),passToSolver(X,R,Cout1,Cout)

;passToSolver(X,R,Cin,Cout)

).

Cuadro 3.32: Cesion de desigualdades al resolutor

En la tabla 3.32 se muestra el codigo para toSolver. La primera clausula, en caso de queel argumento no sea variable, deja el almacen intacto y no hace nada mas. En la segundaclausula, cuando se trata de una variable se extraen del almacen (con eliminacion) susdesigualdades asociadas y se utiliza el predicado auxiliar passToSolver. Este predicadolanza al resolutor, una a una, las desigualdades asociadas a la variable, pero ademas, siel otro miembro es tambien una variable, llama recursivamente a toSolver con esta nuevavariable para propagar la cesion de restricciones.

De acuerdo con lo que acabamos de ver, el nuevo codigo para la funcion + sera:

+(X,Y,H,Cin,Cout):-hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),{H = HX + HY},toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

Ahora se han incluido llamadas a toSolver para cada uno de los argumentos de lafuncion y tambien para el resultado. De esta forma nos aseguramos de que todas las

Page 127: Un lenguaje lógico funcional con restricciones1

3.15. LAS RESTRICCIONES SOBRE LOS NUMEROS REALES 127

restricciones que le corresponden al resolutor, le seran enviadas en algun momento delcomputo.

Aun queda otro detalle pendiente. Supongamos el objetivo X +Y == 2, X /= 1, Y ==1; la primera restriccion es automaticamente enviada al resolutor y en este caso, como niX ni Y tienen desigualdades asociadas, no se pasa ninguna restriccion mas. La segundarestriccion es tratada por el predicado notEqual y queda en el almacen como X : [1]; yla tercera es procesada por equal y produce la ligadura Y = 1. El objetivo tendrıa exito,cuando en realidad es insatisfactible. El motivo es que la restriccion X /= 1 deberıa haberpasado al resolutor, pero no lo ha hecho. Para solucionar este problema necesitaremos elpredicado auxiliar isReal(X), que tiene exito si en el estado actual del computo se puedeafirmar que la variable X es de tipo real (no se utiliza informacion del inferidor), o loque es lo mismo: si X es un numero o una variable que tiene restricciones asociadas en elresolutor.

Con este predicado isReal, al que volveremos mas adelante, estamos en disposicion deintroducir una nueva clausula para el predicado notEqualHnf (vease 3.12), especıfica paradesigualdades entre reales. Esta clausula se muestra en la tabla 3.33 y es la primera delpredicado notEqualHnf . Lo primero que hace es comprobar el modo de uso del sistemacomprobando el flag clpr active y solo actua en el caso de que las restricciones sobrereales hayan sido activadas. En este caso comprueba si tiene suficiente informacion paradeterminar que alguno de los argumentos es de tipo real y, si es ası, envıa la desigualdadal resolutor (en otro caso esta clausula fallara y sera otra la que trate la desigualdad).Observese que el almacen de desigualdades permanece invariable.

notEqualHnf(X,Y,Cin,Cin):-clpr active,(isReal(X);isReal(Y)),!,{ X = \ = Y }.

Cuadro 3.33: Desigualdad entre reales

Otro hecho importante es que no se pasan desigualdades del almacen al resolutor. Porejemplo, en el objetivo X /= Y, X /= 2 la primera restriccion, de acuerdo con lo que hemosvisto, pasa al almacen; la segunda sera tratada por la nueva clausula y pasara al resolutor.Pero ahora, se sabe que X es de tipo real y podrıan pasarse al resolutor todas las restric-ciones que la involucren, y en particular X /= Y . De hecho serıa correcto (y quiza lo mascoherente con lo hemos visto en este apartado) hacer en esta clausula llamadas toSolvercon X y con Y para pasar sus desigualdades al resolutor. Pero no es necesario pasar estasdesigualdades al resolutor. La explicacion es sutil: tras procesar las dos restricciones delobjetivo X /= Y,X /= 2 el resolutor conoce la restriccion X /= 2 pero X /= Y permane-ce en el almacen. Intuitivamente, esta ultima desigualdad deberıa ser tambien conocidapor el resolutor; de lo contrario se corre el riesgo de que en algun momento el resolutorunifique X e Y (que serıa incorrecto). Sin embargo, para que esto ocurra debe lanzarselealguna restriccion aritmetica (que no sea una desigualdad) y que involucre a X e Y (y queprovoque la unificacion), en cuyo caso la primitiva correspondiente se encargara de enviarla restriccion X /= Y al resolutor y no se podra hacer tal unificacion.

En la tabla 3.34 se muestra el codigo del predicado isReal. La primera clausula utiliza elpredicado number de Prolog que tiene exito si su argumento es un numero. La segunda, enel caso de que el argumento sea variable, hace una proyeccion de restricciones sobre dicha

Page 128: Un lenguaje lógico funcional con restricciones1

128 CAPITULO 3. MECANISMO DE COMPUTO

isReal(X):-number(X),!.

isReal(X):-var(X),linear:dump([X], ,L),!,L \ == [ ].

Cuadro 3.34: Codigo de isReal

variable utilizando el predicado dump17. Si esta proyeccion o conjunto de restricciones enlas que interviene la variable, no es vacıa, entonces en algun momento anterior del computose establecio alguna restriccion aritmetica sobre dicha variable y por tanto, es de tipo realcon seguridad.

Para terminar, describimos brevemente la secuencia de computos que hace el siste-ma en presencia de restricciones, mediante el ejemplo de la figura 3.6. En definitiva, esun esquema de traduccion de restricciones sobre reales en programacion logico funcional(CLFP (R)) a restricciones en programacion logica (CLP (R), [FHK+93, Col90, Coh90,HJM+91, JM94]).

3+X <= sin 0.5

X 3 0.47...

TOY>

{X+3 = H}

{H <= 0.47...}

hnf hnf hnf

+

yes

{X <= -2.52...}

al resolutor

al resolutor

Figura 3.6: Computo con restricciones

El presencia del objetivo X+3 <= sin 0.5, se hace una llamada a la funcion ‘<=’, quesolicita una f.n.c. para los dos argumentos X+3 y sin 0.5. La reduccion del segundo produ-ce directamente un numero (0.47...) y para el segundo hay que utilizar la funcion ‘+’. Estafuncion evalua a su vez, una f.n.c. para sus dos argumentos X y 3. A continuacion, se intro-duce una nueva variable H y se envıa al resolutor la restriccion { H = X+3 }. La funcion‘<=’ completa su evaluacion enviando al resolutor la restriccion { H <= 0.47...}. Comoresultado, al final del proceso se obtiene la respuesta X <= -2.52..., que es efectivamentela solucion de la restriccion.

17La llamada dump([X], , L) devuelve en la lista L todas las restricciones sobre reales en las que inter-viene la variable X mediante proyeccion. Este predicado no esta disponible en la version 3# 5 de SicstusProlog ([Gro96]) y fue programado por C. Holzbaur a peticion nuestra. La version 3# 6 ([Gro97]) lo incluyecomo parte del interface del resolutor.

Page 129: Un lenguaje lógico funcional con restricciones1

Capıtulo 4

De la semantica

4.1. Introduccion

En este capıtulo presentaremos algunos aspectos semanticos de T OY, como la pereza ylas funciones indeterministas. Nuestro objetivo en este capıtulo es ofrecer una justificacionformal de algunos algunos aspectos de la semantica del T OY. Nuestro interes principalse ha centrado en la construccion de un calculo operacional que se aproxime a la imple-mentacion real en lo que se refiere al mecanismo resolucion de igualdades y desigualdadesestrictas, y para el cual se puedan probar resultados de correccion y completitud (conrespecto a un calculo de pruebas). Ademas, veremos que las desigualdades suponen unapotente herramienta con la que se puede abordar la Estrategia Guiada por la Demandaque utiliza el sistema para la evaluacion de funciones.

No obstante, el calculo que presentamos no cubre todos los aspectos de la implemen-tacion. En concreto, no trata el orden superior ni los tipos, y tampoco las restriccionessobre los numeros reales. En cuanto a las igualdades y desigualdades, el calculo capturael mecanismo esencial de resolucion, pero no refleja todas las optimizaciones que realiza elsistema real y que estudiamos en el capıtulo anterior.

En cuanto a los antecedentes, en [GHL+96, GHL+98] se propone una logica de re-escritura basada en constructoras (CRWL) como fundamento de la programacion logicofuncional. En este modelo la evaluacion perezosa y las funciones indeterministas encuentranuna justificacion precisa desde el punto de vista semantico. Sobre este trabajo se handesarrollado algunas extensiones que abarcan caracterısticas de orden superior ([GHR97]),y tipos polimorficos y algebraicos ([AR97a, AR97b]).

El calculo que presentamos aquı es otra extension1 del marco CRWL, en la que seincorporan desde un principio las restricciones de desigualdad. Este tipo de restriccionessuponen un recurso importante y tiene interes por sı mismo, ya que aparecen en situacionesmuy comunes en el contexto logico funcional (vease 2.3.8) y podran presentarse tanto enlos programas como en las respuestas.

En [KLM+92] se introducen las desigualdades para una clase restringida de programasen BABEL ([MR89, MR92]) y se propone una maquina abstracta para implementar ellenguaje resultante. Dicho trabajo esta enfocado fundamentalmente hacia la implementa-cion y las desigualdades carecen de tratamiento teorico. En [AGL94] la aproximacion esmas cercana a la que presentaremos aquı, aunque aun hay muchas diferencias. El marco

1Es una extension en cuanto a los calculos presentados. En [GHL+96, GHL+98] se presenta tambienuna semantica de modelos, segun la que todo programa tiene un modelo libre, que no ha sido abordadaaquı.

129

Page 130: Un lenguaje lógico funcional con restricciones1

130 CAPITULO 4. DE LA SEMANTICA

teorico considerado en [AGL94] es el esquema CFLP (X ), que fue concebido para progra-macion logico funcional con restricciones siguiendo las lıneas de CLP (X ) (programacionlogica con restricciones, [JL87]). Este marco es muy general en el sentido de que cubremuchos sistemas de restricciones, pero presenta problemas en la unificacion perezosa. Enel contexto de CRWL la unificacion recibe un tratamiento adecuado, ası como las funcio-nes no deterministas y el sharing. Por otro lado, se podran demostrar la completitud conrespecto a soluciones generales (en [AGL94] era con respecto a soluciones cerradas).

Nuestro calculo operacional, como sucede con el de [GHL+96, GHL+98], implica enprincipio una estrategia ingenua para la evaluacion de funciones. La riqueza expresiva delas desigualdades permitira hacer una transformacion de programas de modo que, con elmismo calculo dicha evaluacion correspondera a la estrategia guiada por la demanda queimplementa T OY (3.10).

Por otro lado, en nuestro calculo se maneja la nocion de prioridad en las condicionesde los objetivos, con la que se imponen ciertas restricciones sobre el orden en el que debenprocesarse dichas condiciones. Con ello ademas de aproximarnos mas a la implementacionreal de T OY, se evitan algunos test globales (y costosos en una implementacion real) queeran necesarios en la version de [GHL+96, GHL+98]. Nuestro calculo incorpora tambien lanocion de suspension, que se hace explıcita en las propias reglas (condiciones de prioridad0). La resolucion de objetivos se hace de forma perezosa y las suspensiones son condicionescuya resolucion se bloquea mientras que no sea estrictamente necesaria para continuar elcomputo. Para procesar las suspensiones se utilizan reglas explıcitas de activacion.

Notacion: en este capıtulo, por la frecuencia de uso, utilizaremos el sımbolo 6= parala desigualdad.

4.2. Preliminares

4.2.1. Signaturas, terminos y c-terminos

Partimos de una signatura Σ = DCΣ∪DFΣ, donde DCΣ =⋃

n∈IN DCnΣ es un conjunto

de sımbolos de constructora y FSΣ =⋃

n∈IN FSnΣ es un conjunto de sımbolos de funcion,

todos ellos con su aridad asociada y tal que DCΣ ∩ FSΣ = ∅. Suponemos ademas unconjunto enumerable V de sımbolos de variable. Definimos Term como el conjunto determinos (totales) construidos con sımbolos de Σ y V de la manera usual y distinguimosel subconjunto CTerm de terminos (totales) construidos o c-terminos, que solo utilizanlos sımbolos de DCΣ y V. Habitualmente omitiremos el subındice Σ.

Normalmente trabajaremos con la signatura extendida Σ⊥ que es el resultado de anadirel nuevo sımbolo de constante (constructora de aridad 0) ⊥ a Σ, que juega el papel devalor indefinido. Sobre esta nueva signatura pueden construirse los conjuntos Term⊥ yCTerm⊥ de terminos parciales y c-terminos parciales respectivamente (estan parcialmentedefinidos).

Como notacion habitual, utilizaremos letras mayusculas X, Y, Z... para los sımbolosde variable, c, d... para los de constructora, f, g... para los de funcion, e para los terminosy t, s, ... para los c-terminos. Para destacar la aridad de un sımbolo de constructora ofuncion, en algunos casos utilizaremos la notacion l/n, donde l es el sımbolo en cuestion yn es su aridad (l ∈ DCn ∪ FSn).

Se puede definir un orden de aproximacion natural v sobre los terminos parciales comoel mınimo orden parcial sobre Term⊥ que satisface:

⊥ v e, para todo e ∈ Term⊥

Page 131: Un lenguaje lógico funcional con restricciones1

4.3. EL CALCULO DE PRUEBAS GORC6= 131

e1 v e′1, ..., en v e′n ⇒ l(e1, ..., en) v l(e′1, ..., e′n), para todo l ∈ DCn ∪ FSn y

ei, e′i ∈ Term⊥.

En algunas demostraciones utilizaremos el concepto de profundidad de un termino quese define de la manera usual:

prof(⊥) = 0prof(X) = 0, ∀X ∈ Vprof(l) = 0, ∀l ∈ DC0 ∪ FS0

prof(l(e1, ..., en) = max{prof(ei)|i = 1..n}+ 1, ∀l ∈ DCn ∪ FSn, n ≥ 0

4.2.2. Sustituciones

Definimos el conjunto de sustituciones totales como

Subst = {θ : V → Term}Ampliando el rango con la signatura extendida Σ⊥ definimos el conjunto de sustitucionesparciales o simplemente sustituciones como

Subst⊥ = {θ : V → Term⊥}En lo que sigue nos interesara en especial un subconjunto de Subst⊥, el de c-sustitucionesparciales2 que se define como

CSubst⊥ = {θ : V → CTerm⊥}Notaremos por tθ al resultado de aplicar la sustitucion θ al termino t en el sentido

habitual y por µθ denotaremos la composicion de θ con µ tal que t(µθ) = (tµ)θ. Como decostumbre θ = [X1/t1, ..., Xn/tn] representa la sustitucion que cumple Xiθ ≡ ti para todoi = 1..n y Y θ ≡ Y para todo Y ∈ V −Xi, i = 1..n.

El orden de aproximacion v induce un orden natural de aproximacion sobre Subst⊥definido como:

θ v θ′ sii Xθ v Xθ′ para todo X ∈ V

4.3. El calculo de pruebas GORC 6=

En esta seccion introducimos el calculo GORC6= (Goal Oriented Rewriting Calculuswith disequalities), que es una extension del calculo logico de reescritura GORC presen-tado en [GHL+96, GHL+98]. Este calculo esta orientado a la resolucion de objetivos yes una aproximacion a la semantica de T OY desde un nivel abstracto. En la extensionque presentamos aquı, ademas de incluir las reglas oportunas para las desigualdades, ha-cemos un tratamiento ligeramente distinto de la igualdad estricta, mas proximo al modelooperacional que desarrollaremos en secciones posteriores.

Algunas propiedades de este calculo que, de alguna manera, han inspirado su cons-truccion son las que siguen:

para la aplicacion de una regla solo se requiere el examen de la estructura masexterna de los terminos,

2Se puede definir tambien el conjunto de c-sustituciones totales como CSubst = {θ : V → CTerm},pero estas sustituciones no tienen ninguna relevancia en nuestra teorıa.

Page 132: Un lenguaje lógico funcional con restricciones1

132 CAPITULO 4. DE LA SEMANTICA

la pereza esta capturada,

el sharing esta implıcito.

Fijada una signatura Σ = DC ∪ FS un programa es un conjunto R de reglas dereescritura condicional de la forma:

l = r <== C

donde:

l ≡ f(t1, ..., tn), el lado izquierdo de la regla, es un patron lineal (las variables tienenuna sola ocurrencia en la tupla (t1, ..., tn)) con f ∈ FSn, ti ∈ CTerm,∀i = 1..n.

r ∈ Term es el cuerpo de la regla, y

C es un conjunto de restricciones (posiblemente vacıo) de igualdad estricta y des-igualdad de la forma e13e′1, ..., em3e′m, con ei, e

′i ∈ Term,∀i = 1..m y 3 ∈ {==, 6=}.

Cuando C es vacıo se omitira el sımbolo <==.

Definimos el conjunto de c-instancias parciales de las reglas de un programa R como:

[R]⊥ = {(l = r <== C)θ | (l = r <== C) ∈ R, θ ∈ CSusbst⊥}.

Dado un programaR el calculo GORC6= servira para construir pruebas de restriccionesde igualdad de la forma e == e′, desigualdades e 6= e′ (en ambos casos con e, e′ ∈ Term⊥)y aproximaciones de la forma e → t (con e ∈ Term⊥ y t ∈ CTerm⊥), con respecto a lasreglas de reescritura de [R]⊥. Cuando se puede construir una prueba para una condicion c(restriccion o aproximacion), diremos que la condicion c es GORC6=-probable con respectoa R (o simplemente probable cuando no haya confusion) y lo notaremos como R ` c.

Las reglas del calculo estan guiadas por la forma (sintactica) de los terminos queaparecen en las condiciones, en particular por el sımbolo mas externo, que determinara laregla que se puede aplicar. La forma externa de un termino puede ser de tres formas:X ∈ V, c(e1, ..., en) con c ∈ DCn y f(e1, ..., en) con f ∈ FSm; y las reglas recogen todaslas posibles igualdades y desigualdades que pueden presentarse atendiendo a dichas formas.

A continuacion presentamos las reglas del calculo GORC6= y algunas ideas intuitivasacerca del significado que tienen. Con el fin de reducir el numero de reglas, aquı y en loque sigue, los sımbolos == y 6= se consideran simetricos.

CALCULO GORC 6=

1. e → ⊥2. X → X ∀X ∈ V

3.e1 → t1, ..., en → tn

c(e1, ..., en) → c(t1, ..., tn)c ∈ CDn, ti ∈ CTerm⊥

4.e1 → s1, ..., en → sn C s → t

f(e1, ..., en) → tt 6≡ ⊥, (f(s1, ..., sn) = s <== C) ∈ [R]⊥

5.f(e) → t t3e′

f(e)3e′f ∈ FSn, t ∈ CTerm⊥, 3 ∈ {==, 6=}

Page 133: Un lenguaje lógico funcional con restricciones1

4.3. EL CALCULO DE PRUEBAS GORC6= 133

6. X == X X ∈ V

7.e1 == e′1, ..., en == e′n

c(e1, ..., en) == c(e′1, ..., e′n)

c ∈ DCn

8. c(e) 6= d(e′) c ∈ DCn, d ∈ DCm, c 6≡ d

9.ei 6= e′i

c(e1, ..., en) 6= c(e′1, ..., e′n)

c ∈ DCn, i ∈ {1..n}

Las reglas (6) a (9) permiten hacer pasos de inferencia para probar restricciones en lasque ambos miembros son formas normales de cabeza. Por ejemplo, una restriccion de laforma c(e1, ..., en) == c(e′1, ..., e

′n) (c ∈ DCn) sera probable si lo son todas las restricciones

e1 == e′1, ..., en == e′n y una desigualdad de la forma c(e1, ..., en) 6= d(e′1, ..., e′m) (c ∈

DCn, d ∈ DCm) se probara automaticamente si c 6≡ d; en otro caso debe ser probablealguna de las desigualdades ei 6= e′i. Para variables, la unica restriccion que puede probarsees X == X.

Cuando aparecen expresiones funcionales en una restriccion como f(e)3e′ (f ∈ FSn)hay que hacer uso de las reglas del programaR. Se aplicara la regla (5), que introduce apro-ximaciones →. La prueba R ` f(e)3e′ se reduce a R ` f(e) → t, t3e′, con t ∈ CTerm⊥.Ahora, la restriccion t3e′ no contiene ningun sımbolo de funcion en su lado izquierdo, yaque t ∈ CTerm⊥ (si su lado derecho es una expresion funcional se aplicara nuevamentela regla (5)). Para probar la aproximacion f(e) → t, la regla (4) permite hacer un pasode reescritura con alguna de las c-instancias de [R]⊥. Los argumentos de llamada debenreducirse (o aproximarse) a los argumentos de la c-instancia (paso de parametros), elcuerpo de la c-instancia debe reducirse a t y finalmente, deben probarse las restriccionesC de la c-instancia.

Las reglas (2) y (3) reducen las pruebas en el caso de aproximaciones entre formasnormales de cabeza, mientras que la regla (1) es la que captura la pereza, como veremosen el siguiente ejemplo.

Ejemplo 1Sea Σ = {0/0, s/1, [ ]/0, : /2} ∪ {f/1, coin/0}. Asumimos ‘[ ]’ y ‘:’ como las construc-

toras de listas, aunque utilizaremos la notacion Prolog para representarlas; por ejemplo,la expresion [0, s(0)|L] representa la lista (0 : (s(0) : L)).

Sea R un programa sobre Σ definido por las reglas siguientes:

f(N) = [N |f(s(N))]

coin = 0coin = s(0)

La funcion f es similar al from de programacion funcional pura (f(0) produce la lista[0, s(0), s(s(0)), ...]) y coin representa los dos posibles resultados de lanzar una moneda alaire. Una (en general puede haber varias) de las posibles derivaciones formales para probarla validez del objetivo f(coin) 6= [0, 0] con respecto a GORC6= (R ` f(coin) 6= [0, 0]) es lasiguiente:

Page 134: Un lenguaje lógico funcional con restricciones1

134 CAPITULO 4. DE LA SEMANTICA

(5)

(4)

(4)(3)

0 → 0coin → 0

(3)(3) 0 → 0 (3)

(3)(3)

0 → 0s(0) → s(0)

(3)

(3)(3)

0 → 0s(0) → s(0)

(1)f(s(0)) → ⊥

[s(0)|f(s(0))] → [s(0)|⊥]f(s(0)) → [s(0)|⊥]

[0|f(s(0))] → [0, s(0)|⊥]

f(coin) → [0, s(0)|⊥](9)

(9)

(8)s(0) 6= 0

[s(0)|⊥] 6= [0]

[0, s(0)|⊥] 6= [0, 0]f(coin) 6= [0, 0]

Este ejemplo muestra la forma de una derivacion de una desigualdad que involucrala funcion indeterminista coin y una lista potencialmente infinita producida por la fun-cion f . La regla de GORC6= que se aplica en cada paso esta anotada entre parentesis.Como el lado izquierdo de la desigualdad inicial es una expresion funcional, la derivacioncomienza aplicando la regla (5) para reducir dicha expresion, tomando como c-termino tla lista [0, s(0)|⊥]. Como resultado se obtiene la aproximacion f(coin) → [0, s(0)|⊥] y ladesigualdad [0, s(0)|⊥] 6= [0, 0].

La aproximacion se trata en el paso (4) , en el que se toma la c-instancia (f(0) =[0|f(s(0))]) ∈ [R]⊥ (regla de f con θ = [N/0]) para reescribir f(coin) a [0|f(s(0))] (ha-ciendo la aproximacion coin → 0, para la que se tomara a su vez la primera regla de coin,que es c-instancia de sı misma). El sharing esta implıcito en (4) : observese que coin, elargumento de f , solo se reduce una vez al evaluar la expresion f(coin), a pesar de queen la regla de f el argumento tiene dos ocurrencias en el cuerpo. Otro hecho importantees el siguiente: en los lados derechos de las aproximaciones → se introducen c-terminos(t en (4) y (5)), o bien, provienen de los argumentos de c-instancias de reglas (s1, ..., sn

en (4)), que son tambien c-terminos, es decir, nunca se va a introducir una expresion quecontenga sımbolos de funcion en el lado derecho de una aproximacion, lo que significa quelas aproximaciones con las que trabajara el calculo siempre tienen c-terminosen el lado derecho.

Observese tambien que tras varios pasos de derivacion (que no detallamos), en el paso(1) se hace la aproximacion f(s(0)) → ⊥. De este modo f(coin) se ha reducido perezo-samente a la expresion [0, s(0)|⊥], es decir, se evalua solo un fragmento de la estructurainfinita. Esta evaluacion produce como resultado una lista cuyos dos primeros elementosson 0 y s(0), y el resto de elementos permanecen indefinidos o indeterminados. No esdifıcil apreciar que en un calculo no perezoso el objetivo que estamos tratando producirıano terminacion al intentar reducir f(coin) (intentarıa generar una estructura infinita).Incluso sin tratarse de estructuras infinitas, un calculo perezoso es capaz, en general, deproporcionar pruebas mas breves para los objetivos, puesto que puede dejar indefinidasaquellas expresiones cuya evaluacion no sea estrictamente necesaria. Ası pues la pereza,en un calculo de pruebas tiene dos consecuencias fundamentales: puede conseguir pruebasmas cortas y permite probar la validez de objetivos que un calculo impaciente no podrıaprobar. En un calculo operacional estas dos virtudes se traducen en mayor eficiencia ymejores propiedades de terminacion respectivamente. La pereza justifica el hecho de queutilicemos la signatura extendida Σ⊥ y la introduccion de la regla (1) en nuestro calculo,que permite aproximar cualquier termino a ⊥, o lo que es lo mismo, dejarla indefinida.

Por ultimo, notese que en la desigualdad [0, s(0)|⊥] 6= [0, 0], equivalente a (0 : (s(0) :⊥)) 6= (0 : (0 : [ ])), en la primera aplicacion de la regla (9) se elige de modo indeterministala segunda pareja de argumentos de ‘:’ para conseguir la prueba. A continuacion, sobre elresultado (s(0) : ⊥) 6= (0 : [ ]) se toman los primeros argumentos obteniendo s(0) 6= 0, quese deriva por (8).

En este ejemplo, las reglas de funcion no contienen restricciones. Si las tuviesen apa-

Page 135: Un lenguaje lógico funcional con restricciones1

4.3. EL CALCULO DE PRUEBAS GORC6= 135

recerıan en la derivacion (c-instanciadas) cuando se aplica la regla (4) de GORC6= y,naturalmente habrıa que probar tambien su validez. ¥

El marco de [GHL+96, GHL+98] asume una semantica por call-time choice, siguiendo elenfoque de [Hus93]. Como se muestra en en estos trabajos y en el apartado 2.3.7 de nuestrotrabajo (ejemplo de double coin), call-time choice es perfectamente compatible con unasemantica no estricta y con la evaluacion perezosa, supuesto que se realiza comparticion(sharing) en todas las apariciones de una variable en el lado derecho de una regla .

A continuacion demostraremos algunas propiedades que se verifican para este calculoy que seran de utilidad para relacionarlo con el calculo operacional que veremos masadelante.

Proposicion 1 (Transitividad de →) La relacion → es transitiva, es decir, si a, b, c ∈Term⊥ y a → b y b → c son probables, entonces a → c tambien es probable.

Demostracion

En primer lugar si b ≡ ⊥ solo puede ser c ≡ ⊥ y a → c ≡ ⊥ es tambien probable porla regla (1).

Si b 6≡ ⊥ procedemos por induccion sobre el numero de pasos l de la prueba de a → b:l = 1 Como b 6≡ ⊥ y b ∈ CTerm⊥ tenemos dos posibles pruebas para a → b:

Π ≡ (2)X → X, con a ≡ b ≡ X. Para que b → c sea probable tiene que ser c ≡ X yentonces tambien a → c es probable por (2).

Π ≡ (3) d → d, siendo a ≡ b ≡ d ∈ DC0 (constante). Como d → c es probable tieneque ser c ≡ d o c ≡ ⊥; en ambos casos a → c es probable por la regla (3) si c ≡ d, opor (1) si c ≡ ⊥.

l + 1 a tiene que tener una de las dos formas siguientes:

a ≡ d(e1, ..., em). Como a → b, entonces tiene que ser b ≡ d(t1, ..., tm) y las aproxi-maciones ei → ti deben ser probables. Como b → c tambien es probable tiene queser c ≡ ⊥ en cuyo caso a → c es probable por (1), o bien c ≡ d(s1, ..., sm) y entoncesdeben ser probables las aproximaciones ti → si. Por h.i. son probables ei → si ypodemos construir una prueba para a → c con la regla de descomposicion (3).

a = f(e1, ..., em). La prueba de a → b debe construirse con la regla (4). Entonces debeexistir (f(t1, ..., tm) = r <== C) ∈ [R]⊥ tal que ei → ti, C, r → b sean probables.Como la prueba de r → b tiene menos de l + 1 pasos y b → c es tambien probable,por h.i., es probable r → c y podemos reconstruir una prueba para a → c con laregla (4) de GORC6=. ¥

La igualdad estricta no es transitiva en general, debido al indeterminismo. Es decir, quea == b y b == c sean GORC6=-probables no implica que a == c sea GORC6=-probable.Por ejemplo, sean a y c dos constantes (constructoras de aridad 0) distintas, f una funcion(indeterminista) definida por las reglas f X = a y f X = c. Podemos probar a == f Xy f X == c, pero no es probable a == c. No obstante, si que podemos demostrar unresultado “parcial” de transitividad para == que se recoge en la siguiente proposicion.

Page 136: Un lenguaje lógico funcional con restricciones1

136 CAPITULO 4. DE LA SEMANTICA

Proposicion 2 (Transitividad restringida de ==) Sean a, c ∈ Term⊥ y b ∈ CTerm⊥.Si a == b, b == c son GORC6=-probables, entonces a == c es GORC6=-probable (yb ∈ CTerm, es decir, b es total).

Antes de demostrar el resultado observemos que en el enunciado se exige la premisab ∈ CTerm⊥ (posiblemente parcial) y se concluye b ∈ CTerm (total). Esto, aunque severa en la demostracion es bastante intuitivo porque no hay ninguna regla en GORC6=que permita probar una igualdad con ⊥ en alguno de sus miembros.

Demostracion

Por induccion sobre la longitud l (numero de pasos) de la prueba Π de a == b:l = 1 Si la prueba de a == b puede hacerse en un paso solo pueden ocurrir dos cosas:

a ≡ b ≡ X ∈ V o a ≡ b ≡ d ∈ DC0. Entonces la prueba de a == c es identica a la dea == b por ser a ≡ b. Y en ambos casos b es total.

l + 1 Hacemos una distincion de casos sobre la forma de a:

Si a ≡ d(e1, ..., en), con d ∈ DCn, como b ∈ CTerm⊥ y a == b es probable, entoncestiene que ser b ≡ d(t1, ..., tn) con ti ∈ CTerm⊥ y la prueba de a == b sera de laforma:

Π1...Πn

d(e1, ..., en) == d(t1, ..., tn)

siendo Πi prueba de ei == ti.Puesto que b == c es probable, hay dos posibilidades para c:

• c ≡ d(e′), con lo que la prueba b == c sera

Π′1...Π′n

d(t1, ..., tn) == d(e′1, ..., e′n)

con Π′i prueba de ti == e′i.Tenemos pruebas de longitud ≤ l para ei == ti y son probables las igualdadesti == e′i con ti ∈ CTerm⊥, luego por h.i., existen pruebas Π′′i para ei == e′i yti ∈ CTerm. Entonces b ∈ CTerm y se puede reconstruir la prueba de a == cque sera

Π′′1...Π′′n

d(e1, ..., en) == d(e′1, ..., e′n)

• c ≡ f(e′), luego la prueba de b == c es de la forma

Π1 Π2

f(e′) == d(t)

con Π′1 prueba de f(e′) → r, Π′2 prueba de r == d(t) y r ∈ CTerm⊥.En estas condiciones, r debe ser de la forma r ≡ d(r1, ..., rn), ri ∈ CTerm⊥ yΠ′2 debe contener pruebas de ri == ti. Tenemos pruebas de longitud < l + 1de ei == ti y tambien pruebas de ri == ti, con ti ∈ CTerm, luego por h.i.,ti ∈ CTerm y debe haber pruebas Πi para ei == ti. Entonces b ∈ CTerm y sepuede reconstruir una prueba para a == c, que tendra la forma:

Page 137: Un lenguaje lógico funcional con restricciones1

4.3. EL CALCULO DE PRUEBAS GORC6= 137

Π1 Π′′1...Π′′n

d(e) == f(e′)

Si a ≡ f(e) la prueba a == b es de la forma

Π1 Π2

f(e) == b

con Π1 prueba de f(e) → r y Π2 prueba de r == b. Como Π2 es de longitud < l +1,b == c es probable y b ∈ CTerm⊥, podemos aplicar la h.i., con lo que tenemos queb ∈ CTerm y a == c probable. ¥

4.3.1. Caracterizacion de == en funcion de →En el calculo GORC que se presenta en [GHL+96, GHL+98] la unica regla que aparece

para la igualdad es (en nuestra notacion):

a → t b → t

a == bt ∈ CTerm

En este calculo la igualdad entre dos expresiones es probable si es probable que am-bas expresiones aproximan al mismo termino total. En nuestro calculo la prueba es “masdirecta”, analizando la estructura sintactica (realmente el sımbolo mas externo) de lasexpresiones. La justificacion de este hecho es que posteriormente presentaremos un calculooperacional que tendremos que relacionar con GORC6= y nos interesa por tanto, captu-rar ciertos aspectos operacionales desde un principio para hacer mas sencillas algunasdemostraciones.

Sin embargo, esta diferencia no tiene mayor trascendencia desde el punto de vistasemantico, como prueba el lema de caracterizacion que presentamos en este apartado. Estelema afirma que una igualdad e == e′ es valida si tanto e y e′ pueden reducirse a c-terminos(valores totalmente definidos) sintacticamente iguales, usando las reglas de reescritura de[R]⊥. En particular, la igualdad entre c-terminos es una cuestion meramente sintactica yen realidad no hay nada que reescribir, como se prueba en el siguiente proposicion.

Proposicion 3 Sean t, s ∈ CTerm. t == s es GORC6=-probable sii t ≡ s

Demostracion

(⇒) Por induccion sobre la profundidad p de t:p = 0 Debe ser t ≡ X variable o t ≡ c constructora de aridad 0. En el primer caso,como s ∈ CTerm y t == s es probable tiene que ser s ≡ X y X == X es probablepor la regla (6). En el segundo caso tiene que ser s ≡ c y c == c se demuestra porla regla (7).

p + 1 Como t ∈ CTerm debe ser t ≡ c(t1, ..., tn) de profundidad p+1 y como t == ses probable y s ∈ CTerm tiene que ser s ≡ c(s1, ..., sn) (regla (7)). En ese caso debenser probables ti == si, ∀i ∈ {1..n}. Como la profundidad de ti es < p + 1, por h.i.ti ≡ si, ∀i ∈ {1..n} y entonces t ≡ s.

Page 138: Un lenguaje lógico funcional con restricciones1

138 CAPITULO 4. DE LA SEMANTICA

(⇐) Se prueba de forma similar, por induccion sobre la profundidad p de t. ¥

Proposicion 4 Si t ∈ CTerm y a → t es GORC6=-probable, entonces a == t es GORC6=-probable.

Demostracion

Por induccion sobre el numero de pasos l de la prueba de a → t:l = 0. Puede ser a ≡ t ≡ X variable o a ≡ t ≡ c sımbolo de constructora de aridad 0;

en cualquier caso por la proposicion anterior a == t es probable.

l + 1. Hay dos posibilidades:

a ≡ c(s1, ..., sm) y t ≡ c(t1, ..., tm) con si → ti probable. Como ti ∈ CTerm, porhipotesis de induccion si == ti es probable y por tanto podemos construir unaprueba de c(s) == c(t) por la regla (7) de GORC6=.

Si a ≡ f(e), como t == t es probable por el lema anterior, entonces mediante laregla (5) de GORC6= podemos construir la prueba:

f(e) → t, t == t

f(e) == t

¥

Lema 1 (Caracterizacion de == en terminos de →) a == b es GORC6=-probablesii ∃t ∈ CTerm tal que a → t y b → t son GORC6=-probables.

Demostracion

(⇒) Por induccion sobre el numero de pasos l de la prueba:

l = 0. Debe ser a ≡ b ≡ X (regla 6) variable o a ≡ b ≡ c constructora de aridad 0(regla 7). Tomando t ≡ a, en el primer caso a → t y b → t pueden demostrarse porla regla (2); en el segundo caso pueden probarse en un solo paso por la (3).

l + 1. A la vista del calculo GORC6= si la igualdad a == b es probable en mas de unpaso, puede tener dos formas:

• Si c(r1, ..., rn) == c(s1, ..., sn) es probable, entonces deben ser probables lasigualdades r1 == s1, ..., rn == sn. Por hipotesis de induccion existen t1, ..., tn ∈CTerm, tal que todas las aproximaciones ri → ti y si → ti son probables.Tomando t ≡ c(t1, ..., tm) tenemos que c(r1, ..., rm) → t y c(s1, ..., sm) → t sonprobables por la regla (3).

• Si f(e) == e′ es probable debe serlo utilizando la regla (5) del calculo, por loque f(e) → s y s == e′ con s ∈ CTerm⊥ deben ser probables. Por hipotesisde induccion, como la prueba de s == e′ tiene menos de l + 1 pasos, existet ∈ CTerm tal que s → t y e′ → t son probables. Ası pues, como f(e) → s ys → t son probables, por transitividad de → tambien debe serlo f(e) → t, loque completa el resultado para este caso.

Page 139: Un lenguaje lógico funcional con restricciones1

4.3. EL CALCULO DE PRUEBAS GORC6= 139

(⇒) Es consecuencia de la proposicion 4 y de la transitividad de ==: si a → t y b → tcon t ∈ CTerm son probable, por dicha proposicion a == t y b == t son proba-bles. Como t ∈ CTerm, por transitividad de ==, tenemos que a == b es tambienprobable. ¥

Para completar este apartado proponemos tambien una caracterizacion sintactica dela desigualdad como contrapartida a la proposicion 4.

Proposicion 5 (Caracterizacion sintactica de 6=) Sean t, s ∈ CTerm⊥, t 6= s es pro-bable en GORC6= ⇔ existe una posicion en la que t y s tienen sımbolos distintos entre sı ydistintos de ⊥.

Demostracion

(⇒) por induccion sobre la longitud de la prueba, que solo puede utilizar las reglas 8 y 9del calculo.

(⇐) por induccion sobre la posicion en la que colisionan t y s. En dicha posicion solopuede haber un sımbolo de constructora. ¥

4.3.2. Sustituciones

En este apartado demostraremos varios resultados que relacionan las sustituciones conel calculo GORC6=.

Lema 2 (Lema de sustitucion) Dados θ, θ′ ∈ CSubst⊥ tal que Xθ′ = tθ y Y θ′ ≡ Y θpara toda Y 6≡ X, entonces θ′ ≡ [X/t]θ.

Demostracion

Tenemos que probar ∀s ∈ Term⊥.sθ′ = s[X/t]θ. Razonamos por induccion sobre laprofundidad p de s:p = 0, debe ser:

s ≡ ⊥, trivial,

s ≡ X, entonces sθ′ = Xθ′ = tθ = X[X/t]θ = s[X/t]θ,

s ≡ Y 6≡ X, entonces sθ′ = Y θ′ = Y [X/t]θ′ = Y [X/t]θ = s[X/t]θ

s ≡ c ∈ DC0, entonces sθ ≡ sθ′ ≡ c

p + 1, debe ser s ≡ l(e1, ..., en) con l ∈ DC ∪ FS. Entonces sθ′ = l(e1, ..., en)θ′ =

l(e1θ′, ..., enθ′) H.I.= l(e1[X/t]θ, ..., en[X/t]θ) = l(e1, ..., en)[X/t]θ. ¥

Proposicion 6 (Orden de aproximacion y →) Sean a, b ∈ CTerm⊥. Se tiene a → bes probable sii a w b.

Page 140: Un lenguaje lógico funcional con restricciones1

140 CAPITULO 4. DE LA SEMANTICA

Demostracion

(⇒) Por induccion sobre la longitud l de la prueba Π de a → b:

l = 0 Hay tres posibilidades:

• Π ≡ (1) a → ⊥ y sabemos que ⊥ v a, ∀a ∈ Term⊥• Π ≡ (2)X → X, y es cierto X v X (reflexividad)

• Π ≡ (3) c → c, con c ∈ DC0 y tambien es cierto c v c

l + 1 Como a ∈ CTerm⊥ tiene que ser

Π ≡ (3)Π1...Πn

c(e1, ..., en) → c(t1, ..., tn)

siendo Πi prueba de ei → ti, i ∈ {1..n}. Por h.i. ei w ti y por la definicion de wtenemos c(e) w c(t).

(⇐) Si a v b debe ser:

• b ≡ ⊥ y tenemos a → ⊥, ∀a ∈ Term⊥• b ≡ c(t1, ..., tn), entonces tiene que ser a ≡ c(s1, ..., sn) con ti v si, ∀i ∈ {1..n}.

Por h.i. si → ti, ∀i ∈ {1..n} y entonces por la regla (3) tenemos que a ≡ c(s) →c(t) es probable. ¥

El siguiente resultado establece que si se tiene una sustitucion θ que permite demostraruna aproximacion de la forma tθ → sθ, entonces tθ y sθ son “casi” iguales. En concreto,es posible que haya algunas posiciones en las que sθ tenga ⊥ y en las que tθ contengacualquier termino (tambien puede ser ⊥). Pero como s, t ∈ CTerm los sımbolos ⊥ debenhaber sido introducidos por θ, es decir, θ puede haber reemplazado algunas variables de sy t por terminos que contengan ⊥. Entonces es posible encontrar otra sustitucion θ′ quereemplaze esas variables por terminos totales, de modo que sθ sea identico a tθ.

Lema 3 (Refinamiento) Sean t, s ∈ CTerm, θ ∈ Subst⊥, s lineal y var(t)∩ var(s) = ∅.Si tθ → sθ es probable, entonces existe θ′ w θ tal que:

a) θ′ = θ[V − var(s)] (θ′ coincide con θ salvo en var(s))

b) tθ′ = sθ′ (ademas tθ = tθ′)

DemostracionAntes de demostrar el teorema observemos que de existir la θ′ que postula el teorema,como solo difiere de θ en var(s) y var(s) ∩ var(t) = ∅ tenemos que tθ′ = tθ (el parentesisde b)).

La prueba se hace por induccion estructural sobre s:

Si s = X variable, como X 6∈ var(t) podemos definir θ′ ≡ θ[V−{X}] y Xθ′ = tθ. Porla definicion de θ y por la propiedad 6 tenemos Xθ = sθ v tθ = Xθ′ y Y θ = Y θ′,luego θ v θ′.

Page 141: Un lenguaje lógico funcional con restricciones1

4.3. EL CALCULO DE PRUEBAS GORC6= 141

Si s = c(s1, ..., sn), para que tθ → sθ sea probable en GORC6=, debe ser tθ ≡c(t1, ..., tn) y deben ser probables las aproximaciones ti → siθ. Como var(t)∩var(s) =∅, debe ser var(ti) ∩ var(si) = ∅. Podemos definir sustituciones θi restringidas a lasvariables de si:

Xθi ={

Xθ, si X ∈ var(si)X, e.o.c.

De este modo tenemos siθi = siθ y tiθi = ti, y hemos conseguido que θi este en lashipotesis del teorema con respecto a la aproximacion tiθi → siθi. Entonces existeθ′i w θi tal que θ′i = θi[V − var(si)] y

tiθ′i = siθ

′i(≡ ti) (i)

Como s es lineal, podemos definir θ′ ası:

Xθ′ ={

Xθ′i, si X ∈ var(si)Xθ, e.o.c.

y tenemos θ′ w θ porque Xθ′ = Xθ′i w Xθi = Xθ, si X ∈ var(si) ⊆ var(s) yXθ′ = Xθ e.o.c.. De hecho, θ′ = θ[V − var(s)]. Por otro lado:

sθ′ = c(s1θ′, ..., snθ′) = c(s1θ

′1, ..., snθ′n)

(i)= c(t1, ..., tn) = t = tθ′ ¥

A continuacion destacamos un hecho que relaciona el orden v con las sustituciones,que por sı mismo no tienen especial relevancia, pero que puede ayudar a comprender mejoralgunos razonamientos posteriores: dado e ∈ Term⊥ existe otro termino que notaremose ∈ Term y existe una sustitucion µ : V → {⊥} tal que eµ ≡ e, de modo que las variablespara las que µ toma el valor ⊥ sean nuevas con respecto a las de e. El termino e es igualque e excepto en aquellas posiciones donde e tiene ⊥, en las cuales e tiene variables nuevas.Si e ≡ ⊥, podemos tomar e ≡ X y µ ≡ [X/⊥]; si e ≡ X podemos tomar e ≡ X y µ ≡ []; ysi e ≡ l(e1, ..., en), con l ∈ DC ∪DF podemos construir e1, ..., en y µ1, ..., µn de modo quelos conjuntos de variables para los que las µi toman el valor ⊥ sean disjuntos entre sı (estoes posible porque para la construccion de los ei podemos utilizar cualquier conjunto de va-riables; en particular, podemos escogerlos de modo que sean disjuntos dos a dos). Podemosreconstruir e ≡ l(e1, ..., en) tomando µ como la composicion de las µi en cualquier ordenpuesto que el resultado sera el mismo. Por ejemplo, si e ≡ f(c(X,⊥), d(c(⊥, Y ), Z),⊥),podemos tomar e ≡ f(c(X,A), d(c(B, Y ), Z), C) y µ ≡ {A/⊥, B/⊥, C/⊥}

Si entre dos terminos e, e′ ∈ Term⊥ tenemos la relacion e v e′ puede ocurrir que seanidenticos o que sean iguales salvo en un conjunto de posiciones en las que e′ esta “masdefinido” que e′, es decir, en dichas posiciones e tiene el sımbolo ⊥, mientras que e′ tienecualquier otro termino distinto de ⊥.

El siguiente resultado establece una relacion entre la estructura de las pruebas y elorden de aproximacion del modo siguiente: si tenemos una prueba para una aproxima-cion o restriccion, entonces se pueden encontrar pruebas para todas las aproximaciones orestricciones cuyos miembros esten mas definidos (el lado izquierdo, en el caso de las apro-ximaciones) en el orden de aproximacion v, y ademas dichas pruebas seran de la mismalongitud y estructura. Que tengan la misma estructura significa que se construyen utili-zando las mismas reglas de GORC6=. Por ejemplo, si f ∈ FS, c, d ∈ DC y t ∈ CTerm⊥,

Page 142: Un lenguaje lógico funcional con restricciones1

142 CAPITULO 4. DE LA SEMANTICA

y f(X, c(⊥),⊥) → t es GORC6=-probable, entonces existe una prueba estructuralmenteigual de f(X, c(Y ), d) → t. La idea es que si para construir la primera prueba no es ne-cesario conocer los valores concretos de algunos argumentos (los que son ⊥), es porqueesos valores no son “necesarios” para la derivacion. Entonces, podemos reemplazar dichosvalores por terminos cualesquiera y la aproximacion continuara siendo probable replicandolos pasos que se dieron en la original. En el caso de las restricciones el razonamiento essimilar.

Lema 4 (Monotonıa) Sean e, e′ ∈ Term, θ, θ′ ∈ Subst⊥ y t ∈ Cterm⊥. Si θ v θ′ y Πes una GORC6=-prueba de eθ → t (eθ3e′ respectivamente), entonces existe una GORC6=-prueba Π′ de eθ′ → t (eθ′3e′ resp.) con la misma longitud y estructura que Π (siendo3 ∈ {==, 6=}).

Antes de demostrar este resultado observemos que en el caso de las restricciones, la sus-titucion solo se aplica a uno de los miembros, el resultado es cierto aplicando sustitucionesa ambos miembros. Basta aplicar dos veces el lema:

R ` eθ3e′θ y θ v θ′ ⇒ R ` eθ′3e′θ y θ v θ′ ⇒R ` eθ′3e′θ′

El enunciado, tal y como lo hemos expresado, facilitara la demostracion.Demostracion

El enunciado del teorema encierra tres enunciados (para →, == y 6=).Para el primero de ellos vamos a demostrar un resultado en apariencia mas general,

aunque realmente es equivalente. Probaremos que si a v a′ y Π es una prueba de R ` a →t, entonces existe una prueba Π′ de R ` a′ → t con la misma longitud y estructura queΠ. La monotonıa de → es una consecuencia inmediata de este resultado ya que si θ v θ′

por la definicion de v tenemos que eθ v eθ′. Identificando eθ con a y eθ′ con a′ estamosen las hipotesis del enunciado que vamos a demostrar.

En primer lugar observemos que en el caso de t ≡ ⊥, sabemos que para todo b ∈ Term⊥hay una prueba de b → ⊥ de longitud 1 utilizando la primera regla de GORC6=. En estecaso Π ≡ Π′.

Para el caso de t 6≡ ⊥ (por otro lado mas interesante), razonamos por induccion sobrela profundidad p de a:

p = 0 Puede ser:

a ≡ ⊥, en cuyo caso tiene que ser t ≡ ⊥ que ya esta estudiado.

Si a ≡ X, como a v a′ tiene que ser a′ ≡ X, ya que en el orden v es maximal (unavariable solo esta relacionada con ella misma y con ⊥ y dos variables distintas sonincomparables). Entonces Π ≡ Π′ ≡ (2)X → X.

a ≡ c ∈ DC0, tiene que ser a′ ≡ c y entonces Π ≡ Π′ ≡ (2)c → c.

p + 1 Puede ser:

a ≡ c(a1, ..., an), entonces tiene que ser a′ ≡ c(a′1, ..., a′n) con ai v a′i. El caso t ≡ ⊥

ya esta estudiado y la otra posibilidad es t ≡ c(t1, ..., tn), con lo que la prueba sera dela forma:

Page 143: Un lenguaje lógico funcional con restricciones1

4.3. EL CALCULO DE PRUEBAS GORC6= 143

Π ≡ (3)Π1...Πn

c(a1, ..., an) → c(t1, ..., tn)

con Πi prueba de ai → ti. Como ai v a′i, por h.i. existe para cada i = 1..n una pruebaΠ′i de a′i → ti con la misma longitud y estructura que Πi. Ası podemos reconstruirla prueba

Π′ ≡ (3)Π′1...Π

′n

c(a′1, ..., a′n) → c(t1, ..., tn)

con la misma longitud y estructura que Π.

a ≡ f(a1, ..., an), tiene que ser a′ ≡ f(a′1, ..., a′n) con ai v a′i. El caso t ≡ ⊥ ya esta

estudiado y si t 6≡ ⊥ sera

Π ≡ (4)Π1...Πn ΠC Πs

f(a1, ..., an) → t

siendo (f(s1, ..., sn) = s <== C) ∈ [R]⊥, Πi prueba de ai → si, ΠC pruebas de lasrestricciones C y Πs prueba de s → t. Como ai v a′i, por h.i. podemos construirpruebas Π′i de a′i → si con la misma longitud y estructura que Πi. Y podemosentonces reconstruir una prueba Π′ con la misma longitud y estructura que Π quetendra la forma:

Π′ ≡ (4)Π′1...Π

′n ΠC Πs

a′ ≡ f(a′1, ..., a′n) → t

Para la monotonıa de 3 (3 ∈ {==, 6=}) se puede hacer una prueba similar probandoun resultado equivalente al del enunciado: si Π es una prueba de R ` a3e′ y a v a′

entonces se puede construir una prueba Π′ de a′3e′ con la misma longitud y estructuraque Π.

Razonamos como antes, por induccion sobre la profundidad p de a:

p = 0 Posibles pruebas Π de a3e′ para a de profundidad 0:

No puede ser Π ≡ ⊥3e′ porque en GORC6= no se puede probar nada de la forma⊥3e′.

Si Π ≡ (6) X == X, con a ≡ e′ ≡ X, como a v a′ tiene que ser a′ ≡ X y entoncesΠ ≡ Π′.

Si Π ≡ (7) c == c, con a ≡ e′ ≡ c ∈ DC0 tiene que ser a′ ≡ c y se tiene Π ≡ Π′.

Si Π ≡ (8) c 6= d(e′), con a ≡ e′ ≡ c ∈ DC0, tiene que ser a′ ≡ c y tenemos Π′ ≡ Π.

p + 1 a puede ser de dos formas:

a ≡ c(a1, ..., an). Como a v a′ sera a′ ≡ c(a′1, ..., a′n) con ai v a′i. La prueba Π de

a3e′ puede tener cuatro formas dependiendo de la forma de e′:

Page 144: Un lenguaje lógico funcional con restricciones1

144 CAPITULO 4. DE LA SEMANTICA

• e′ ≡ f(e′), Π ≡ (5)Π1 Π2

c(a)3f(e′)con t ∈ CTerm⊥, Π1 prueba de f(e′) → t, Π2 prueba de t3c(a). Comot ∈ CTerm, ahora la prueba de t3c(a) tiene que caer en uno de los casosanteriores, en los que se prueba que existe una prueba Π′2 de t3c(a′) con lamisma longitud y estructura que Π2, con lo que podemos reconstruir la pruebabuscada reemplazando Π2 por Π′2.

• e′ ≡ c(e′), Π ≡ (7)Πi

c(a) == c(e′)con Πi pruebas de ai == e′i. Por h.i. existen pruebas Π′i con las que podemosreconstruir la prueba Π′ para a′ == e′.

• e′ ≡ d(e′), Π ≡ (8) c(a) 6= d(e′)

con c 6≡ d. Como a′ ≡ c(a), con la misma regla (8) podemos construir Π′.

• e′ ≡ c(e′), Π ≡ (9)Πi

c(a) 6= c(e′)siendo Πi prueba de ai 6= ei para algun i. Por h.i. tenemos una prueba Π′i dea′i 6= ei con la misma longitud y estructura con la que podemos reconstruir laprueba buscada Π.

a ≡ f(a1, ..., an). Tiene que ser a′ ≡ f(a′1, ..., a′n). La prueba Π sera de la forma:

Π ≡ (5)Π1 Π2

f(a)3e′

siendo Π1 prueba de f(a) → t, Π2 prueba de t3e′ y t ∈ CTerm⊥. Por monotonıa de→ existe una prueba Π′1 de f(a′) → t con la misma longitud y estructura que Π1,con la que podemos reconstruir la prueba Π buscada. ¥

4.4. Calculo de resolucion de objetivos

De forma similar a lo que ocurre en los lenguajes logicos, hacer un computo en elcontexto en el que trabajamos es resolver un objetivo, que a su vez significa obtenervalores (en forma de restricciones) para las variables que aparecen en dicho objetivo, demodo que al sustituir las variables por los valores calculados, el objetivo obtenido seaGORC6=-probable.

Nuestra meta ahora es definir un calculo que refleje algunos aspectos de la semanticaoperacional de T OY, es decir, un calculo de resolucion de objetivos. Este calculo, al igualque el LNC (Lazy Narrowing Calculus) de [GHL+96, GHL+98], combina el estrechamientoperezoso con la resolucion de restricciones. No obstante, la version que presentamos aquı esmuy distinta a la de [GHL+96, GHL+98], no solo por la incorporacion de restricciones dedesigualdad. Una diferencia muy importante es que a cada condicion (restriccion o apro-ximacion) del objetivo se le asocia una prioridad, de modo que en cada paso de computosolo pueden procesarse aquellas condiciones que tienen prioridad maxima. Puede habervarias con prioridad maxima, en cuyo caso la eleccion es indeterminista (indeterminismodon’t care).

Page 145: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 145

La idea que encierra el uso de prioridades es la de secuenciar los computos con un finfundamental e intuitivamente natural: cuando se aplica una regla de funcion f(t1, ..., tn) =r <== C para reducir una llamada f(e1, ..., en), hacer primero el paso de parametrosantes de resolver las restricciones C o reducir el cuerpo r, es decir, procesar primero lasaproximaciones e1 → t1, ..., en → tn. Con ello se consiguen evitar algunos tests globalesy de coste elevado que se hacıan en [GHL+96, GHL+98]; por ejemplo, la comprobacionde si un termino es un c-termino (sin llamadas a funcion) que se hacıa con frecuencia en[GHL+96, GHL+98], en nuestro calculo se realiza en muy pocas ocasiones. En particular,este test solo es necesario para resolver desigualdades; las igualdades pueden resolverse sinel.

Paralelamente a la introduccion de prioridades se introducen de manera explıcita lassuspensiones o aproximaciones cuya evaluacion se retrasa hasta que sea necesaria paraprocesar alguna otra condicion. Cuando el computo puede continuar sin reducir una apro-ximacion de prioridad maxima, dicha aproximacion se suspende y deja de ser prioritaria. Sien el futuro tal reduccion debe procesarse, entonces se activa (tambien de forma explıcita)la suspension.

En esta misma seccion presentaremos los resultados de correccion y la completitud denuestro calculo LNC6= con respecto al calculo de pruebas GORC6=.

4.4.1. El calculo LNC 6=

Definimos en primer lugar la nocion de objetivo y objetivo inicial, y a continuacionpresentamos las reglas de LNC6=.

Definicion 1 (Objetivo para el calculo LNC 6=) Un objetivo es una expresion de laforma G ≡ ∃U.P2R2σ2δ, donde:

U es el conjunto de variables existenciales de G o evar(G).

P es un multiconjunto de aproximaciones con prioridad asociada de la forma l1α1→

t1, ..., lnαn→ tn, αi ∈ IN. A las aproximaciones de ındice 0 las llamaremos suspensio-

nes.

Definimos el conjunto de variables producidas como pvar(G) = {X|∃(l α→ r) ∈ Ptal que X ∈ var(r)}. El subconjunto de variables suspendidas es un subconjuntodistinguido de variables producidas definido como suspvar = {X|(l 0→ r) ∈ P, X ∈var(r)}.R (parte de resolucion) es un multiconjunto de restricciones con prioridad asociadade la forma e

α3 e′, con e, e′ ∈ Term, 3 ∈ {==, 6=} y α ∈ IN.

σ (parte de sustitucion) es un conjunto de igualdades de la forma X = t, con X ∈ Vy t ∈ CTerm.

δ (parte de desigualdades) es un conjunto de desigualdades de la forma X 6= t, conX ∈ V y t ∈ CTerm.

Dada una variable X, el conjunto de desigualdades asociadas a X se define comoδX = {X 6= t|(X 6= t) ∈ δ}. ¥

Definicion 2 (Objetivo inicial para LNC 6=) Un objetivo inicial tiene la forma G ≡2R22 (esto es P = σ = δ = ∅ y tambien evar(G) = ∅) donde todas las restricciones deR tienen ındice 1. ¥

Page 146: Un lenguaje lógico funcional con restricciones1

146 CAPITULO 4. DE LA SEMANTICA

Definicion 3 (Variables Seguras) Definimos el conjunto de variables seguras de untermino (variables que no van a desaparecer debido a las reducciones) como:

svar(X) ≡ {X}svar(c(e1, ..., en)) ≡ ∪n

i=1svar(ei), si c ∈ DCn

svar(f(e)) ≡ ∅, si f ∈ FSn ¥

El conjunto de variables seguras de un termino tambien podrıa definirse como el con-junto de variables que no desaparece al formar la cascara del termino (vease 3.11), sinembargo, la definicion que hemos hecho aporta una vision mas precisa del comportamien-to de estas variables en el contexto actual.

Algunas cuestiones acerca de la notacion que se usara en las reglas del calculo son lassiguientes:

Como ya se ha comentado al principio de la seccion, las reglas llevan a cabo trans-formaciones en los objetivos procesando aquellas condiciones de ındice (o prioridad)maxima. Para simplificar las reglas, este ındice se notara con m en todas ellas.

Si C es un conjunto de restricciones, Cn denota el mismo conjunto en el cual todaslas restricciones tienen n como ındice asociado. Lo mismo para las desigualdades δ.

Cuando una igualdad esta en forma resuelta, X == t con X ∈ V y t ∈ CTermpasara a la parte de sustitucion solo si la variable X es relevante, es decir, si noha sido introducida en algun paso de computo como variable existencial, sino queaparecıa en el objetivo original. Para anotar estas igualdades (y solo ellas) en laparte de sustitucion introducimos la siguiente notacion:

Si G ≡ ∃U.P2R2σ2δ entonces:

(G ]X = t) ≡{ ∃U.P2R2X = t, σ2δ si X 6∈ evar(G)∃(U − {X}.P2R2σ2δ si X ∈ evar(G)

Si X no es exitencial se anade la igualdad (resuelta) a la parte de sustitucion y sies exitencial deja de serlo. En realidad, en las reglas donde se utiliza la notacion ](Obind,Bind,Imit== y Imit6=) la variable X se reemplazara por un c-termino y des-aparecera de las producciones, la parte de resolucion y las desigualdades; para X soloquedara a lo sumo una igualdad en σ.

REGLAS DE LNC6=Reglas para →

Dcp→ ∃U.c(e) m→ c(t), P2R2σ2δ Ã ∃U.em→ t, P2R2σ2δ

Obind U.Xm→ c(t), P2R2σ2δ, δX Ã ∃U.(P2δm

X , R2σ2δ)[X/c(t)] ]X = c(t)si X 6∈ suspV ar(G)

Ibind ∃Y, U.Xm→ Y, P2R2σ2δ Ã ∃U.(P2R)[Y/X]2σ2δ

Sus ∃U.l(e) m→ X,P2R2σ2δ Ã ∃U.l(e) 0→ X, P2R2σ2δsi l ∈ DC ∪DF

Nrw ∃U.f(e) m→ c(t), P2R2σ2δ Ã ∃Y ,U.em+1→ s, s

m→ c(t), P2Cm, R2σ2δsi f(s) = s <== C es una variante de una regla de R con variables nuevas Y

Page 147: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 147

Reglas de activacion de suspensiones

Act1 ∃U.f(e) 0→ X, P2R2σ2δ Ã ∃Y , U.em+2→ s, s

m+1→ X,P2Cm+1, R2σ2δ

si (Xm3 e′) ∈ R o (X m→ c(t)) ∈ P ) y

f(s) = s <== C es una variante de una regla de R con variables nuevas Y

Act2 ∃X, U.c(e) 0→ X, P2R2σ2δ Ã ∃Y , U.em+1→ Y , (P2R)[X/c(Y )]2σ2δ

si (Xm3 e′) ∈ R o (X m→ d(t)) ∈ P , y Y variables nuevas

Reglas para == y 6=

Eval ∃U.P2f(e)m3 e′, R2σ2δ Ã ∃Y, U.f(e) m+1→ Y, P2Y

m3 e′, R2σ2δ

si 3 ∈ {==, 6=} y Y variable nueva

Reglas para ==

Dcp== ∃U.P2c(e) m== c(e′), R2σ2δ Ã ∃U.P2em== e′, R2σ2δ

Id ∃U.P2Xm== X,R2σ2δ Ã ∃U.P2R2σ2δ

si X 6∈ suspvar(G)

Bind ∃U.P2Xm== Y, R2σ2δ, δX Ã ∃U.(P2δm

X , R2σ2δ)[X/Y ] ]X = Ysi X 6≡ Y y X, Y 6∈ suspvar(G)

Imit== ∃U.P2Xm== c(e), R2σ2δ, δX Ã ∃Y ,U.(P2δm

X , Ym== e,R2σ2δ)[X/c(Y )] ]X =

c(Y )si X 6∈ suspvar(G), X 6∈ svar(c(e)), Y variables nuevas

Reglas para 6=

Clash ∃U.P2c(e)m6= d(e′), R2σ2δ Ã ∃U.P2R2σ2δ

si c 6≡ d

Store ∃U.P2Xm6= t, R2σ2δ Ã ∃U.P2R2σ2δ,X 6= t

si X 6∈ suspvar(G), t ∈ CTerm, suspvar(G) ∩ var(t) = ∅

Dcp6= ∃U.P2c(e)m6= c(e′), R2σ2δ Ã ∃U.P2ei

m6= e′i, R2σ2δ

si i ∈ {1, ..., n}

Imit6= ∃U.P2Xm6= c(e), R2σ2δ, δX Ã elegir uno de G1, G2 donde

G1 ≡ ∃U, Y .(P2δmX , R2σ2δ)[X/d(Y )] ]X = d(Y ), siendo d ∈ DC, d 6≡ c

G2 ≡ ∃U, Y .(P2Yi

m6= ei, δ

mX , R2σ2δ)[X/c(Y )] ]X = c(Y )

si X 6∈ suspvar(G), (c(e) 6∈ CTerm∨var(c(e))∩suspvar(G) 6= ∅), Y variables nuevas

Reglas de fallo

Fail1 ∃U.c(e) m→ d(e′), P2R2σ2δ Ã FAIL, si c 6≡ d

Fail2 ∃U.P2c(e) m== d(e′), R2σ2δ Ã FAIL, si c 6≡ d

Fail3 ∃U.P2Xm== e,R2σ2δ Ã FAIL, si X 6≡ e,X ∈ svar(e)

Page 148: Un lenguaje lógico funcional con restricciones1

148 CAPITULO 4. DE LA SEMANTICA

Fail4 ∃U.P2Xm6= X, R2σ2δ Ã FAIL

Fail5 ∃U.P2cm6= c,R2σ2δ Ã FAIL ¥

Como puede apreciarse hemos separado las reglas que tratan las aproximaciones delas reglas para resolver restricciones. Entre las de aproximaciones, la primera es una reglasencilla de descomposicion, la segunda y la tercera son de ligadura de variables. En Obind,cuando una variable X se liga a un c-termino, es posible que algunas de las desigualdadesen forma resuelta de δ dejen de estar en dicha forma. Esto puede ocurrirle solo a lasdesigualdades de δX (las que tienen X en uno de los miembros) y por eso δX pasa a laparte de resolucion.

La cuarta regla suspende todas aquellas aproximaciones que tienen una variable en sulado derecho y permaneceran suspendidas hasta que dicha variable sea necesitada por elcomputo. Esta necesidad es detectada en las dos reglas de activacion y se produce cuandola variable en cuestion aparece en alguno de los miembros de una restriccion. Notese queno solo se suspenden las reducciones de funciones, sino tambien las aproximaciones deconstructoras.

La primera regla de activacion y Nrw son reglas de estrechamiento que utilizan reglasde reescritura de [R]⊥. En ambas, las prioridades juegan un papel fundamental para hacerel paso de argumentos en primer lugar. En Act1 los ındices son una unidad mayores queen Nrw para asegurar que la reduccion del cuerpo de la funcion se procese antes que lacondicion que ha provocado la activacion de la suspension.

En Act2, en presencia de una suspension de la forma c(e) → X que ha despertado, seprocede por imitacion, es decir, se liga X con un termino c(Y ) que recoge la estructuramas externa de c(e) y se plantean las aproximaciones pertinentes. De este modo no esnecesario comprobar si c(e) es o no un c-termino.

La regla Eval es la contrapartida operacional a la regla (5) de GORC6=. La aproxima-

cion f(e) m+1→ Y se suspendera automaticamente. De hecho, podrıa suspenderse en estaregla, pero hemos preferido agrupar todas las suspensiones en la misma regla.

Para la resolucion de igualdades entre formas normales de cabeza, Dcp== es una reglade descomposicion, y tanto Id como Bind son de ligadura de variables. En esta ultimaobservese que tambien δX pasa a la parte de resolucion por el mismo motivo que enObind. La ultima regla, Imit==, nuevamente evita comprobar que el segundo miembroes un c-termino y procede por imitacion.

Para las desigualdades entre formas normales de cabeza, Clash corresponde a la regla(9) de GORC6= y Dcp6= a la (8). En Store se detecta la presencia de una desigualdad enforma resuelta y se pasa a δ; en este caso, sı es necesario hacer el test de que el segundomiembro sea un c-termino. La regla Imit6= es un eleccion indeterminista (indeterminismodon’t care) para resolver una desigualdad de la forma X 6= c(e). En este caso se puedeproceder de dos formas, del mismo modo que hace T OY: ligando X con una constructorad(Y ) distinta de c, o bien imitando la estructura y planteando la desigualdad entre algunpar de argumentos.

Sobre las reglas de fallo, la unica que merece algun comentario es Fail3, que correspondeal occurs-check: una desigualdad entre una variable y un termino cuya cascara contiene adicha variable, automaticamente produce un fallo.

El calculo LNC 6= que acabamos de presentar esta guiado en gran medida por la estruc-tura sintactica mas externa de los terminos (igual que GORC6=), aunque contiene ciertogrado de indeterminismo en Imit6=. Debemos probar la bondad de los pasos de computo

Page 149: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 149

que pueden hacerse con este calculo, con independencia de la eleccion de la regla que seaplica, en el caso de que haya varias posibles. Y esto es lo que veremos de manera masformal con los resultados de correccion y completitud.

Veamos ahora un ejemplo de computo con LNC6=.

Ejemplo 2Consideremos un programa R con las funciones f y coin definidas como en el ejemplo

de la seccion anterior. Una solucion para el objetivo f(coin) 6= [X|Xs] puede ser calculadadel siguiente modo:

2f(coin)16= [X|Xs]22

EvalÃ

∃Z1.f(coin) 2→ Z12Z1

16= [X|Xs]22

SusÃ

∃Z1.f(coin) 0→ Z12Z1

16= [X|Xs]22

Act1Ã

∃Z1, Z2.coin3→ Z2, [Z2|f(s(Z2))]

2→ Z12Z1

16= [X|Xs]22

Sus,SusÃ

∃Z1, Z2.coin0→ Z2, [Z2|f(s(Z2))]

0→ Z12Z1

16= [X|Xs]22

Act2Ã

∃Z2, Z3, Z4.coin0→ Z2, Z2

3→ Z3, f(s(Z2))2→ Z42[Z3|Z4]

16= [X|Xs]22

IbindÃ

∃Z2, Z4.coin0→ Z2, f(s(Z2))

2→ Z42[Z2|Z4]16= [X|Xs]22 Ã Sus

∃Z2, Z4.coin0→ Z2, f(s(Z2))

0→ Z42[Z2|Z4]16= [X|Xs]22

Dcp6=Ã

∃Z2, Z4.coin0→ Z2, f(s(Z2))

0→ Z42Z2

16= X22 Ã Act1

∃Z2, Z4.Z23→ 0, f(s(Z2))

0→ Z42Z2

16= X22

ObindÃ

∃Z4.f(s(0)) 0→ Z42016= X22

StoreÃ∃Z4.f(s(0)) 0→ Z4222X 6= 0

Al final del computo hemos obtenido en σ la forma resuelta X 6= 0, que es una solucional objetivo inicial. Mas adelante definiremos con precision forma resuelta y solucion. Porel momento es suficiente observar que si en el objetivo f(coin) 6= [X|Xs] reemplazamos Xpor cualquier valor distinto de 0 se verifica la desigualdad, supuesto que coin se reduce a0 (que es una de las posibilidades). Otras respuestas (hay infinitas) que pueden derivarseson X 6= s 0 o Xs = [ ]. ¥

Nuestro interes se centra ahora en aquellos objetivos que son alcanzables a partir de unobjetivo inicial mediante derivaciones en LNC6=. La definicion que hemos dado de objetivopara LNC6= establece la forma sintactica de los objetivos, sin embargo, en la construcciondel calculo se ha puesto especial cuidado en que estos objetivos mantengan invariantes unaspropiedades que seran fundamentales posteriormente para la demostracion de correcciony completitud. La siguiente definicion de objetivo admisible captura estas propiedades.

Notacion: En lo sucesivo, para abreviar utilizaremos con frecuencia la notacion e[X](X ∈ V) para expresar X ∈ var(e). No hay posibilidad de confusion con las sustitucionesque son de la forma [X/t].

Definicion 4 (Objetivo admisible) Un objetivo G ≡ ∃U.P2R2σ2δ es admisible sicumple las condiciones siguientes:

Page 150: Un lenguaje lógico funcional con restricciones1

150 CAPITULO 4. DE LA SEMANTICA

Suspensiones. Si l0→ r ∈ P , entonces r ∈ V (todas las suspensiones son de la

forma l0→ X).

Aciclicidad Consideremos la relacion X À Y ⇔ ∃(l α→ r) ∈ P ∧ l[X] ∧ r[Y ]. QueP sea acıclico significa que el cierre transitivo de À es irreflexivo (no existe X talque X

∗À X).

Linealidad. Si P ≡ l1α1→ t1, ..., ln

αn→ tn la tupla (t1, ..., tn) debe ser lineal.

Produccion.

• pvar(G) ⊆ evar(G);

• pvar(G) ∩ var(σ) = ∅. Ademas, si (X = t) ∈ σ entonces X solo tiene estaocurrencia en todo el objetivo G;

• pvar(G) ∩ var(δ) = ∅.

Orden.

a) Sea aα→ b[Z] ∈ P con α 6= 0. Entonces:

a.1) Si c[Z]β→ d ∈ P , entonces α > β.

a.2) Si (cβ3 d)[Z] ∈ R, entonces α > β.

b) Si a0→ Z, c

α→ d ∈ P , entonces var(a) ∩ var(d) = ∅ ¥

En primer lugar observemos que un objetivo inicial es admisible. Las propiedades deaciclicidad, linealidad y produccion son propiedades que ya se proponıan en el calculoLNC de [GHL+96, GHL+98].

Podemos relajar la definicion de aciclicidad y enunciar una version mas debil sin perdidade generalidad: si l

α→ r ∈ P entonces var(l) ∩ var(r) = ∅. No hay ninguna perdida deinformacion porque con esta definicion mas la condicion de orden se tiene la version fuertedel enunciado: supongamos que en P hay un ciclo, es decir, existe una variable X talque X À X. Por la version debil de aciclicidad no puede existir una regla de la formal[X] α→ r[X], luego el ciclo debe producirse por una secuencia (obviamente finita puestoque P es finito) de la forma l1[X] α1→ r1[Y1], l2[Y1]

α2→ r2[Y2], ..., lm[Ym−1]αm→ rm[X]. Ahora

bien, si α1 6= 0 no puede ser α2 = 0 porque se tendrıa l2[Y1]0→ r2[Y2], l1[X] α1→ r1[Y1] ∈

P , pero por la propiedad de orden (b), Y1 no puede aparecer en l2 y en r1. De hecho,se tiene αi > 0 para todo i = 1..m, y por la condicion de orden (a.1) tendrıamos lacadena α1 > α2 > ... > αm > α1 que claramente es una contradiccion; y si α1 = 0,nuevamente por la propiedad de orden (b), X no puede aparecer en ningun lado derechode las aproximaciones, por lo que la primera y ultima aproximaciones de la secuencia departida no pueden aparecer en P .

Por lo que acabamos de exponer, en el lema siguiente de preservacion de admisibili-dad trabajaremos con la version debil de aciclicidad, que es mas sencilla de tratar. Esteresultado sera fundamental para las demostraciones de correccion y completitud.

Lema 5 (Preservacion de la admisibilidad) Si G es un objetivo admisible y G Ã G′

entonces G′ tambien es admisible.

Page 151: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 151

Demostracion

Vamos a dividir la demostracion de este resultado en tres partes:

Si G es un objetivo admisible y G Ã G′ entonces G′ verifica la propiedad desuspensiones.

Si G es un objetivo admisible y G Ã G′ entonces G′ verifica la propiedad de orden.

Si G es un objetivo admisible y G Ã G′ entonces G′ verifica las propiedades deaciclicidad, linealidad y produccion

El lema sera un corolario de estos dos resultados parciales.

Parte1La propiedad de suspensiones es facil de demostrar. Observese que las suspensiones que

introduce Sus son de la forma l(e) 0→ X y esta es la unica regla que introduce suspensiones.La unica posibilidad para que una suspension deje de tener una variable en su lado

derecho es por aplicacion de una sustitucion en las reglas del calculo. Estudiemos las reglasque aplican sustituciones:

En Obind,Imit==, Imit6= y Bind se comprueba expresamente que la variable que sesustituye no esta suspendida (X 6∈ suspvar(G)).

En Ibind se sustituye una variable por otra, con lo que las suspensiones seguiranteniendo una variable en el lado derecho.

Y en Act2, la variable X que se sustituye esta suspendida (no puede haber otrasuspension con X en el lado derecho por linealidad), pero en G′ desaparece la suspension(de hecho desaparece la variable X).

Parte 2Procedemos por analisis de casos sobre la regla aplicada. En lo sucesivo suponemosG ≡ ∃U.P2R2σ2δ yG′ ≡ ∃U ′.P ′2R′2σ′2δ′

Dcp→ a.1) Supongamos u1 ≡ aα→ b[Z], u2 ≡ c[Z]

β→ d ∈ P ′. Pueden darse los casossiguientes:Si u1, u2 ∈ P , por hipotesis α > β.Si ninguna aparecıa en P , sino que han sido introducidas por la regla, debenser de la forma ei

m→ ti. En P tenıamos c(e) m→ t y por aciclicidad var(c(e)) ∩var(c(t)) = ∅, que implica var(ei) ∩ var(tj) = ∅, ∀i, j ∈ {1..n}, pero entoncesu1 y u2 no pueden tener la forma que suponıamos al principio y llegamos a unacontradiccion.Si u1 es nueva, u1 ≡ ei

m→ ti[Z], con α = m, y u2 ∈ P , entonces en P tenıamosc(e) m→ c(t)[Z] y por hipotesis α = m > β.Si u2 es nueva, u2 ≡ ei[Z] m→ ti, con β = m, y u1 ∈ P , entonces en P tenıamosc(e)[Z] m→ c(t) y por hipotesis α > β = m, pero entonces no podrıamos aplicarla regla Dcp→.

a.2) Suponemos u1 ≡ aα→ b[Z] ∈ P ′, r ≡ (c

β3 d)[Z] ∈ R′.

Si u1 ∈ P , como tambien r ∈ R, por hipotesis α > β.Si u1 es nueva, u1 ≡ ei

m→ ti[Z], con α = m, en P tenıamos c(e) m→ c(t)[Z] y en

R, (cβ3 d)[Z], y por hipotesis α = m > β.

Page 152: Un lenguaje lógico funcional con restricciones1

152 CAPITULO 4. DE LA SEMANTICA

b) Como los conjuntos de variables en expresiones a izda. y dcha. de → no semodifican y tampoco lo hacen R y δ, por hipotesis se verifica el resultado.

Obind Sea θ ≡ [X/c(t)].

a.1) Supongamos u1 ≡ aθα→ bθ[Z], u2 ≡ cθ[Z]

β→ dθ ∈ P ′. Como en esta regla no

se introducen producciones nuevas tenemos Xm→ c(t), a α→ b, c

β→ d ∈ P . Su-pongamos Z 6∈ var(b), como Z ∈ var(bθ) debe ser Z ∈ var(c(t)) y X ∈ var(b),pero serıa α > m y no se podrıa aplicar esta regla. Por lo tanto Z ∈ var(b). Porlinealidad Z 6∈ var(c(t)), luego Z ∈ var(c). Entonces tenemos X

m→ c(t), a α→b[Z], c[Z]

β→ d ∈ P y por hipotesis α > β.

a.2) Igual que antes debe ser Z ∈ var(b), Z 6∈ var(c(t)) y tenemos Xm→ c(t), a α→

b[Z] ∈ P . Si (cθβ3 dθ)[Z] ∈ R′, puede provenir de R o de una desigualdad de

δX .

En el primer caso serıa (cβ3 d)[Z] ∈ R y por hipotesis α > β.

En el segundo caso como Z ∈ var(b), Z es producida y por las condiciones deadmisibilidad Z 6∈ var(c 6= d). Deberıa ser introducida en esta desigualdad porθ. Pero Z 6∈ var(c(t)), por lo que este segundo caso no puede darse.

b) Supongamos aθ0→ Zθ, cθ

α→ dθ ∈ P ′. Como Dcp→ no introduce suspensionesni producciones nuevas tenemos X

m→ c(t), a 0→ Z, cα→ d ∈ P y por hipotesis

var(a) ∩ var(d) = ∅ (1). Por linealidad var(c(t)) ∩ var(d) = ∅ (2). No puedeser X ∈ var(d) porque serıa α > m, por lo que dθ ≡ d (3). Por otro ladovar(aθ) ⊆ var(a) ∪ var(c(t)) (4). De este modo tenemos:

var(aθ)∩var(dθ)(3)= var(aθ)∩var(d)

(4)= (var(a)∪var(c(t))∩var(d) = (var(a)∩

var(d)) ∪ (var(c(t)) ∩ var(d))(1)(2)= ∅

Ibind Sea θ ≡ [Y/X].

a.1) Supongamos aθα→ bθ[Z], cθ[Z]

β→ dθ ∈ P ′. En P debemos tener Xm→ Y, a

α→b, c

β→ d. Por linealidad Y 6∈ var(b), entonces bθ = b y debe ser Z ∈ b. SiZ 6∈ var(c) deberıa aparecer en cθ por la sustitucion, lo que implicarıa Z ≡ X.En P tendrıamos Z

m→ Y, aα→ b[Z] y serıa α > m y no se podrıa aplicar la regla.

Debe ser por lo tanto Z ∈ var(c) por lo que en P tenemos aα→ b[Z], c[Z]

β→ dy por hipotesis α > β.

a.2) Si aθα→ bθ[Z] ∈ P ′, cθ

β3 d ∈ R′. Igual que antes, por linealidad Z ∈ var(b).

Si Z 6∈ var(cβ3 d), como antes serıa Z ≡ X y llegarıamos a contradiccion, por

lo que Z ∈ var(cβ3 d). En P tenemos por tanto a

α→ b[Z], c[Z]β→ d y por

hipotesis α > β.

b) Si aθ0→ Zθ, cθ

α→ dθ ∈ P ′ en P tendremos Xm→ Y, a

0→ Z, cα→ d. Por

linealidad Y 6∈ var(b), luego bθ = b. No puede ser X ∈ var(b), ya que serıaα > m, por tanto X 6∈ var(b). Por otro lado var(aθ) ⊆ var(a) ∪ {X} y porhipotesis var(a) ∩ var(d) = ∅. Ası tenemos var(aθ) ∩ var(d) = ∅.

Sus a.1) Trivial.

Page 153: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 153

a.2) Trivial.

b) Lo unico que hay que probar es que la nueva suspension introducida verifica elteorema. Supongamos que no es cierto, es decir, l(e)[Y ] 0→ X, a

α→ b[Y ] ∈ P ′,entonces en P tenıamos l(e)[Y ] m→ X, a

α→ b[Y ] y por hipotesis serıa α > m yno se podrıa aplicar la regla.

Nrw a.1) Sean u1 ≡ aθα→ b[Z], u2 ≡ c[Z]

β→ d ∈ P ′. Analicemos las posibles situaciones:

Si u1 es nueva, u1 ≡ eim+1→ si[Z] (α = m + 1), como todas las variables de s

son nuevas, y en particular Z, no puede ser u2 ≡ ej [Z] m+1→ sj , y el resto decondiciones tienen prioridad menor que m + 1.

Si u1 ≡ sm→ c(t)[Z], con m = α, u2 podrıa ser u2 ≡ ei[Z] m+1→ ti, pero

en P tendrıamos f(e)[Z] m→ c(t)[Z], que claramente incumple la condicion de

aciclicidad. La otra posibilidad es u2 ≡ c[Z]β→ d ∈ P , entonces en P tenıamos

f(e) m→ c(t)[Z], c[Z]β→ d y por hipotesis m = α > β.

Si u1 ≡ aα→ b[Z] ∈ P , puede ser u2 ≡ ei[Z] m+1→ si, en P tendrıamos f(e)[Z] m→

c(t), a α→ b[Z], serıa α > m y no se podrıa aplicar la regla. Tampoco puede seru2 ≡ s[Z] m→ c(t) porque las variables de s son todas nuevas y Z ∈ var(ei). Y

si fuese u2 ≡ c[Z]β→ d ∈ P , entonces u1, u2 ∈ P y por hipotesis α > β.

a.2) Supongamos u1 ≡ eim+1→ si[Z] ∈ P ′, r ≡ (c

β3 d)[Z] ∈ R′ (α = m + 1). Como

las variables de s son nuevas Z debe ser nueva y debe ser r ∈ Cm con lo queβ = m y m + 1 = α > β = m.

Si u1 ≡ sm→ c(t)[Z] ∈ P ′, r ≡ (c

β3 d)[Z] ∈ R′, como Z ∈ var(c(t)), Z no es

nueva, por lo que r ∈ R. En P tenıamos f(e) m→ c(t)[Z] y en R, (cβ3 d)[Z] y

por hipotesis m = α > β.

Si u1 ≡ aα→ b[Z] ∈ P y r ≡ (b

β3 c)[Z] ∈ R′, como las variables de Cm son

todas nuevas tiene que ser r ∈ R y por hipotesis α > β.

b) Si e0→ [Z] ∈ P ′ tiene que ser e

0→ [Z] ∈ P . Por hipotesis, como var(f(e)) ∩var(c(t)) = ∅ y como var(s) son todas nuevas, var(f(e))∩ var(s) = ∅. El restode lados derechos ya aparecıan en P y por hipotesis cumplen la condicion.

Act1 a.1) Sean u1 ≡ aα→ b[Z], u2 ≡ c[Z]

β→ d ∈ P ′.

Si fuese u1 ≡ eim+2→ si[Z] no podrıa ser u2 ≡ ej [Z] m+2→ sj porque las varia-

bles de s son todas nuevas y no pueden aparecer en ej . Para cualquier otraposibilidad de u2, con seguridad la prioridad sera menor que m + 2.

Si u1 ≡ sm+1→ X no puede ser u2 ≡ ei[Z] m+2→ si porque en P tendrıamos

f(e)[Z] m→ Z que incumple la linealidad y si u2 ≡ c[Z]β→ d ∈ P , como por

hipotesis β < m, sera β < m + 1.

Si u1 ≡ aα→ b[Z] ∈ P no puede ser u2 ≡ ei[Z] m+2→ si porque en P tendrıamos

aα→ b[Z], f(e)[Z] 0→ X, que incumple la parte b) de la hipotesis. Tampoco

puede ser u2 ≡ s[Z] m+1→ X porque las variables de s son todas nuevas y no

pueden, por tanto aparecer en b. Y si u2 ≡ c[Z]β→ d ∈ P , como u1, u2 ∈ G, por

hipotesis α > β.

Page 154: Un lenguaje lógico funcional con restricciones1

154 CAPITULO 4. DE LA SEMANTICA

a.2) Supongamos u1 ≡ aα→ b[Z] ∈ P ′, r ≡ (c

β3 d)[Z] ∈ R′.

Si u1 ≡ eim+2→ si[Z] (α = m + 2), Z serıa nueva y solo puede ser r ∈ Cm+1

(β = m + 1), con lo que α > β.

Si u1 ≡ sm+1→ X, siendo X ≡ Z y α = m + 1 no puede ser r ∈ Cm+1 porque

las variables de Cm+1 son todas nuevas y por tanto X 6∈ Cm+1. Si r ≡ Xm3 e′

con β = m, tenemos m + 1 = α > β. Si r ≡ cβ3 d[X] ∈ R, tiene que ser

m + 1 = α > β para poder aplicar la regla.Si u1 ≡ a

α→ b[Z] ∈ P , debe ser α < m. Para poder aplicar la regla no puede serr ∈ Cm+1 porque las variables de Cm+1 son todas nuevas. Si (r ≡ X

m3 e′)[Z],

entonces u1 ∈ P y r ∈ R y por hipotesis α > β = m. Si r ≡ (cβ3 d)[Z] ∈ R,

como antes, por hipotesis α > β.

b) Supongamos u ≡ a0→ Z, r ≡ c

α→ d ∈ P ′. Como la regla no introduce suspen-siones nuevas tiene que ser u ∈ P .Si u ∈ G, por hipotesis var(a) ∩ var(r) = ∅.Si u ≡ ei

m+2→ si, como las variables de si son nuevas var(a) ∩ var(si) = ∅.Si u ≡ s

m+1→ X. Por la parte b) de la hipotesis var(a) ∩ {X} = ∅ porque Xaparece en un lado derecho de una produccion.

Act2 Sea θ ≡ [X/c(Y )]

a.1) Suponemos u1 ≡ aθα→ bθ[Z], u2 ≡ cθ[Z]

β→ dθ ∈ P ′.

No puede ser u1 ≡ eim+1→ Yi (Z ≡ Yi), porque en P tendrıamos c(e)[Yi]

0→ X ycomo Yi es nueva no podıa aparecer en P .Si u1 proviene de una produccion en P de la forma a

α→ b sabemos por linealidadque X 6∈ var(b), lo que implica bθ = b y debe ser Z ∈ var(b). Para u2 tenemosdos casos:No puede ser u2 ≡ ei[Z] m+1→ Yi porque serıa Z ∈ var(c(e)) y en G tendrıamosvar(c(e)) ∩ var(b) 6= ∅, lo que contradice hipotesis b).

Si u2 proviene de una produccion de P tenemos c(e) 0→ X, aα→ b[Z], c

β→ d ∈ P .Tiene que ser Z ∈ var(c), porque de lo contrario Z aparecerıa en cθ debido a lasustitucion θ, lo que implicarıa Z = Yi para algun i y Z serıa nueva, pero estono puede ser porque ya aparecıa en G (en b). Por lo tanto Z ∈ var(c) y en P

tenemos aα→ b[Z], c[Z]

β→ d, y por hipotesis α > β.

a.2) Supongamos u ≡ aθα→ bθ[Z], r ≡ (cθ

β3 dθ)[Z]. Como antes, u proviene de una

produccion de P , aα→ b y debe ser Z ∈ var(b). Luego en P tenemos a

α→ b[Z].

En R debıamos tener la restriccion cβ3 d y Z tiene que aparecer en ella, porque

de lo contrario, la habrıa introducido la sustitucion θ; luego serıa Z = Yi paraalgun i nueva, pero Z ya aparece en P luego no ha sido introducida por θ.

Luego en P tenıamos aα→ b[Z] y en R (c

β3 d)[Z], y por hipotesis tenemos

α > β.

b) Supongamos u ≡ aθ0→ Zθ, r ≡ cθ

α→ dθ ∈ P ′. Como la regla no introducesuspensiones nuevas debe ser a

0→ Z ∈ P . Para r tenemos dos opciones:

Page 155: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 155

Si cα→ d ∈ P . Entonces en P tenemos c(e) 0→ X, a

0→ Z, cα→ d ∈ P . Por

hipotesis tenemos que var(a)∩ var(d) = ∅ (1). Por linealidad X 6∈ var(d) luegodθ = d (2) y Yi 6∈ var(d),∀i = 1..n (3). Y por ultimo, var(aθ) ⊆ var(a) ∪{Yi}i=1..n (4). Uniendo resultados:

var(aθ) ∩ var(dθ)(2)= var(aθ) ∩ var(d)

⊆(4) (var(a) ∪ {Yi}i=1..n) ∩ var(d) =

(var(a) ∩ var(d)) ∪ ({Yi}i=1..n ∩ var(d))(1)(3)= ∅.

Si r ha sido introducida por la regla, entonces no es afectada por θ y r ≡ei

m+1→ Yi ∈ P ′, en P tenıamos a0→ Z, c(e) 0→ X. Como Yi es nueva, para

que Yi ∈ var(a) debe ser X ∈ var(a), pero entonces en P tendrıamos a[X] 0→Z, c(e) 0→ X que contradice la hipotesis Luego r no puede haber sido introducidapor la regla.

Eval a.1) La unica → nueva es f(e) m+1→ Z y como Y es nueva no puede aparecer enninguna otra produccion. Tampoco podemos tener a

α→ b[Z], f(e)[Z] m+1→ Y ∈P ′ porque en P tendrıamos a

α→ b[Z], (f(e)m3 e′)[Z], serıa α > m y no se podrıa

aplicar la regla.

a.2) El unico par de produccion y restriccion que cumple las hipotesis del teoremay que no aparecıa en P es f(e) m+1→ Y, Y

m3 e′ y se cumple m + 1 > m.

b) La unica produccion nueva que se ha introducido tiene una variable nueva ensu lado derecho que, no puede por tanto, aparecer en ningun lado izquierdo deuna suspension.

Dcp== a.1) Trivial porque las producciones y las suspensiones no cambian.

a.2) Trivial porque se reemplaza una restriccion por otra con el mismo ındice y lasmismas variables.

b) Idem que a).

Id a.1) Trivial porque las producciones y las suspensiones no cambian.

a.2) Trivial porque todas las restricciones de P ′ aparecıan tambien en P .

b) Idem que a).

Bind Sea θ ≡ [X/t]

a.1) Supongamos u1 ≡ aθα→ bθ[Z], u2 ≡ cθ[Z]

β→ dθ ∈ P ′, entonces en P debıamos

tener aα→ b, c

β→ c. Tiene que ser Z ∈ var(b) porque de lo contrario la habrıaintroducido θ y serıa Z ∈ var(t) y X ∈ var(b), pero entonces en P tendrıamosa

α→ b[X], X m== t y no se podrıa aplicar la regla porque por hipotesis α >m. Por tanto Z ∈ var(b). Por otro lado debe ser Z ∈ var(c), ya que de otromodo igual que antes serıa X ∈ var(c) y Z ∈ var(t) y tendrıamos en P a

α→b[Z], X m== t[Z] y no se podrıa aplicar la regla porque por hipotesis α > m.

Luego Z ∈ var(c) y en P tenemos aα→ b[Z], c[Z]

β→ d y por hipotesis α > β.

a.2) Supongamos u ≡ aθα→ bθ[Z] ∈ P ′, r ≡ (cθ

β3 dθ)[Z] ∈ P ′. Como antes tiene

que ser Z ∈ var(b). Casos para r:

Page 156: Un lenguaje lógico funcional con restricciones1

156 CAPITULO 4. DE LA SEMANTICA

Si r proviene de R, r ≡ cβ3 d debe ser Z ∈ var(c

β3 d) ya que de lo contrario

serıa X ∈ var(cβ3 d y Z ∈ var(t) y en P tendrıamos a

α→ b[Z], X m== t[Z],

serıa α > m y no se podrıa aplicar la regla. Por tanto Z ∈ var(cβ3 d), en P

tenemos aα→ b[Z], c

β3 d y por hipotesis α > β.

Si r proviene de δX , como Z ∈ var(b), Z es producida y no puede por tan-to aparecer en δ y en particular no puede aparecer en δX . Entonces tendrıaque haber sido introducida por θ, siendo Z ∈ var(t), pero esto como antes escontradictorio porque serıa α > m.

b) Supongamos aθ0→ Zθ, cθ

α→ dθ ∈ P ′. No puede ser Z ≡ X porque Z ∈ pvar(G)y X 6∈ pvar(G), luego Zθ ≡ Z. Por otro lado, no puede ser X ∈ var(d)porque en P tendrıamos c

α→ d[X], X m== t y serıa α > m, luego dθ ≡ d (1).Por la misma razon d no puede contener variables que aparezcan en t, luegovar(d) ∩ var(t) = ∅ (2). Tenemos por hipotesis var(a) ∩ var(d) = ∅ (3), ytambien sabemos que var(aθ) ⊆ var(a) ∪ var(t) (4), luego:

var(aθ) ∩ var(dθ)(1)= var(aθ) ∩ var(d)

(4)

⊆ (var(a) ∪ var(t) ∩ var(d)=

( var(a) ∩var(d)) ∪ (var(t) ∩ var(d))

(2)(3)= ∅.

Imit== Sea θ ≡ [X/c(Y )]

a.1) Supongamos u1 ≡ aθα→ bθ[Z], u2 ≡ cθ[Z]

β→ dθ. En P tendremos aα→ b, c

β→ d.Tiene que ser Z ∈ var(b) porque de lo contrario habrıa sido introducida por θ,serıa X ∈ var(b) y por hipotesis tendrıamos α > m y no se podrıa aplicar laregla, luego Z ∈ var(b). Por otro lado, si Z 6∈ var(c), serıa introducida por θ,pero θ solo introduce variables nuevas y Z ∈ var(b), luego Z ∈ var(c). Ası, en

P tenemos aα→ b[Z], c[Z]

β→ d y por hipotesis α > β.

a.2) Supongamos u ≡ aθα→ b ∈ P ′, r ≡ (cθ

β3 dθ)[Z] ∈ R′. Igual que antes tiene que

ser Z ∈ var(b). Para r tenemos tres posibilidades:

Si proviene de R, en R tenemos cβ

== d. Tiene que ser Z ∈ var(cβ→ d) porque

θ solo introduce variables nuevas y Z ya aparecıa en var(b), por lo que en G

tenemos aα→ b[Z], (c

β→ d)[Z] y por hipotesis es α > β.Si es de la forma r ≡ Yiθ

m== eiθ, tiene que ser Z ∈ var(ei) ya que θ solointroduce variables nuevas. Pero entonces en P tenemos a

α→ b[Z] y en R,X

m== c(e)[Z] y por hipotesis serıa α > m y no podrıamos aplicar la regla,luego esta opcion no es posible.Si r proviene de una desigualdad de δX , Z no puede aparecer en dicha des-igualdad porque Z ∈ pvar(G) y δ no contiene variables producidas. Ademas,no puede haberla introducido θ que solo introduce variables nuevas.

b) Supongamos s ≡ aθ0→ Zθ, cθ

α→ dθ ∈ P ′. Como la regla no introduce sus-pensiones ni producciones nuevas en P tenemos a

0→ b, cα→ d. Como X 6∈

pvar(G) no puede aparecer en ningun lado derecho de una produccion, lue-go Zθ ≡ Z y dθ ≡ d (1). Por otro lado, como las variables Y son nuevas,{Yi}i=1..n ∩ var(d) = ∅ (2), sabemos var(aθ) ⊆ var(a) ∪ {Yi}i=1..n (3) y porhipotesis var(a) ∩ var(d) = ∅ (4). Uniendo resultados:

Page 157: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 157

var(aθ) ∩ var(dθ)(1)= var(aθ) ∩ var(d)

(3)

⊆ (var(a) ∪ {Yi}i=1..n) ∩ var(d) =

(var(a) ∩ var(d)) ∪ ({Yi}i=1..n ∩ var(d))(2)(4)= ∅.

Clash a.1) Trivial porque las producciones y suspensiones no cambian.

a.2) Trivial por lo mismo y porque desaparece una restriccion.

b) Idem que a.1)

Store a.1) Idem que antes.

a.2) Idem.

b) Idem.

Dcp6= a.1) Idem.

a.2) Trivial porque las variables de la restriccion nueva que aparece son un subcon-junto de las variables de la que desaparece.

b) Idem.

Imit6= Sea θ ≡ [X/d(Y )], siendo d ∈ DCn. En este caso nos da igual que sea d = c o no; loque nos interesa es que θ reemplaza X por una expresion que solo contiene variablesnuevas.

a.1) Supongamos u1 ≡ aθα→ bθ[Z], u2 ≡ cθ[Z]

β→ dθ. Como X 6∈ pvar(G), X 6∈var(b) y bθ = b, luego Z ∈ var(b). Como θ solo introduce variables nuevas yZ ∈ var(b) (no es nueva) tiene que ser Z ∈ var(c). Ası en P tenemos a

α→b[Z], c

β→ c y por hipotesis α > β.

a.2) Supongamos u ≡ aθα→ bθ[Z] ∈ P ′, r ≡ (cθ

β3 d)[Z] ∈ R′. Igual que antes

Z ∈ var(b). Casos para r:

Si r proviene de una restriccion de R, cβ3 d, como θ solo introduce variables

nuevas, tiene que ser Z ∈ var(cβ3 d). En G tendremos a

α→ b[Z], (cβ3 d)[Z] y

por hipotesis α > β.Si r viene de una desigualdad de δX , como Z ∈ pvar(G), dicha desigualdad nopuede contener a Z por lo que tendrıa que haber sido introducida por θ, peroθ solo introduce variables nuevas y Z ya aparecıa en G.

Si r es de la forma Yiθm6= eiθ, tiene que ser Z ∈ var(ei), ya que θ solo introduce

variables nuevas. Pero entonces en P tendrıamos aα→ b[Z], X

m6= c(e)[Z] y por

hipotesis serıa α > m y no podrıamos aplicar la regla.

b) Analogo a Imit==.

Parte 3Procedemos tambien por analisis de casos.

Dcp→ La aciclicidad y la linealidad deben cumplirse en G′ porque de lo contrario, trivial-mente no se verificarıan en G. La produccion se cumple porque los conjuntos devariables producidas y existenciales no cambian y tampoco lo hacen σ y δ.

Page 158: Un lenguaje lógico funcional con restricciones1

158 CAPITULO 4. DE LA SEMANTICA

Obind Aciclicidad: X 6∈ suspvar(G) por hipotesis y si existiese lα→ r[X] ∈ P con α > 1

serıa α > m y no serıa aplicable la regla, por lo tanto X 6∈ pvar(G). Entonceslos lados derechos de las reglas permanecen invariables. Las unicas variables que sepueden introducir en los lados izquierdos son las de c(t), que no pueden aparecen enningun lado derecho de por linealidad de G.

Linealidad: se preserva porque los lados derechos de P no cambian.

Produccion: tenemos las inclusiones evar(G′) ⊂ evar(G) y pvar(G′) ⊂ pvar(G′)y por hipotesis pvar(G) ⊆ evar(G) luego pvar(G′) ⊆ evar(G′). Por otro lado lasunicas variables que se introducen en σ son las de c(t) y la propia X, y como loslados derechos de P no cambian y no contienen variables de c(t) (linealidad) ni a Xes claro que la nueva σ′ no contiene variables producidas. En δ las unicas nuevas sonlas de c(t) que no son producidas.

Ibind Aciclicidad: Y no es producida por linealidad de G, luego los lados derechos de lasproducciones no cambian. Si en G′ se incumpliese la aciclicidad serıa por haber intro-ducido X en un lado izquierdo de una produccion cuyo lado derecho ya contuvieseX. Esta produccion en G tendrıa la forma l[Y ] α→ r[X] y por el lema de ordentendrıa que ser α = 0 ya que de lo contrario serıa α > m. Pero el propio lema deorden afirma que en esta situacion Y no podrıa aparecer en ningun lado derecho enG, hecho que es obviamente false. Luego en G′ se verifica la aciclicidad.

Linealidad: trivial porque los lados derechos de P no cambian por la aplicacion dela regla.

Produccion: tenemos pvar(G′) ⊂ pvar(G) y evar(G′) ⊂ evar(G′) (desaparece laY en ambos casos). Por hipotesis pvar(G) ⊆ evar(G) luego pvar(G′) ⊂ evar(G′).Por otro lado σ y δ no cambian (y las variables producidas disminuyen) luego nocontienen variables producidas.

Sus Trivial.

Nrw Aciclicidad: las producciones nuevas eim+1→ si tienen todas las variables de la derecha

nuevas, por lo que son acıclicas y a sm→ c(t) le ocurre lo mismo en el lado izquierdo.

El resto de producciones no cambia.

Linealidad: los nuevos lados derechos son s1, ..., sn que provienen de una regla delprograma de la forma f(s1, ..., sn) = s <== C en la que, por definicion de programala tupla s1, ...., sn es lineal. Ademas las variables de esta tupla son nuevas y no puedenaparecer en ningun lado derecho que estuviese en G.

Produccion: las nuevas variables producidas que se introducen son las de s que estanentre las nuevas Y que introduce la regla, que a su vez son existenciales en G′.σ y δ no cambian y las variables producidas nuevas no pueden aparecer en ellasprecisamente porque son nuevas.

Act1 Aciclicidad: las producciones nuevas eim+2→ si tienen todas las variables de la derecha

nuevas, por lo que son acıclicas y a sm+1→ X le ocurre lo mismo en el lado izquierdo.

El resto de producciones no cambia.

Linealidad: ıdem que en Nrw.

Produccion: ıdem que en Nrw.

Page 159: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 159

Act2 Sea θ = [X/c(Y )].

Aciclicidad: por linealidad de G, X no puede aparecer en ningun lado derecho deP , luego los lados derechos permanecen invariables por θ. Las variables que puedehaber introducido θ en los lados izquierdos son las de Y que son nuevas, por loque la aciclicidad se preserva en las aproximaciones de P al aplicar θ. Todas lasaproximaciones nuevas que se introducen tienen una variable nueva Yi en su ladoderecho, por lo que tambien son acıclicas.

Linealidad: como los lados derechos de P no cambian y las producciones nuevas sonde la forma ei

m+1→ Yi con Yi variable nueva (Yi 6= Yj para todo i 6= j) la linealidadse preserva.

Produccion: las nuevas variables producidas que se introducen son las de Y que sonclaramente existenciales en G′. La variable X ya no es existencial en G′, pero es quede hecho ya no aparece en P debido a la sustitucion θ que se aplica, y claramente noaparece en los lados derechos de las nuevas producciones que son variables nuevas,luego pvar(G′) ⊆ evar(G′). Por otro lado σ y δ no cambian y las nuevas variablesproducidas claramente no les afectan, luego pvar(G′)∩var(σ) = pvar(G′)∩var(δ) =∅.

[Eval La unica produccion nueva que se introduce es f(e) m+1→ Y que tiene una variablenueva como lado derecho (que no puede aparecer en f(e), por lo que se preservan laaciclicidad y la linealidad. Esta nueva variable producida es tambien existencial enG por lo que pvar(G′) ⊆ evar(G′), y como σ y δ no cambian y contienen a la nuevavariable tenemos pvar(G′) ∩ var(σ) = pvar(G′) ∩ var(δ) = ∅.

Dcp==,Id Triviales porque no cambian ni las producciones, ni las variables existenciales, ni σni δ.

Bind Y no puede aparecer en ningun lado derecho de P por la propiedad de orden (a.2),luego los los lados derechos de P no cambian y claramente se preserva la linealidad.Por la misma razon X no puede aparecer en ningun lado derecho y como es la unicavariable que se puede haber introducido en los lados izquierdos la aciclicidad seconserva.

Las variables producidas y las existenciales no cambian luego pvar(G′) ⊆ evar(G′).Por otro lado, en δ solo puede haberse introducido la variable Y y en σ se introducenX e Y , pero ninguna de ambas era producida y los lados derechos de P no hancambiado, luego pvar(G′) ∩ var(σ) = pvar(G′) ∩ var(δ) = ∅.

Imit== X no puede aparecer en ningun lado derecho de P por el lema de orden luego loslados derechos de P no cambian y se preserva la linealidad. En los lados izquier-dos eventualmente pueden haberse introducido variables nuevas que no afectan a laaciclicidad.

Como el conjunto de variables producidas permanece intacto y el de las existencialesconserva las que tenıa es claro que pvar(G′) ⊆ evar(G′). En δ a lo sumo pue-den haberse introducido variables nuevas y en σ ademas puede haberse introducidoX, pero ninguna de todas estas variables es producida, luego pvar(G′) ∩ var(σ) =pvar(G′) ∩ var(δ) = ∅.

Clash Idem que EDC, ID.

Page 160: Un lenguaje lógico funcional con restricciones1

160 CAPITULO 4. DE LA SEMANTICA

Store P no cambia, luego la linealidad y la aciclicidad son claras. Tanto X como var(t) nopueden ser ninguna producidas por la propiedad de orden (a.2) y son las unicas que seintroducen en δ. Ademas σ no cambia, luego pvar(G′)∩var(σ) = pvar(G′)∩var(δ) =∅. Como los conjuntos de variables producidas y existenciales no cambian tenemospvar(G′) ⊆ evar(G′) por hipotesis.

Dcp6= Idem que EDC, ID.

Imit6= X no es producida porque por el lema de orden no puede aparecer en ningunaproduccion de P de ındice > 0 y por hipotesis X 6∈ suspvar(G), luego los ladosderechos de P no estan afectados por la sustitucion y la linealidad se preserva.Por otro lado, en las dos alternativas que ofrece esta regla X se sustituye por unaexpresion que solo contiene las variables nuevas Y , que pueden introducirse en loslados izquierdos de P pero no en los derechos que no cambian, luego la aciclicidadtambien se preserva.

Las demostraciones de pvar(G′) ⊆ evar(G′) y pvar(G′) ∩ var(σ) = pvar(G′) ∩var(δ) = ∅ son las mismas que en Imit==. ¥

En las dos definiciones siguientes se precisan los conceptos de forma resuelta y desolucion, en las que nos apoyaremos para concretar los resultados correccion y completitud.

Definicion 5 (Forma resuelta) Decimos que un objetivo admisible G ≡ ∃U.P2R2σ2δes (o esta en) una forma resuelta si se cumple:

R = ∅ (todas las restricciones han sido resueltas)

∀(l α→ t) ∈ P , α = 0 (todas las producciones que quedan son suspensiones). ¥

Notese que en las formas resueltas pueden haber suspensiones. Esto quiere decir queal final de un computo puede haber producciones que no se han procesado debido a lapereza. Tales suspensiones son ahora inutiles.

Definicion 6 (Solucion) Decimos que θ ∈ Subst⊥ es una GORC6=-solucion (simple-mente solucion) para un objetivo G ≡ ∃U.P2R2δ2σ y lo notaremos por θ ∈ Sol(G)si:

Xθ ∈ CTerm,∀X ∈ V − evar(G),

R ` Pθ, Rθ, δθ, y

σθ es un conjunto de identidades. ¥

El primer requisito, Xθ ∈ CTerm,∀X ∈ V − evar(G), es para tener garantizado queninguna variable de las que aparecıa en el objetivo inicial toma el valor ⊥ (todas tomanvalores finitos y totales). Las variables existenciales que ha introducido el computo puedentomar cualquier valor.

Los otros dos requisitos relacionan la nocion de solucion con el calculo GORC6=. Alaplicar θ a las condiciones de P y R, las condiciones resultantes deben ser probables en elcalculo GORC6=. Para las desigualdades de δ debe ocurrir lo mismo.

Page 161: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 161

Las igualdades de σ afectadas de θ, no solo deben ser GORC6=-probables, sino que dehecho, seran un conjunto de identidades de c-terminos. Observese que todas las variablesnuevas que introduce el calculo LNC 6= estan cuantificadas existencialmente. En σ solo seanaden igualdades de la forma X = t siendo X una variable no existencial y t ∈ CTerm.Es decir, X es una variable que aparecıa en el objetivo inicial y este tipo de variablestoman valores finitos y totales (primer requisito). Por eso las igualdades σθ deben seridentidades entre c-terminos.

4.4.2. Correccion

En este apartado demostraremos que el calculo LNC 6= es correcto con respecto aGORC6=, es decir, que si θ′ es solucion de un objetivo G′ que se derivado de otro Gentonces existe θ que es solucion de G y que coincide con θ′ al menos sobre las variablesno existenciales. No podemos afirmar que una solucion de G′ sea tambien solucion deG porque las reglas del calculo pueden introducir y eliminar variables existenciales. Lointeresante es que las variables del objetivo inicial toman los mismos valores mediante unau otra sustitucion.

A continuacion presentamos el enunciado formal y su demostracion, que se apoya enel lema de sustituciones y las condiciones de admisibilidad de objetivos.

Teorema 1 (Correccion) El calculo de resolucion de objetivos es correcto con respectoa GORC6= en el sentido siguiente:

Si G es un objetivo admisible y G Ã G′ se verifica:

si G′ ≡ FAIL entonces Sol(G) = ∅,si θ′ ∈ Sol(G′), entonces existe θ ∈ Sol(G) con θ = θ′[V − (evar(G) ∪ evar(G′))]

DemostracionProbaremos que el enunciado es cierto para cada una de las reglas del calculo.

Dcp→ Si θ′ ∈ sol(G′) entonces eiθ′ → tiθ

′ es probable. Tomando θ ≡ θ′ podemos reconstruiruna prueba para c(e)θ → c(t)θ utilizando la regla 3 del calculo de pruebas GORC6=.El resto de producciones no cambia.

Obind Si θ′ ∈ Sol(G′) entonces (P, δX , R, δ)[X/c(t)]θ′ es probable y σ[X/c(t)]θ′ es un con-junto de identidades. Como X ∈ evar(G) y X 6∈ var(c(t)) por aciclicidad, podemosdefinir θ como Xθ ≡ c(t)θ′ y Zθ ≡ Zθ′, ∀Z 6≡ Y . De este modo, por el lema de sus-titucion tenemos θ ≡ [X/c(t)]θ′, luego (P, δX , R, δ)[X/c(t)]θ′ = (P, δX , R, δ)θ con loque para esta parte del objetivo las pruebas son las mismas tanto en G como en G′.Para la produccion X

m→ c(t) tenemos: Xθ = c(t)θ′ por definicion de θ, pero ademascomo X 6∈ var(c(t)) tenemos c(t)θ′ = c(t)θ. Ası (X → c(t))θ ≡ c(t)θ → c(t)θ queobviamente es probable. Por ultimo σθ = σθ′ es un conjunto de identidades, por loque θ es la sustitucion que postula el teorema.

Ibind En primer lugar observemos que como Y ∈ pvar(G) entonces Y 6∈ var(σ) ∪ var(δ),es decir, σ y δ no se ven afectadas por la sustitucion [Y/X]. Como Y ∈ pvar(G) ypvar(G) ⊆ evar(G) podemos definir θ como Y θ = Xθ′ y Zθ = Zθ′, ∀Z 6≡ Y . Porel lema de sustitucion se prueba que (P,R, δ)θ = (P, R, δ)[Y/X]θ′ y Xθ → Y θ ≡Xθ → Xθ′ ≡ Xθ → Xθ que es obviamente probable. Es decir, las pruebas en G sonidenticas a las pruebas en G′. Ademas σθ = σθ′ es un conjunto de identidades.

Page 162: Un lenguaje lógico funcional con restricciones1

162 CAPITULO 4. DE LA SEMANTICA

Sus En este caso tomando θ ≡ θ′ el resultado es trivial.

Nrw De nuevo tomando θ ≡ θ′, basta advertir que como (e → s, s → c(t), C)θ′ es probable,podemos reconstruir una prueba de (f(e) → c(t))θ con la regla 4 del calculo GORC6=.

Act1 Analogo a Nrw, pero ahora en vez de c(t) tenemos X.

Act2 Notese que como X ∈ pvar(G) entonces X 6∈ σ ∪ δ, es decir, la sustitucion [X/c(Y )]deja invariantes σ y δ. De estos hecho se sigue que (P2R)[X/c(Y )]2σ2δ es identicoa (P2R2σ2δ)[X/c(Y )].

Dado que X ∈ pvar(G) ⊆ evar(G), podemos definir θ como Xθ ≡ c(Y )θ′ yZθ ≡ Zθ′, ∀Z 6≡ X y por el lema de sustitucion tendremos θ = [X/c(Y )]θ′. Lue-go (P2R2σ2δ)[X/c(Y )]θ′ es identico a (P2R2σ2δ)θ. Como la primera parte esprobable por hipotesis, la segunda tambien lo es. Y por otro lado σθ = σ[X/c(Y )]θ′

es un conjunto de identidades.

Nos queda por ver que existe una prueba para la aproximacion (c(e) → X)θ. Comoθ = [X/c(Y )]θ′ tenemos que (c(e) → X)θ ≡ (c(e) → X)[X/c(Y )]θ′, que a su vez esidentico a (c(e) → c(Y )θ′ ya que X 6∈ var(c(e)) por linealidad de G. Ahora bien,para (e → Y ))θ′ existen pruebas por hipotesis, y con ellas se puede construir unapara (c(e) → X)θ utilizando la regla 3 del calculo GORC6=.

Eval Tomando θ ≡ θ′ tenemos que todas las pruebas de Gθ estan presentes en G′θ′, ex-cepto la de (f(e) → e′)θ que puede reconstruirse con las pruebas (f(e) → Y, Y 3e′)θ′

que tenemos en G′ y la regla 5 del calculo GORC6=

Dcp== Trivial tomando θ ≡ θ′ y utilizando la regla 7 del calculo GORC6=.

Id Tomando θ ≡ θ′, como G′θ contiene todas las pruebas de Gθ excepto la de Xθ == Xθque obviamente se puede probar, tenemos el resultado.

Imit== Definimos θ como Xθ ≡ c(Y ) y Zθ ≡ Zθ′, ∀Z 6≡ X, con lo que tenemos θ ≡[X/c(Y )]θ′ por el lema de sustitucion. De este modo todas las pruebas de Gθ estanentre las de G′[X/c(Y )]θ′ excepto la de (X == c(e))θ. Ahora bien, (X == c(e))θ ≡(X == c(e))[X/c(Y )]θ′ ≡ (c(Y ) == c(e))θ′, y se puede construir una prueba paraesta ultima restriccion con las pruebas de (Y == e)θ′ que tenemos en G′ utilizandola regla 7 del calculo GORC6=.

Clash Tomando θ ≡ θ′ tenemos que todas las pruebas de G aparecen en G′, excepto la de(c(e) 6= d(e′))θ que es trivialmente probable por la regla 8 del calculo GORC6=. σθes un conjunto de identidades por hipotesis.

Store Trivial porque para el calculo GORC6= son identicos G y G′.

Dcp6= Tomando θ ≡ θ′, si en G tenemos una prueba para (ei 6= e′i)θ podemos construir unaprueba para (c(e) 6= c(e′))θ utilizando la regla 9 del calculo GORC6=.

Imit6= Haremos la demostracion para las dos alternativas de la regla. En el primer casodefinimos θ como Xθ ≡ d(Y )θ′ y Zθ ≡ Zθ′, ∀Z 6≡ X. Por el lema de sustituciontenemos θ ≡ [X/d(Y )]θ′. En G′[X/d(Y )]θ′ estan presentes todas las pruebas que hayque hacer en G, excepto la de (X 6= c(e))θ′ ≡ (d(Y ) 6= c(e))θ que puede construirsecon la regla 8 del calculo GORC6= (c 6≡ d).

Page 163: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 163

Para la otra alternativa podemos tomar θ como Xθ ≡ c(Y )θ′ y Zθ ≡ Zθ′, ∀Z 6≡ X.Por el lema de sustitucion tenemos θ ≡ [X/c(Y )]θ′. Las pruebas de Gθ estan entrelas de G′[X/c(Y )]θ′, excepto la de (X 6= c(e))θ que puede construirse con la de(Yi 6= ei)[X/c(Y ]θ′ y la regla 9 del calculo GORC6=. ¥

4.4.3. Completitud

Ahora nos interesa demostrar que el calculo LNC 6= es capaz de derivar formas resueltaspara un objetivo inicial. Ademas las soluciones del objetivo inicial no deben perderse enlos pasos de derivacion. En otras palabras, dado un objetivo inicial y una solucion parael, se puede derivar una forma resuelta para la que existe una solucion que coincide conla primera, al menos sobre las variables del objetivo inicial. Este es la idea del resultadode completitud que formalizaremos mas tarde. Este resultado sera una consecuencia (casi)inmediata del lema de progreso que veremos a continuacion.

El lema de progreso utiliza las nociones de testigo y orden de testigos cuyas definicionesson variantes de las de [GHL+96, GHL+98]:

Definicion 7 (Testigo) Sea R un programa y sea G ≡ ∃U.P2R2σ2δ un objetivo parael calculo LNC6= y θ ∈ Sol(G). Un testigo para θ es un multiconjunto que contiene una

GORC6=-prueba para cada una de las condiciones (l α→ t) ∈ Pθ, (eβ3 e′) ∈ Rθ y δθ. ¥

Definicion 8 (Orden de testigos (/)) Dado un programa R, si M≡ {{Π1, ...,Πn}} yM′ ≡ {{Π′1, ..., Π′m}} son multiconjuntos de GORC6=-pruebas de condiciones (produccio-nes y restricciones), definimos:

M /M′ sii {{|Π1|, ..., |Πn|}} ≺ {{|Π′1|, ..., |Π′m|}}donde |Π| es la longitud (numero de pasos de inferencia) de Π y ≺ es la extension paramulticonjuntos del orden habitual sobre IN ([DM79]). ¥

En ([GHL+96, GHL+98]) el este orden de testigos bastaba para probar el lema deprogreso que se presentaba. En nuestro calculo debemos extender este orden debido a queen las reglas Sus y Store el testigo de un objetivo y su transformado son exactamenteel mismo. No obstante, en Sus disminuye el numero de producciones no suspendidas (sesuspende una de ellas) y en Store se pasa una desigualdad a la parte de desigualdadesresueltas, es decir, disminuye el numero de restricciones en la parte de resolucion R. Esobvio que tanto el numero de producciones, como el de restricciones, es finito en un objetivoadmisible. De acuerdo con lo anterior es posible definir un orden lexicografico (y por tantobien fundado) entre parejas de testigos y objetivos:

Definicion 9 (Orden de objetivos y testigos (/)) Sea R un programa yG ≡ ∃U.P2R2σ2δ y G′ ≡ ∃U ′

.P ′2R′2σ′2δ′ dos objetivos admisibles tales que G Ã G′.Sean θ ∈ Sol(G), θ′ ∈ Sol(G′), M un testigo para θ y M′ un testigo para θ′. Entonces

(M, G)/(M′, G′) sii

M /M′, o bien

M = M′ y Card({l α→ r|(l α→ r) ∈ P, α 6= 0}) < Card({l α→ r|(l α→ r) ∈ P, α 6= 0}),o bien

Page 164: Un lenguaje lógico funcional con restricciones1

164 CAPITULO 4. DE LA SEMANTICA

M = M′, Card({l α→ r|(l α→ r) ∈ P, α 6= 0}) = Card({l α→ r|(l α→ r) ∈ P, α 6= 0}) yCard(R′) < Card(R).

donde Card denota el cardinal de un conjunto. ¥

Lema 6 (Progreso) Si G ≡ ∃U.P2R2σ2δ es un objetivo admisible, entonces:

Si G no es una forma resuelta, entonces existe alguna transformacion de LNC 6=aplicable a G.

Si M es un testigo de θ ∈ Sol(G) y T es cualquier transformacion aplicable a G,entonces existe G′ ≡ ∃U ′

.P ′2R′2σ′2δ′, una sustitucion θ′ y un testigo M′ tal que:

i) G Ã G′ mediante la transformacion T ,

ii) M′ es un testigo de θ′ ∈ Sol(G),

iii) (M, G)/(M′, G′),

iv) θ = θ′[V − (evar(G) ∪ evar(G′))]. ¥

Demostracion

Demostraremos cada parte del teorema por separado.Parte 1

Si G es admisible y no esta en forma resuelta, entonces debe existir alguna condicion(produccion o restriccion) de prioridad maxima m > 0 en G. Tomemos una cualquiera deellas, que tendra la forma e

m→ t, o bien em3 e′.

Supongamos que es de la forma em→ t y analicemos los posibles casos:

e ≡ Xt ≡ Y , Ibind (i.e., se puede aplicar Ibind)t ≡ c(t)

X 6∈ suspvar(G), ObindX ∈ suspvar(G), existe l((e′) 0→ X ∈ P

l ∈ DCn, Act2

l ∈ FSn, Act1

e ≡ c(e)t ≡ Y , Sust ≡ c(t), Dcp→t ≡ d(t), con c 6≡ d, Fail1

e ≡ f(e)t ≡ Y , Sust ≡ c(t), Nrw

Supongamos ahora que la condicion es de la forma em3 e′:

e ≡ f(e), Evale ≡ c(e)

e′ ≡ c(e′)3 ≡==, Dcp==

3 ≡6=, Dcp6=e′ ≡ d(e′) con c 6≡ d

Page 165: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 165

3 ≡==, Fail23 ≡6=, Clash

Los casos que quedan son a) Xm3 Y y b) X

m3 c(e).

a) Xm3 Y

X, Y 6∈ pvar(G)X ≡ Y

3 ≡==, Id3 ≡6=, Fail4

X 6≡ Y3 ≡==, Bind3 ≡6=, Store

X ∈ pvar(G), entonces existe l(e′′) 0→ X ∈ Pl ∈ DCn, Act2

l ∈ FSn, Act1

b) X3c(e), puede ser:X == c(e)

X ∈ pvar(G), entonces existe l(e′′) 0→ X ∈ Pl ∈ DCn, Act2

l ∈ FSn, Act1

X 6∈ pvar(G)X ∈ svar(c(e)), Fail3X 6∈ svar(c(e)), Imit==

X 6= c(e)X ∈ pvar(G), entonces existe l(e′′) 0→ X ∈ P

l ∈ DCn, Act2

l ∈ FSn, Act1

X 6∈ pvar(G)c(e) ∈ CTerm ∧ var(c(e)) = ∅, Storec(e) 6∈ CTerm ∨ var(c(e)) 6= ∅, Imit6=

Parte 2Procedemos por analisis de casos sobre la regla T aplicable a G.

Dcp→ Si M es un testigo de θ, debe contener una prueba Π0 de c(e)θ → c(t)θ que sera dela forma:

Π0 ≡ (3)Π1...Πn ΠC ΠB

f(e) → c(t)

con (f(s) = s <== C) ∈ [R]⊥, Πi prueba de eiθ → siθ, ΠC pruebas de C y ΠB

prueba de s → c(t)θ.

Podemos tomar θ′ ≡ θ y M sera el resultado de reemplazar Π0 por Π1...Πn ΠC , ΠB.

Obind M debe contener una prueba Π0 de Xθ → c(t)θ. Por el lema de refinamiento existeθ′ w θ tal que Xθ′ = c(t)θ′ con θ′ = θ[V − var(c(t))]. θ′ es un posible candidato yaque coincide con θ salvo en var(c(t)) ⊆ pvar(G) ⊆ evar(G).

Page 166: Un lenguaje lógico funcional con restricciones1

166 CAPITULO 4. DE LA SEMANTICA

Por otro lado, [X/c(t)]θ′ w θ: X[X/c(t)]θ′ = c(t)θ′ = Xθ′ w Xθ y para toda Y 6≡ X

Y [X/c(t)]θ′ = Y θ′ w θ.

Entonces, si lα→ r ∈ P existira una demostracion de lθ → rθ y por monotonıa exis-

tira en G′ una demostracion con la misma longitud y estructura de l[X/c(t)]θ′ → rθ.Ahora bien, X 6∈ pvar(G) por el lema de orden y X 6∈ susV ar(G) por hipote-sis (X no aparece en ningun lado derecho); entonces X 6∈ var(r) y por otra partevar(c(t)) ∩ var(r) = ∅ por linealidad. Entonces r[X/c(t)]θ′ ≡ rθ. Si e

α3 e′ exis-

tira una demostracion de eθ3e′θ y por monotonıa (aplicando dos veces el teorema)existira una demostracion de e[X/c(t)]θ′3e′[X/c(t)]θ′. Lo mismo ocurre con las de-sigualdades de δX y como δ y σ no contienen no tienen variables producidas y θ′

solo difiere de θ en var(c(t)) ⊂ pvar(G) tenemos (δ, σ)θ′ = (δ, σ)θ (son las mismasdemostraciones). Y por ultimo Xθ′ = c(t)θ′ es obviamente una identidad.

Por lo tanto M′ es el resultado de eliminar la prueba Π0 de M, y se satisfacen todaslas condiciones del teorema.

Ibind M debe contener una prueba Π0 de Xθ → Y θ. Por el lema de refinamiento existeθ′ w θ tal que Xθ′ = Y θ′′ y θ′ = θ[V − {Y }], es decir. Como en OB se prueba que[Y/X]θ′ w θ.

Si lα→ r ∈ P , entonces M contendra una prueba de lθ → rθ y por monotonıa existe

una prueba con la misma longitud y estructura de l[Y/X]θ′ → rθ. Por linealidadY 6∈ var(r) y como θ′ ≡ θ[V − {Y }], tenemos que r[Y/X]θ′ = rθ.

Si eα3 e′ ∈ R, M contendra una prueba de eθ3e′θ y por monotonıa existe una

prueba de e[Y/X]θ′3e′[Y/X]θ′ con la misma longitud y estructura. Para la parteresuelta y las desigualdades tenemos σθ = σ[Y/X]θ′ y δθ = δ[Y/X]θ′.

Ası, M′ es el resultado de eliminar la prueba Π0 de M.

Sus Podemos tomar θ′ ≡ θ′ y tenemos M ≡ M′, pero ahora Card({l α→ r|α 6= 0}) <Card({l α→ r|α 6= 0}) ya que se ha suspendido una de las producciones de G.

Nrw M debe contener una prueba Π0 de f(e)θ → c(t)θ que tendra la forma

Π0 ≡ (4)Π1...Πn ΠC ΠB

f(e) → c(t)

con (f(s) = s <== C) ∈ [R]⊥ Πi prueba de eiθ → siθ, ΠC pruebas de C y ΠB

prueba de s → c(t)θ.

Podemos tomar θ′ ≡ θ y M sera el resultado de reemplazar Π0 por Π1...P in ΠC ,ΠB.

Act1 Analoga a Nrw.

Act2 M debe contener una prueba Π0 de c(e)θ → Xθ. Dicha prueba tiene que utilizar laregla 3 de GORC6= y debe ser Xθ ≡ c(t). La prueba sera:

Π1...Πn

c(e)θ → c(t)

Page 167: Un lenguaje lógico funcional con restricciones1

4.4. CALCULO DE RESOLUCION DE OBJETIVOS 167

con Πi prueba de eiθ → ti, i = 1..n.

Definimos θ′ sobre las variables nuevas (existenciales) Yiθ′ ≡ ti y Zθ′ ≡ Zθ, ∀Z 6∈

{Yi}i=1..n. De este modo θ′ esta en las hipotesis del teorema.

Ademas tenemos la equivalencia: Xθ ≡ c(Y )θ′ ≡ X[X/c(Y )]θ′, es decir, todas lasapariciones de X en (P, R) son reemplazadas por el mismo termino por θ y por[X/c(Y )]θ′. Para el resto de variables ocurre lo mismo por definicion de θ′. Ası, paraP , R y δ tenemos las mismas pruebas con θ y con [X/c(Y )]θ′.

Por otro lado tenemos c(e)θ′ = c(e)θ, ya que c(e) no contiene ninguna variable deY . Ademas c(Y )θ′ = c(t) = Xθ, luego (c(e) → X)θ es identico a (c(e) → c(Y ))θ′,de donde se sigue que el testigo M′ buscado es el el resultado de sustituir en M laprueba Π0 por Π1...Πn.

Eval M debe contener una prueba de la forma

Π0 ≡ (5)Π1 Π2

f(e)θ3e′θ

con Π1 prueba de f(e)θ → t, Π2 prueba de t3e′θ y t ∈ CTerm⊥.

Podemos tomar Y θ′ ≡ t (Y ∈ evar(G′)) y Zθ′ ≡ Zθ, ∀Z 6≡ Y . M′ es el resultado dereemplazar Π0 por Π1 Π2.

Dcp== En M debe haber una prueba Π0 de la forma

Π0 ≡ (7)Π1...Πn

c(e)θ == c(e′)θ

con Πi prueba de eiθ == e′iθ. Podemos tomar θ′ ≡ θ y M′ sera el resultado dereemplazar Π0 por Π1...Πn en M.

Id En M debe haber una prueba de Xθ == Xθ. Podemos tomar θ′ ≡ θ; M′ es elresultado de eliminar dicha prueba de M.

Bind En M debe haber una prueba de Xθ == Y θ, luego Xθ = Y θ. Podemos tomarθ′ ≡ θ. De este modo tenemos que [X/Y ]θ = θ: Xθ = Y θ = X[X/Y ]θ y para todoZ 6≡ X tenemos Zθ = Z[X/Y ]θ. Por lo tanto sθ = s[X/Y ]θ,∀s ∈ CTerm⊥ y en M′

aparecen todas las pruebas de M excepto la de Xθ == Y θ. Por otro lado Xθ = Y θes una identidad.

Imit== M debe contener una prueba Π0 de Xθ == c(e)θ, luego tiene que ser Xθ ≡ c(t).Entonces sera

Π0 ≡ (7)Π1...Πn

c(e)θ == c(t) ≡ Xθ

con Πi prueba de eiθ == ti.

Podemos definir θ′ como Yiθ′ ≡ ti y Zθ′ ≡ Zθ, ∀Z 6≡ Yi, de modo que cumple

las condiciones del teorema ya que las Yi son todas variables nuevas. Se cumpleθ = [X/c(Y )]θ′: Xθ = c(t) = c(Y )θ′ = X[X/c(Y )]θ′ y para todo Z 6≡ X tenemosZθ = Z[X/c(Y )]θ′.

Por lo tanto M′ es el resultado de reemplazar Π0 por Π1...Πn y el resto de pruebasqueda igual. Ademas Xθ′ = c(Y )θ′ es una identidad. ¥

Page 168: Un lenguaje lógico funcional con restricciones1

168 CAPITULO 4. DE LA SEMANTICA

Clash En M debe haber una prueba de c(e)θ 6= d(e′)θ. Podemos tomar θ′ ≡ θ; M′ es elresultado de eliminar dicha prueba de M.

Store En este caso podemos tomar θ′ ≡ θ y tenemos M = M′. El numero de produccionesno suspendidas permanece invariable, pero el numero de desigualdades en la partede resolucion ha disminuido.

Dcp6= En M debe haber una prueba Π0 de la forma

Π0 ≡ (9)Π1...Πn

c(e)θ 6= c(e′)θ

siendo Πj prueba de ejθ == e′jθ, j = 1..n. Entre estas pruebas debe haber una Πi

de eiθ == e′iθ. Podemos tomar θ′ ≡ θ y M′ sera el resultado de reemplazar Π0 porΠi en M, con lo que M′ /M.

Imit6= M debe contener una prueba Π0 de Xθ 6= c(e)θ. Esta prueba tiene que utilizar unade las reglas 8 o 9 de GORC6=.

a) Si utiliza la regla 8, sera Xθ = d(t) con c 6≡ d (d ∈ DC). Entonces la regla Imit6=permite derivar el objetivo G1. Definimos θ′ sobre las variables nuevas (existenciales)Yiθ

′ ≡ ti y Zθ′ ≡ Zθ, ∀Z 6∈ {Yi}i=1..n. De este modo θ′ esta en las hipotesis delteorema (notese que X puede ser o no existencial, pero en cualquier caso Xθ = Xθ′

por definicion de θ′). Tenemos la equivalencia: Xθ ≡ d(t) ≡ X[X/d(Y )]θ′, y para elresto de variables Z de G tenemos Zθ = Zθ′. Luego para P , X 6= c(e), R y delta)se tienen las mismas pruebas por aplicacion de θ o θ′, por lo que el testigo M′ es elresultado de eliminar la prueba Π0 de M, con lo que M′ /M.

Por otro lado, σθ = σ[X/d(Y )]θ′ es un conjunto de identidades y si a σ se le haanadido la sustitucion X = d(Y ) es porque X 6∈ evar(G), luego Xθ ∈ CTerm porla definicion de solucion, y por otro lado, (X = d(Y ))θ′ es equivalente a Xθ = d(t),que es una identidad (entre terminos totales).

b) Si utiliza la regla 9 debe ser Xθ ≡ c(t) y la prueba Π0 tendra la forma

Π0 ≡ (9)Πi

c(t) 6= c(e)θ

siendo Πi una prueba de ti 6= ei para algun i = 1..n (c ∈ DCn). Entonces Imit6=permite derivar el objetivo G2. Ahora definimos θ′ como Yiθ

′ ≡ ti para el i concretoque nos proporciona la prueba y Zθ′ ≡ Zθ, ∀Z 6≡ Yi. Como antes tenemos Xθ ≡X[X/c(Y )]θ′ y el resto de variables son afectadas igual por θ que por θ′. Luego paraP , X 6= c(e), R y δ) se tienen las mismas pruebas por aplicacion de θ o θ′ y el testigoM′ que buscamos es el resultado de reemplazar Π0 por Πi en M.

Con σ ocurre exactamente lo mismo que antes. ¥

Teorema 2 (Completitud) Sea R un programa, G un objetivo inicial y θ ∈ Sol(G).Entonces existe una forma resuelta G′ ≡ ∃U.P22σ2δ y θ′ tal que:

G Ã G′,

Page 169: Un lenguaje lógico funcional con restricciones1

4.5. ESTRATEGIA DDS 169

θ′ ∈ Sol(G′), y

θ = θ′[var(G)] ¥

Demostracion

En virtud del anterior lema de progreso es posible construir una derivacion

G ≡ G0 Ã G1 Ã G2 Ã ...

tal que existen θ ≡ θ0, θ1, θ2... y M = M0,M1,M2, ... tales que θi ∈ Sol(Gi), θi+1 =θi[V − (evar(Gi) ∪ evar(Gi+1))], Mi es un testigo de θi y Mi+1/Mi, para todo i = 1....

Puesto que θ ∈ Sol(G) (el objetivo tiene solucion) y / es un orden bien fundado, laderivacion anterior debe ser finita y terminar en n pasos con una forma resuelta G′ ≡∃U.P22σ2δ. Ademas como evar(G) = ∅ y θi = θi+1[V − (evar(Gi) ∪ evar(Gi+1))], esfacil ver que θn = θ[var(G)]. ¥

4.5. Estrategia DDS

El calculo LNC 6= que hemos presentado es un marco general para conseguir resultadosde correccion y completitud. Ademas, imponiendo prioridades sobre las condiciones de losobjetivos, hemos obtenido una aproximacion a una implementacion real avalada por elmarco teorico. Sin embargo, la evaluacion de funciones en este calculo se llevarıa a cabode forma ingenua en la supuesta implementacion.

Nuestro objetivo ahora es conseguir reflejar la Estrategia Guiada por la Demanda, cuyasideas fundamentales se estudiaron en la introduccion de 3.10. En la implementacion realse utilizan mecanismos de control (en ejecucion) para evaluar f.n.c.’s que, en definitiva, sonel fundamento de la estrategia. Ahora, en un marco abstracto, no disponemos de ningunmecanismo de control. Sin embargo, disponemos del poder expresivo de las desigualdadesy esto bastara.

Observese que para resolver una desigualdad de la forma X 6= c(e), la regla Imit6=del calculo LNC6= es capaz de introducir una constructora d 6≡ c con el fin de forzar unconflicto y evitar la evaluacion de ninguna expresion de e. Ademas, el calculo puede resolverdesigualdades entre una variable y una f.n.c. en un solo paso, mediante las reglas Storey Imit6=. En realidad, la filosofıa de la pereza esta tambien presente en la resolucion dedesigualdades y sugiere la siguiente idea: para conseguir una f.n.c. de una expresionse puede resolver una desigualdad entre entre dicha expresion y una variablenueva, de tal modo que las reglas del calculo reduciran la expresion solo hasta que sepueda resolver la desigualdad, es decir, la reduciran a f.n.c..

Esta idea se plasma en una transformacion de las reglas de funcion. Consideremos lafunciones leq y add estudiadas en 3.10.2, que en la notacion de este capıtulo (primer orden)tendran la forma (para abreviar, representamos las constructoras de naturales zero y succomo 0 y s respectivamente):

leq(0, Y ) = trueleq(s(X), 0) = falseleq(s(X), s(Y )) = leq(X, Y )

add(0, Y ) = Yadd(s(X), Y ) = s(add(X, Y ))

De acuerdo con la idea anterior, las reglas de leq se transforman en las siguientes:

Page 170: Un lenguaje lógico funcional con restricciones1

170 CAPITULO 4. DE LA SEMANTICA

leq(A,B) = leq(A,B) <== A 6= U

leq1(0, Y ) = trueleq1(s(X), 0) = falseleq1(s(X), s(Y )) = leq(X,Y )

Ahora hay solo una regla para leq. Esta regla computa una f.n.c. para el primer argu-mento y llama a leq1 que esta definida exactamente igual que la antigua leq. Resolvamosel objetivo leq(add(0, s(0)), add(s(0), s(0))) == B utilizando esta nueva definicion de leq:

2leq(add(0, s(0)), add(s(0), s(0))) 1== B22Eval,SusÃ

∃Z1.leq(add(0, s(0)), add(s(0), s(0))) 0→ Z12Z11== B

Act1,Sus,Sus,SusÃ∃Z1, Z2, U.add(0, s(0)) 0→ Z2, add(s(0), s(0)) 0→ Z3, leq1(Z2, Z3)

0→ Z12Z2

26= U,Z1

1==

B22Act1Ã

∃Z1, Z2, Z3, Z4, U,0 4→ 0, s(0) 4→ Z4, Z43→ Z2, add(s(0), s(0)) 0→ Z3, leq1(Z2, Z3)

0→ Z12Z2

26=

U,Z11== B22

Dcp→,SusÃ∃Z1, Z2, Z3, Z4, U.s(0) 0→ Z4, Z4

3→ Z2, add(s(0), s(0)) 0→ Z3, leq1(Z2, Z3)0→ Z12Z2

26=

U,Z11== B22

IbindÃ∃Z1, Z3, Z4, U.s(0) 0→ Z4, add(s(0), s(0)) 0→ Z3, leq1(Z4, Z3)

0→ Z12Z4

26= U,Z1

1== B22Act2,SusÃ

∃Z1, Z3, U, Z5,00→ Z5, add(s(0), s(0)) 0→ Z3, leq1(s(Z5), Z3)

0→ Z12Z11== B22 Ã

....

Ahora, antes de aplicar cualquier regla de leq1 (la antigua leq) se ha evaluado la f.n.c.s(Z5) para el primer argumento. Despues, la primera regla de leq1 fallara automaticamentey los pasos de computo que se hicieron sobre add(0, s(0)) no se han perdido, sino que sereutilizan para probar otras reglas de leq1.

Hemos conseguido nuestro proposito, pero tambien hemos introducido un efecto nodeseado: tenemos que resolver desigualdades que no aparecıan en el programa inicial. Esto,en general, puede suponer una sobrecarga en el almacen de restricciones δ y, de hecho,no estamos realmente interesados en resolver tales desigualdades; solo las utilizamos paracalcular f.n.c.’s. Pero este problema puede ser resuelto facilmente introduciendo una nuevaregla en el calculo LNC6=:

Elm6= ∃Y, U.P2em6= Y, R2σ2δ Ã ∃U.P2R2σ2δ

si Y no aparece en ningun otro sitio en G y (e ≡ c(e) o e ≡ X 6∈ suspvar(G)).

El cometido de esta regla es deshacerse de este tipo de desigualdades introducidas conel unico objeto de evaluar f.n.c.’s. La correccion se preserva trivialmente: la variable Y esexistencial y si el objetivo derivado tiene una solucion θ se puede construir una solucionpara el objetivo original dandole una valor apropiado a Y . La completitud tampoco se vealterada, ya que esta derivacion elimina una restriccion y la misma solucion del objetivooriginal es tambien solucion del derivado.

Una vez explorada la idea general de la transformacion, quedan dos cuestiones pen-dientes:

Debemos dar una definicion formal de las transformaciones y demostrar que un pro-grama y su transformado son equivalentes, es decir, que la transformacion preservala semantica.

Page 171: Un lenguaje lógico funcional con restricciones1

4.5. ESTRATEGIA DDS 171

El ejemplo que hemos utilizado muestra como hacer la transformacion considerandoel primer argumento de leq, pero es facil ver que en el ultimo paso de la derivacionel problema de la reevaluacion persiste para las dos ultimas reglas de leq1 (segundoargumento). Necesitamos un algoritmo que lleve a cabo una transformacion completasobre las funciones y que capture la Estrategia Guiada por la Demanda en toda suextension (tal y como se presento en 3.10).

Los dos apartados siguientes resuelven ambas cuestiones.

4.5.1. Transformacion de programas

En esta seccion utilizaremos la nocion de posicion en un termino que vimos en 3.10.1,ası como las de posicion demandada y uniformemente demandada. Ademas necesitaremosel orden de subsumcion (≤) sobre CTerm⊥ definido como:

s ≤ t sii ∃θ ∈ CSubst⊥ tal que sθ ≡ t

Definicion 10 (Transformacion de conjuntos de reglas) Sea R un programa sobreΣ, f una funcion y

S ⊆ Rf un subconjunto de reglas de f de la forma

f(t1) = r1 <== C1

· · ·f(tn) = rn <== Cn

f(s) un patron compatible con S, i.e., un c-termino lineal que satisface quef(s) ≤ f(ti), para todo i = 1..n.

u ∈ V P (f(s)) una posicion uniformemente demandada por S.

El conjunto transformado de S usando f(s) y u se define sobre la signatura Σ∪{fu}como ST = {R} ∪ Su, siendo

R ≡ f(s) = fu(s) <== X 6= Udonde X es la variable que tiene s en la posicion u y U es una variable nueva.

Su ≡ { fu(t1) = r1 <== C1

...fu(tn) = rn <== Cn}

¥

La transformacion para la funcion leq se puede hacer tomando el patron leq(A,B) yla posicion 1. Pero tambien podemos tomar un subconjunto S de reglas de leq que solocontenga las dos ultimas reglas de leq, considerando el patron leq(s(A), B) y la posicion2, con lo que obtendremos las reglas:

leq(0, Y ) = true (the first rule does not change)leq(s(A), B) = leq2(s(A), B) <== B 6= Uleq2(s(A), 0) = falseleq2(s(A), s(B)) = leq(A,B)

Page 172: Un lenguaje lógico funcional con restricciones1

172 CAPITULO 4. DE LA SEMANTICA

La transformacion de un conjunto de reglas S de un programa R produce un nuevoconjunto de reglas ST . Podemos definir un nuevo programa R = (R−S)∪ST , en el que sehan reemplazado las reglas de S por otras nuevas. Nuestro objetivo ahora es demostrar queambos programas son semanticamente equivalentes, es decir, las pruebas que se puedenhacer con R tambien se pueden hacer con R′. Y al contrario, pero con una salvedadrazonable: en R′ podremos probar aproximaciones de la forma fu(e) → t que no tienensentido en R porque no existe el sımbolo fu en su signatura asociada.

Para probar este resultado necesitamos una hipotesis adicional: la signatura debe con-tener al menos dos sımbolos distintos de constructora. Desde el punto de vista semanticoesta condicion garantiza que las desigualdades introducidas por la transformacion puedenser probadas utilizando la regla (8) del calculo GORC6= (conflicto de constructoras), to-mando una c-instancia apropiada de la regla R producida por la transformacion. Pero enla practica, esta condicion no es necesaria porque esas desigualdades no se van a resolveren realidad, sino que seran eliminadas por la nueva regla Elm6= que hemos introducido.

Lema 7 (Equivalencia Semantica por Transformaciones) Sea Σ una signatura enla que al menos hay dos sımbolos de constructora, R un programa sobre Σ, f una funcionde R y S ⊆ Rf ; si R′ = (R− S) ∪ ST se tiene:

a) R ` e → t sii R′ ` e → t

b) R ` e3e′ sii R′ ` e3e′

siendo t ∈ CTermΣ⊥, 3 ∈ {==, 6=} y e, e′ ∈ TermΣ⊥. ¥

Demostracion

Razonemos las dos implicaciones de la parte a).

(⇒) Suponemos R ` e → t y razonamos por induccion sobre la longitud l de la prueba:

l = 0. La unica prueba posible es R ` e → ⊥ por la regla (1) de GORC6= y es obvioque R′ ` e → ⊥.

l + 1. Supongamos que R ` e → t es probable en l +1 pasos. Entonces t 6≡ ⊥ y debeser aplicable alguna de las reglas (2) (3) o (4) del calculo GORC6=.

Si se puede aplicar (2) o (3) es claro que se puede replicar el paso de prueba utilizandoel programa R′, ya que estas dos reglas no utilizan ninguna regla de [R]⊥. La pruebade partida se reduce mediante esta regla a otras de la forma R ` P1, ..., Pk, dondecada Pi es de longitud ≤ l + 1. Por hipotesis de induccion tenemos R′ ` P1, ..., Pk ycon lo que se puede reconstruir la prueba R′ ` e → t.

Si se puede aplicar (4) debe ser e ≡ f(e). Supongamos por tanto, que R ` f(e) → tes probable en l + 1 pasos. Entonces debe existir una regla en R

Q ≡ f(u) = u <== C

y µ ∈ CSubst⊥ tal que la regla (4) de GORC6= sea aplicable utilizando la c-instanciaQµ y reducir la prueba a R ` e → uµ, Cµ, uµ → t.

Todas estas pruebas son de longitud ≤ l + 1 y por hipotesis de induccion tenemos

R′ ` e → uµ,Cµ, uµ → t (ii)

Page 173: Un lenguaje lógico funcional con restricciones1

4.5. ESTRATEGIA DDS 173

Si la regla Q 6∈ S, entonces en R′ tambien existe esa misma regla Q para f ypodemos tomar la misma c-instancia Qµ. La prueba R′ ` f(e) → t se puede reducira R′ ` e → uµ,Cµ, uµ → t, que es probable por (ii).

El caso mas interesante es cuando la regla Q ∈ S. Observemos que la nueva regla Rpara f se definio como:

R ≡ f(s1, ..., sn) = fp(s1, ..., sn) <== X 6= U

de modo que s ≤ r para toda regla (f(r) = r <== C) ∈ S, y en particular paraQ, de donde se sigue que existe θ ∈ CSubst⊥ tal que sθ = u. Como X en R ocupauna posicion demandada por una constructora c, Xθ forzosamente tiene que ser dela forma Xθ = c(a).

Por otro lado como la variable U no aparece en s y por hipotesis nuestra signaturacuenta al menos con dos sımbolos de constructora, existe d ∈ DC, d 6≡ c y se puededefinir θ′ ∈ CSubst⊥ como Uθ′ ≡ d(b) y V θ′ ≡ V θ para toda V 6≡ U . De este modoen [R]⊥ tendremos la c-instancia:

Rθ′µ ≡ f(u)µ = fp(u)µ <== c(a)µ 6= d(b)µ

Utilizando esta c-instancia, la prueba de R′ ` f(e) → t se reduce por la regla (4) deGORC6= a

R′ ` e → uµ︸ ︷︷ ︸(I)

, fp(uµ) → t︸ ︷︷ ︸(II)

, c(a)µ 6= d(b)µ︸ ︷︷ ︸(III)

(iii)

Razonemos la existencia de pruebas para (I), (II) y (III).

(I): por (ii).

(II): R ` f(uµ) → t es reducible, utilizando Qµ, a R ` uµ → uµ,Cµ, uµ → t.Como Q ∈ S, en R′ existe una regla Q′ ≡ fp(u) = u <== C y se puede utilizar lac-instancia Q′µ para reducir la prueba a R ` uµ → uµ,Cµ, uµ → t, cuyas pruebasson todas de longitud < l + 1, y por hipotesis de induccion seran todas probables.

(III): la desigualdad c(a) 6= d(b) es claramente probable por la regla (8) de GORC6=.

(⇐) . Suponemos R′ ` e → t. Como antes procedemos por induccion sobre el numero depasos l de la prueba:

l = 0. Tiene que ser t ≡ ⊥ y es claro que R ` e → ⊥ es probable por la regla (1) deGORC6=.

l + 1. Debe ser t 6≡ ⊥ y tiene que ser aplicable una de las reglas (2), (3) o (4) deGORC6=. El razonamiento para (2) y (3) es el mismo que en la otra implicacion.

Para (4), supongamos que R′ ` f(e) → t es probable en l + 1 pasos utilizando laregla (4) de GORC6=, de donde se sigue que debe existir una c-instancia de una reglaQ de f que permita aplicar la regla (4).

Si

Q ≡ f(u) = u <== C

Page 174: Un lenguaje lógico funcional con restricciones1

174 CAPITULO 4. DE LA SEMANTICA

no es la regla nueva que introduce la transformacion para la funcion f , entoncesQ ∈ R. La prueba R′ ` f(e) → t es reducible utilizando una c-instancia Qµ aR′ ` e → uµ,Cµ, uµ → t, en la que cada prueba tiene longitud ≤ l + 1. La mismac-instancia Qµ permite aplicar la regla (4) de GORC6= y reducir R ` f(e) → t aR ` e → uµ, Cµ, uµ → t, que son probables por hipotesis de induccion.

La otra posibilidad es que Q sea la nueva regla que introduce la transformacion paraf , es decir,

Q ≡ f(u) = fp(u) <== U 6= X

Entonces la prueba de R′ ` f(e) → t utiliza una c-instancia Qµ y es reducible aR′ ` e → uµ,Xµ 6= Uµ, fp(uµ) → t cuyas pruebas tienen todas longitud ≤ l + 1.Por hipotesis de induccion tenemos, en particular

R ` e → uµ (iv)

A su vez, la prueba de R′ ` fp(uµ) → t utilizara una c-instancia Q′η, siendo

Q′ ≡ fp(v) = v <== C

una regla de R′, y sera reducible a R′ ` uµ → vη, Cη, vη → t, cuyas pruebas tienentodas longitud ≤ l + 1, porque R′ ` fp(uµ) → t ya tenıa menos de l + 1 pasos. Porhipotesis de induccion tenemos

R ` uµ → vη︸ ︷︷ ︸(I)

, Cη︸︷︷︸(II)

, vη → t︸ ︷︷ ︸(III)

(v)

De (iv) y la parte (I) de (v), por transitividad de → se deduce

R ` e → vη (vi)

Uniendo los resultados (vi) y las partes (II) y (III) de (v) tenemos

R ` e → vη, Cη, vη → t (vii)

En R existe una regla para f identica a Q′, excepto en el nombre de funcion que enR′ es fp, de la que podemos tomar una c-instancia por medio de η y reconstruir unaprueba para R ` f(e) → t utilizando (vii) y la regla (4) de GORC6=

La parte b) es una consecuencia inmediata de a): los pasos de ambas pruebasR ` e3e′ y R′ ` e3e′ son identicos, excepto aquellos en los que se prueban aproxi-maciones de la forma f(e) → t, y i) garantiza que se pueden probar exactamente lasmismas aproximaciones de este tipo usando las reglas de uno u otro programa R oR′. En general estas pruebas no tendran la misma longitud ni estructura. ¥

Page 175: Un lenguaje lógico funcional con restricciones1

4.5. ESTRATEGIA DDS 175

4.5.2. Algoritmo de transformacion

En esta seccion se muestra un algoritmo de transformacion de funciones que trabaja deforma similar al que presentamos en 3.10.2 para la construccion de arboles definicionales.Para transformar programas completos, unicamente habra que aplicar este algoritmo acada funcion del programa. Llamaremos trans a la funcion de transformacion que tomaun conjunto de reglas S y un patron f(s) compatible con S, y devuelve el conjuntotransformado de reglas de S.

La llamada inicial sera de la forma trans(Rf , f(X)) y una llamada generica sera trans(S, f(s)).Algoritmo:

Si S es un conjunto unitario o vacıo devolver S.

En otro caso, aplicar una de las siguientes alternativas (solo una es aplicable):

Alguna posicion de V P (f(s)) es uniformemente demandada en S.

Sea u la menor en el orden lexicografico de esas posiciones.

Sea ST = {R} ∪ Su el conjunto transformado de S usando f(s) y u.

Sean c1, ..., cn las constructoras que ocupan la posicion u en los lados izquierdos delas reglas de S. Sobre Su hacemos la siguiente particion:

sea S1u el conjunto de reglas de Su que demandan c1 en la posicion u.

...sea Sn

u el conjunto de reglas de Su que demandan cn en la posicion u.

Sea X la variable de la posicion u en f(s). Para cada constructora ci construimos elpatron

pi = fu(s)[X/ci(Y1, ..., Ym)]

donde Y1, ..., Ym son variables nuevas.

Devolver: {R} ∪ trans(S1u, p1) ∪ ... ∪ trans(Sn

u , pn)

Alguna posicion de V P (f(s)) es demandada, pero ninguna es uniforme-mente demandada.

Sean u1, ..., uk las posiciones demandadas. Hacemos una particion del conjunto dereglas S del siguiente modo:

Sea Su1 el conjunto de reglas de Rf que demandan la posicion u1, Q1 = S − Su1 .Sea Su2 el conjunto de reglas de Q1 que demandan la posicion u2, Q2 = Q1 − Su2 ....Sea Suk

el conjunto de reglas de Qk−1 que demandan la posicion uk.Y sea S0 el conjunto de reglas de S que no demandan ninguna posicion.

Devolver: S0 ∪ trans(Su1 , f(s)) ∪ ... ∪ trans(Sun , f(s))

Ninguna posicion de V P (f(s)) es demandada.

En este caso sencillamente devolver: S ¥

Utilizando este algoritmo, el conjunto de reglas transformadas para la funcion leq es:leq(A,B) = leq1(A,B) <== A 6= U

leq1(0, B) = trueleq1(s(A), B) = leq12(s(A), B) <== B 6= U

leq12(s(A), 0) = falseleq12(s(A), s(B)) = leq(A,B)

Page 176: Un lenguaje lógico funcional con restricciones1

176 CAPITULO 4. DE LA SEMANTICA

Lema 8 (Correccion del algoritmo de Transformacion) Sea Σ una signatura en laque al menos hay dos sımbolos de constructora, R un programa sobre Σ y f una funcionde R. Entonces:

a) El algoritmo de transformacion es terminante con la llamada trans(Rf , f(X)).

b) El programa R′ = (R−Rf ) ∪ trans(Rf , f(X)) es semanticamente equivalente a Ren el sentido del lema de equivalencia semantica (7).

Demostracion

La demostracion que proponemos realmente prueba un resultado mas general. En elenunciado la llamada inicial es trans(Rf , f(X)), pero el algoritmo funciona exactamenteigual con una llamada de la forma trans(Rf , f(s)) siendo f(s) ≤ f(t) para toda (f(t) =t <== C) ∈ Rf , que es un caso mas general. Probaremos el resultado considerando unallamada inicial de esta forma.

Para probar la parte a) lo primero es probar que cuando el algoritmo solicita unconjunto transformado ST tal conjunto es calculable de acuerdo con la definicion deconjunto transformado. Para ello utilizaremos el invariante siguiente: todas las llamadastrans(V ′, f(v)) que genera el algoritmo cumplen la condicion f(v) ≤ f(t) para toda regla(f(t) = t <== C) ∈ V. Veamos que efectivamente esta condicion es invariante:

En la primera llamada trans(Rf , f(v)) esta condicion es cierta por hipotesis. Supon-gamos que despues de m llamadas recursivas esta condicion es cierta para trans(V, f(v)).Si el algoritmo aplica la primera alternativa es porque en V P (f(v)) hay alguna posicionuniformemente demandada por las reglas de V y entonces se puede construir el conjuntoVT usando f(v) y la menor de las posiciones uniformemente demandada u. Porconstruccion, los conjuntos V i

u de la particion que genera el algoritmo cumplen la condicionde que si W ∈ V i

u entonces W tiene la constructora ci en la posicion u. Por otro lado elpatron pi solo difiere de p en que tiene ci en la posicion u, de donde se sigue que pi ≤ f(t)para toda (f(t) = t <== C) ∈ V i

u y por tanto las llamadas trans(V iu, pi) verifican el

invariante.Si se aplica la segunda alternativa es obvio que se mantiene el invariante porque las

llamadas que se generan son de la forma trans(Vui , f(v)) donde Vui ⊆ V y el patron nocambia. Y si se aplica la tercera alternativa, no se generan llamadas recursivas.

Con esto hemos probado que el algoritmo “no se bloquea”. Para probar que terminatenemos que demostrar que las llamadas que produce recursivamente van disminuyendo encomplejidad. Para ello definimos la complejidad de una llamada trans(V, f(v) comoel par

(|V|, NCP (lhs(V))−NCP (f(v))

siendo |V| el cardinal del conjunto V, NCP (lhs(V)) el numero de posiciones de construc-tora en los lados izquierdos de las reglas de V y NCP (f(v)) el numero de posiciones deconstructora en f(v). El orden sobre las complejidades es el orden lexicografico usual.Ahora debemos probar que esta complejidad disminuye en cada llamada.

En las llamadas que genera la primera alternativa el numero de constructoras de lospatrones pi se ha incrementado en 1 (se anade la constructora ci), luego el segundo ele-mento del par de complejidad disminuye. El primer elemento puede no modificarse (sitodas las reglas demandan la misma constructora) pero en ningun caso se incrementa.

Page 177: Un lenguaje lógico funcional con restricciones1

4.5. ESTRATEGIA DDS 177

En las llamadas que genera la segunda alternativa, puesto que no hay ninguna posicionuniformemente demandada, la particion debe producir mas de un conjunto por lo que elcardinal de los conjuntos que se transforman es estrictamente menor que el del conjuntode llamada, por lo que los primeros elementos del par disminuyen. Y la tercera alternativano produce llamadas.

Para la parte b) probaremos el siguiente resultado (mas general): En las hipotesis delenunciado, dado S ⊆ Rf y f(s) tal que f(s) ≤ f(t) para toda (f(t) = t <== C) ∈ S

R y (R− S) ∪ trans(S, f(s))

son semanticamente equivalentes.Razonamos por induccion sobre el numero de pasos l (numero de llamadas a trans)

que necesita el algoritmo para terminar:l = 0 Entonces el algoritmo ha utilizado la tercera alternativa que no modifica el pro-

grama y por tanto la semantica se preserva.

l + 1Si el algoritmo utiliza la primera alternativa, podemos definir R′ ≡ (R− S) ∪ ST que

sabemos es semanticamente equivalente a R por el lema de equivalencia semantica. Latransformacion que hace el algoritmo sobre cada uno de los conjuntos de la particion deS debe tener menos de l + 1 pasos, por lo que tenemos la secuencia de equivalencias

R′ ≡ (R− S) ∪ {R} ∪ (⋃Si

u), por h.i. es equivalente a

R′′ ≡ (R′ − S1u) ∪ trans(S1

u, p1), por h.i. es equivalente a

R′′′ ≡ (R′′ − S2u) ∪ trans(S2

u, p2), por h.i. es equivalente a

...

Rk ≡ (Rk−1 − Sk−1u ) ∪ trans(Sk−1

u , pk−1) por h.i. es equivalente a

Rk+1 ≡ (Rk − Sku) ∪ trans(Sk

u , pk)

Y ahora, deshaciendo la secuencia tenemos

Rk+1 ≡ (Rk − Sku) ∪ trans(Sk

u , pk), sustituyendo Rk

≡ (Rk−1 − (Sk−1u ∪ Sk

u)) ∪ trans(Sku , pk) ∪ trans(Sk−1

u , pk−1), sustituyendo Rk−1

...

≡ (R− S) ∪ {R} ∪ trans(S1u, p1) ∪ ...trans(Sk

u , pk)

≡ (R− S) ∪ trans(S, f(s))

Luego Rk+1 ≡ (R−S)∪ trans(S, f(s)) es equivalente a R′ que a su vez es equivalentea R.

Si el algoritmo aplica la segunda alternativa podemos hacer un razonamiento similaraplicando la hipotesis de induccion sobre la transformacion que se hace sobre cada uno delos conjuntos de la particion y obtenemos la secuencia de equivalencias:

Page 178: Un lenguaje lógico funcional con restricciones1

178 CAPITULO 4. DE LA SEMANTICA

R ≡ (R− S0) ∪ S0 (≡ R) por h.i. es equivalente a

R′ ≡ (R− Su1) ∪ trans(Su1 , f(s)) por h.i. es equivalente a

R′′ ≡ (R′ − Su2) ∪ trans(Su2 , f(s)) por h.i. es equivalente a

...

Rk−1 ≡ (Rk−2 − Suk−1) ∪ trans(Suk−1

, f(s)) por h.i. es equivalente a

Rk ≡ (Rk−1 − Suk) ∪ trans(Suk

, f(s))

Y haciendo las sustituciones como antes obtenemosRk ≡ (Rk−1 − Suk

) ∪ trans(Suk, f(s))

≡ (Rk−2 − (Suk∪ Suk−1

)) ∪ trans(Suk−1, f(s)) ∪ trans(Suk

, f(s))

...

≡ (R− (Su1 ∪ ... ∪ Suk)) ∪ trans(Su1 , f(s)) ∪ ... ∪ trans(Suk

, f(s))

≡ (R− S) ∪ S0 ∪ trans(Su1 , f(s)) ∪ ... ∪ trans(Suk, f(s))

≡ (R− S) ∪ trans(S, f(s))

Luego Rk ≡ (R− S) ∪ trans(S, f(s)) es equivalente semanticamente a R. ¥

Page 179: Un lenguaje lógico funcional con restricciones1

Capıtulo 5

Conclusiones y trabajo futuro

El lenguaje T OY tiene muchos puntos en comun con los paradigmas en los que se hainspirado y las principales virtudes de la programacion funcional y logica estan presentesen el. Pero ademas, aporta elementos que no aparecen en los otros dos estilos, como sonlas variables logicas de orden superior o las funciones indeterministas.

Las restricciones sobre reales, encajan perfectamente en el mecanismo de ejecuciondel sistema y su introduccion es sencilla, salvo por problemas esencialmente tecnicos. Laprogramacion logico funcional con este tipo de restricciones ha resultado ser un contextomuy rico desde el punto de vista expresivo, ya que conjuga la potencia de las funcionescon la de las restricciones. Esto no puede hacerse en Prolog porque no hay funciones y enotros lenguajes funcionales como Haskell no se permiten estas restricciones.

Es importante destacar que T OY es un lenguaje declarativamente puro, en el sentidode que todo programa T OY se ajusta al marco semantico desarrollado en la seccion 4.31.En el lenguaje no existen recursos ajenos a la semantica. Es un hecho bien conocido (ydesafortunado) que tal situacion no se da en Prolog, que dispone de un amplio repertoriode recursos no ajustados a la semantica habitual de los programas logicos ([Apt90]). Tales el caso de los predicados metalogicos (descomposicion de terminos, reconocimiento devariables, etc), el corte o los predicados assert y retract de modificacion dinamica delprograma. En T OY no existen tales predicados (o funciones) y en realidad, no son ne-cesarios. De hecho, T OY es probablemente mas rico que Prolog desde el punto de vistaexpresivo, aun en ausencia de tales predicados, debido a las funciones (vease 2.6.2).

La construccion de este sistema, como es natural, ha planteado problemas tecnicos quehan incrementado nuestro conocimiento acerca de las implementaciones y que, en general,se han resuelto apropiadamente. Pero tambien ha suscitado problemas de caracter masteorico que deben tenerse en cuenta en el diseno de los calculos formales y sus pruebasde correccion y completitud. Tal es el caso de las funciones indeterministas. No obstante,algunos de estos problemas como el las variables de orden superior y las respuestas maltipadas, aun siguen abiertos.

El sistema tambien ha ayudado a disenar el calculo operacional que presentamos enel ultimo capıtulo. En muchos casos, durante la construccion de dicho calculo nos hemospreguntado: “¿como lo hace T OY?”. Esta pregunta es natural desde el momento en quepretendıamos reflejar algunos aspectos operacionales del sistema, pero tambien es ciertoque se tenıa la confianza suficiente para saber que la implementacion realmente estabahaciendo lo correcto.

1Suponiendolo ampliado a orden superior en la lınea de [G94] y conteniendo tipos en la lınea de [AR97b]

179

Page 180: Un lenguaje lógico funcional con restricciones1

180 CAPITULO 5. CONCLUSIONES Y TRABAJO FUTURO

A modo de autocrıtica, debemos anadir que el sistema no incluye ningun tipo dedepurador y en algunos casos no resulta sencillo explicar la secuencia de computos que serealiza, incluso conociendo la traduccion en profundidad. Tambien es cierto que este tipode computos normalmente no es trivial.

El primer capıtulo de este trabajo puede servir no solo como manual de usuario delsistema T OY, sino tambien como introduccion al paradigma logico funcional con restric-ciones. Pero las aportaciones principales de este documento residen en:

La descripcion detallada y exhaustiva del mecanismo operacional de T OY (capıtulo2). Esta descripcion posiblemente sirva de base para el desarrollo de futuras versionesy mejoras en el sistema.

El desarrollo de un marco teorico que, aunque incompleto con respecto a la imple-mentacion, recoge muchas de las caracterısticas esenciales del lenguaje. En particular,se ha dado una justificacion formal de las desigualdades y de Estrategia Guiada porla Demanda que utiliza el sistema. Previsiblemente este marco servira como funda-mento para fomalizar algunas optimizaciones de la implementacion real, ası comopara construir nuevos marcos semanticos.

Como trabajo futuro, sobre la implementacion actual se pueden incluir nuevas posi-bilidades como las construcciones where y let de los lenguajes funcionales, operaciones deentrada/salida, nuevas restricciones (dominios finitos) y tambien se pueden realizar masoptimizaciones de codigo.

Uno de nuestros intereses fundamentales es el estudio de la negacion en el contextologico funcional, sobre el que hay algunos trabajos relacionados ([M94, M96]). En progra-macion logica este es un tema ampliamente estudiado ([AB94, Kun87]). Prolog implementala negacion como fallo finito ([Cla78]), es decir, la negacion de un objetivo tiene exito siel objetivo (sin negar) tiene un arbol de busqueda finito y en el que todas las ramas sonfallidas. Este tipo de negacion solo es completo para objetivos cerrados (sin variables). Pa-ra objetivos con variables tambien se han realizado investigaciones como [Gin91], pero ennuestro marco las aproximaciones que mas nos interesan son las de negacion constructiva([Cha88, Stu91]) y algunas basadas en transformacion de programas ([BMP+90]).

Por otro lado nuestro lenguaje incluye funciones, pero no tiene sentido (en principio,al menos) negar una funcion. Para las funciones el enfoque apropiado parece el de analizaraquellos valores del dominio para los cuales la funcion no esta definida. Este conjunto devalores no es computable en general, pero sı es posible construir aproximaciones del mismoque, posiblemente aporten resultados interesantes (tanto teoricos como practicos).

Page 181: Un lenguaje lógico funcional con restricciones1

Apendice A

Gramatica de T OY .

Este apendice contiene las reglas basicas de la gramatica del lenguaje T OY. Adopta-remos las siguientes convenciones de notacion:

1. Los sımbolos no terminales aparecen en tipografıa standard.

2. Los terminales (reservados) aparecen encerrados en cajas .

3. Las barras verticales (′|′) se utilizan para separar diferentes alternativas.

4. Los items entre ‘[’ y ‘]’ son opcionales.

5. La notacion (item)∗ representa cero o mas apariciones item.

6. La notacion (item)+ representa una o mas ocurrencias de item.

7. Los Tokens apareceran en cursiva.

Dentro de los Tokens distinguimos las siguientes clases:

• consym,funsym: Identificadores que comienzan por una letra minuscula seguida decualquier numero de letras, dıgitos, apostrofe (′) o subrayado ( ). Por ejemplo, f,reverse o nat son nombres validos de constructora y funcion. Hay algunas palabrasreservadas del lenguaje, que no se pueden utilizar como identificadores. Son:

data, type, infixl, infixr, infix, primitive,if, then, else, include, where, let,in, subtype, int, real, char, bool

Los identificadores let, where, in y subtype corresponden a construcciones no imple-mentadas aun, pero estan reservadas para futuras versiones del sistema.

181

Page 182: Un lenguaje lógico funcional con restricciones1

182 APENDICE A. GRAMATICA DE T OY .

• varsym: Los identificadores que comienzan por una letra mayuscula seguida de cual-quier numero de letras, dıgitos, apostrofe (′) o subrayado ( ). Se admiten variablesanonimas, que deberan comenzar con un subrayado ( ) (recuerdese que dos variablesanomimas con el mismo nombre son variables distintas). Por ejemplo,

I don′t Know What To Do, X, Nothing, , anything

son nombres de variable validos, pero

Mr.Mr , 9Days, waiting for you

no lo son.

• funopsym: Los operadores infijos se escriben utilizando uno o mas de los siguientessımbolos:

! # & ∗ \ + − . < = > ? @ ˆ |

Los sımbolos

% $ :

tambien se admiten, pero no en la primera posicion del nombre. Los nombres deoperador

<− :: = .. : ∗ + − / <== / ∗ ˆ : − −>

son reservados.

• conopsym: Los peradores infijos de constructora siguen las mismas reglas que fu-nopsym, pero deben comenzar siempre con el sımbolo : .

Se admiten dos tipos de comentarios:

• de lınea, que comienzan con el caracter % y terminan con la lınea.

• delimitados, que comienzan con el sımbolo \∗ y terminan con ∗\ , ambos reserva-dos. Los comentarios delimitados se pueden anidar y son utiles, por ejemplo, cuandose quiere eliminar una funcion del programa sin borrar su codigo.

Page 183: Un lenguaje lógico funcional con restricciones1

183

GRAMATICA BASICA

program −→ ( { topdecl } )+ top-level declarationstopdecl −→

data typeLhs = constrs datatype declaration| type typeLhs = type type alias declaration| operdecl priority declaration| primitive prims :: type primitive declaration| decls value declarations| include string external files

prims −→ prim ( , prim )∗ multiple bindingsprim −→ fun primitive bindingdecls −→ decl ( ; decl)∗ multiple declarationsdecl −→

typedecl function type declaration| funrule function rule| clause Prolog clause

operdecl −→ precedence and groupinginfix integer oplist no grouping

| infixl integer oplist left grouping| infixr integer oplist right grouping

oplist −→ op ( , op )∗ operator list

EXPRESIONES DE TIPO

typeLhs −→ typesym ( varsym)∗ after data or type

constrs −→ constr ( | constr )∗ multiple constructorsconstr −→ data declaration right-hand side

type conop type infix constructor| con ( atype )∗ prefix constructor

type −→ ctype ( − > ctype )∗ function typectype −→ type

| typesym ( atype )∗ datatype| atype

atype −→ simple type| varsym type variable| ( ) unit type

| [ type ] list type

| ( type ( , type)∗ ) tuple typetypedecl −→ fun ( , fun)∗ :: type function’s type

Page 184: Un lenguaje lógico funcional con restricciones1

184 APENDICE A. GRAMATICA DE T OY .

REGLAS DE FUNCION Y CLAUSULAS

funrule −→ ruleLhs = exp [conditionrule] function ruleruleLhs −→ function rule left-hand side

pat funop pat rule for infix operator| fun ( apat )∗

conditionrule −→ <== conditioncondition −→ exp ( , exp)∗ conditional expresionsclause −→ ruleLhs : − condition Prolog Clause ( : − mandatory )

NOMBRES

fun −→ function namefunsym

| ( funopsym ) function operatorcon −→ constructor name

consym| ( conopsym ) infix constructor operator

op −→ infix operatorsfunop infix function

| conop infix constructorfunop −→ infix function

funopsym binary operator| ′ funsym ′ function as a binary operator

conop −→ infix constructorconopsym binary constructor

| ′ consym ′ constructor as a infix constructor

Page 185: Un lenguaje lógico funcional con restricciones1

185

EXPRESIONES

exp −→ expressionif exp then exp else exp if then else expression

| if exp then exp if then expression| opExp operator expresion

opExp −→opExp op opExp infix operator or constructor

| pfxExp prefix expressionpfxExp −→ [ − ] appExp prefix expressionappExp −→ ( atomic )+ function applicationatomic −→ atomic expression

varsym variable| fun function name| con constructor name| integer integer number| real real number| ( ) unit| exp parenthesised expression| ( atomic op ) left-section

| ( op atomic ) right-section

| ( exp ( , exp )∗ ) tuple| list list

list −→ list[ [ exp ( , exp )∗ ] ] enumerated list

| [ exp | list ] Prolog list

Page 186: Un lenguaje lógico funcional con restricciones1

186 APENDICE A. GRAMATICA DE T OY .

PATRONES

pat −→ patternspat conop pat constructor operator

| ( apat )+ applicationapat −→ application pattern

varsym variable| con constructor| fun function| integer integer number| real real number| ( ) unit| ( pat op ) left-section

| ( op pat ) right-section

| ( pat ( , exp )∗ ) tuple| listapat list

listapat −→ list of patterns[ [ apat ( , apat )∗ ] ] enumerated list

| [ apat | listapat ] Prolog list

Page 187: Un lenguaje lógico funcional con restricciones1

Apendice B

Declaracion de primitivas (archivobasic.toy)

/*** THIS FILE DEFINES THE PREDEFINED FUNCTIONS AND TYPES OF THE SYSTEM ***/

data bool = true | false

% primitive functions source code can be found in ’primitives.pl’ where% its actual type is also declared.% Type definitons in this file are just informative

% unary integer and real functionsprimitive uminus, % unary minus operator

abs % absolute value:: real -> real

% unary real functionsprimitive sqrt,

ln,exp, % natural logarithm and exponentialsin,cos,tan,cot,asin,acos,atan,acot,sinh,cosh,tanh,coth,asinh,acosh,atanh,acoth:: real -> real

% binary arithmetic operators and functions for reals and integersprimitive (+),(-),(*),min,max :: real -> real -> real

% binary real functionsprimitive (/),

(**),log % Exponentiation and logarithm:: real -> real -> real

187

Page 188: Un lenguaje lógico funcional con restricciones1

188 APENDICE B. DECLARACION DE PRIMITIVAS (ARCHIVO BASIC.TOY)

% integer powersprimitive (^) :: real -> int -> real

% binary integer functionsprimitive div, mod, gcd :: int -> int -> int

% rounding and truncating functionsprimitive round,trunc,floor,ceiling :: real -> int

% integer to real conversionprimitive toReal :: int -> real

% relational operatorsprimitive (<),(<=),(>),(>=) :: real -> real -> bool

% equality and disequality functionsprimitive (==),(/=) :: A -> A -> bool

% infix operator precedencesinfix 90 ^,**infix 80 /infixl 80 *infixl 70 +,-

infix 50 < ,<=,>,>=infix 20 ==, /=

% ’if_then_else’ and ’if_then’ functions are equivalent to the ’sugar syntax’% functions if .. then .. else and if .. then, but useful as partial functionsif_then_else :: bool -> A -> A -> Aif_then_else true X Y = Xif_then_else false X Y = Y

if_then :: bool -> A -> Aif_then true X = X

% ’flip’ function is necessary for syntax sections management .flip :: (A -> B -> C) -> B -> A -> Cflip F X Y = F Y X

Page 189: Un lenguaje lógico funcional con restricciones1

Apendice C

Funciones de uso comun (archivomisc.toy)

% misc.toy: a collection of useful functions and type declarations,% most of them taken from Gofer’s prelude

% type alias for stringstype string = [char]

infixl 90 !! % nth-element selectorinfixr 90 . % function compositioninfixr 50 ++ % concatenation of listsinfixr 40 // % non-deterministic choiceinfixr 40 ‘and‘,/\ % parallel and sequential conjunctioninfixr 30 ‘or‘,\/ % parallel and sequential disjunction

% boolean functions

and,or,(/\),(\/) :: bool -> bool -> boolnot :: bool -> bool

% Parallel andfalse ‘and‘ _ = false_ ‘and‘ false = falsetrue ‘and‘ true = true

% Parallel ortrue ‘or‘ _ = true_ ‘or‘ true = truefalse ‘or‘ false = false

189

Page 190: Un lenguaje lógico funcional con restricciones1

190 APENDICE C. FUNCIONES DE USO COMUN (ARCHIVO MISC.TOY)

% Sequential andfalse /\ _ = falsetrue /\ X = X

% Sequential ortrue \/ X = truefalse \/ X = X

% Negationnot true = falsenot false = true

andL, orL ,orL’ :: [bool] -> boolandL = foldr (/\) trueorL = foldr or falseorL’ = foldr (\/) false% orL’ is ’stricter’, but more deterministic, than orL

any, any’,all :: (A -> bool) -> [A] -> boolany P = orL . (map P)any’ P = orL’ . (map P)% any’ is ’stricter’, but more deterministic, than any

all P = andL . (map P)

undefined :: Aundefined = if false then undefined

% (nf X) is the identity, restricted to finite and totally defined values% Operationally, (nf X) forces the computation of a normal form for X,% if it exists.nf X = X <== X==X

% (hnf X) is the identity, restricted to not undefined values.% Operationally, (hnf X) forces the computation of a head normal form for X,% if it exists.hnf X = X <== X /= _

% (strict F) is the restriction of F to finite, totally defined arguments.% Operationally, it forces the evaluation to nf of the argument before applying Fstrict F X = F X <== X==X

% (strict’ F) is the restriction of F to not undefined arguments.% Operationally, it forces the evaluation to hnf of the argument before applying Fstrict’ F X = F X <== X /= _

Page 191: Un lenguaje lógico funcional con restricciones1

191

% mapping a function through a listmap:: (A -> B) -> [A] -> [B]map F [] = []map F [X|Xs] = [(F X)|(map F Xs)]

%% Function composition(.) :: (B -> C) -> (A -> B) -> (A -> C)(F . G) X = F (G X)

%% List concatenation(++) :: [A] -> [A] -> [A][] ++ Ys = Ys[X|Xs] ++ Ys = [X|Xs ++ Ys]

%% Xs!!N is the Nth-element of Xs(!!) :: [A] -> int -> A[X|Xs] !! N = if N==0 then X else Xs !! (N-1)

iterate :: (A -> A) -> A -> [A]iterate F X = [X|iterate F (F X)]

repeat :: A -> [A]repeat X = [X|repeat X]

copy :: int -> A -> [A]copy N X = take N (repeat X)

filter :: (A -> bool) -> [A] -> [A]filter L [] = []filter P [X|Xs] =

if P X then [X|filter P Xs]else filter P Xs

%% Fold primitives: The foldl and scanl functions, variants foldl1 and%% scanl1 for non-empty lists, and strict variants foldl’ scanl’ describe%% common patterns of recursion over lists. Informally:%%%% foldl F a [x1, x2, ..., xn] = F (...(f (f a x1) x2)...) xn%% = (...((a ‘f‘ x1) ‘f‘ x2)...) ‘f‘ xn%% etc...%%%% The functions foldr, scanr and variants foldr1, scanr1 are duals of these%% functions:%% e.g. foldr F a Xs = foldl (flip f) a (reverse Xs ) for finite lists Xs .

foldl :: (A -> B -> A) -> A -> [B] -> Afoldl F Z [] = Zfoldl F Z [X|Xs] = foldl F (F Z X) Xs

Page 192: Un lenguaje lógico funcional con restricciones1

192 APENDICE C. FUNCIONES DE USO COMUN (ARCHIVO MISC.TOY)

foldl1 :: (A -> A -> A) -> [A] -> Afoldl1 F [X|Xs] = foldl F X Xs

foldl’ :: (A -> B -> A) -> A -> [B] -> Afoldl’ F A [] = Afoldl’ F A [X|Xs] = strict (foldl’ F) (F A X) Xs

scanl :: (A -> B -> A) -> A -> [B] -> [A]scanl F Q [] = [Q]scanl F Q [X|Xs] = [Q|scanl F (F Q X) Xs]

scanl1 :: (A -> A -> A) -> [A] -> [A]scanl1 F [X|Xs] = scanl F X Xs

scanl’ :: (A -> B -> A) -> A -> [B] -> [A]scanl’ F Q [] = [Q]scanl’ F Q [X|Xs] = [Q|strict (scanl’ F) (F Q X) Xs]

foldr :: (A -> B -> B) -> B -> [A] -> Bfoldr F Z [] = Zfoldr F Z [X|Xs] = F X (foldr F Z Xs)

foldr1 :: (A -> A -> A) -> [A] -> Afoldr1 F [X] = Xfoldr1 F [X,Y|Xs] = F X (foldr1 F [Y|Xs])

scanr :: (A -> B -> B) -> B -> [A] -> [B]scanr F Q0 [] = [Q0]scanr F Q0 [X|Xs] = auxForScanr F X (scanr F Q0 Xs)%whereauxForScanr F X Ys = [F X (head Ys)|Ys]

scanr1 :: (A -> A -> A) -> [A] -> [A]scanr1 F [X] = [X]scanr1 F [X,Y|Xs] = auxForScanr F X (scanr1 F [Y|Xs])

%% List breaking functions:%%%% take n Xs returns the first n elements of Xs%% drop n Xs returns the remaining elements of Xs%% splitAt n Xs = (take n Xs , drop n Xs )%%%% takeWhile P Xs returns the longest initial segment of Xs whose%% elements satisfy p%% dropWhile P Xs returns the remaining portion of the list%% span P Xs = (takeWhile P Xs , dropWhile P Xs )

Page 193: Un lenguaje lógico funcional con restricciones1

193

%%%% takeUntil P Xs returns the list of elements upto and including the%% first element of Xs which satisfies p

take :: int -> [A] -> [A]take N [] = []take N [X|Xs] = if N==0 then [] else [X|take (N-1) Xs]

drop :: int -> [A] -> [A]drop N [] = []drop N [X|Xs] = if N==0 then [X|Xs] else drop (N-1) Xs

splitAt :: int -> [A] -> ( [A] , [A] )splitAt N [] = ([],[])splitAt N [X|Xs] = if N==0

then ([], [X|Xs])else auxForSplitAt X (splitAt (N-1) Xs)

%whereauxForSplitAt X (Xs,Ys) = ([X|Xs],Ys)

takeWhile :: (A -> bool) -> [A] -> [A]takeWhile P [] = []takeWhile P [X|Xs] = if P X then [X| takeWhile P Xs] else []

takeUntil :: (A -> bool) -> [A] -> [A]takeUntil P [] = []takeUntil P [X|Xs] = if P X then [X] else [X| takeUntil P Xs]

dropWhile :: (A -> bool) -> [A] -> [A]dropWhile P [] = []dropWhile P [X|Xs] = if P X then dropWhile P Xs else [X|Xs]

span, break :: (A -> bool) -> [A] -> ( [A] , [A] )span P [] = ([],[])span P [X|Xs] = if P X

then auxForSpan X (span P Xs)else ([], [X|Xs])

auxForSpan X (Xs,Ys) = ([X|Xs],Ys) % Identical to auxForSplitAt

break P = span (not . P)

Page 194: Un lenguaje lógico funcional con restricciones1

194 APENDICE C. FUNCIONES DE USO COMUN (ARCHIVO MISC.TOY)

zipWith :: (A->B->C) -> [A]->[B]->[C]zipWith Z [] Bs = []zipWith Z [A|As] [] = []zipWith Z [A|As] [B|Bs] = [Z A B | zipWith Z As Bs]

zip :: [A]->[B]->[(A,B)]zip Xs Ys = zipWith mkpair Xs Ys%wheremkpair :: A -> B ->(A,B)mkpair X Y = (X,Y)

unzip :: [(A,B)] -> ([A],[B])unzip [] = ([],[])unzip [(X,Y)|XsYs] = auxForUnzip X Y (unzip XsYs)

auxForUnzip X Y (Xs,Ys) = ([X|Xs],[Y|Ys])

until :: (A -> bool) -> (A -> A) -> A -> Auntil P F X = if P X then X else until P F (F X)

until’ :: (A -> bool) -> (A -> A) -> A -> [A]until’ P F = (takeUntil P) . (iterate F)

%% Standard combinators: %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %%

const :: A -> B -> Aconst K X = K

id :: A -> Aid X = X

% non-deterministic choice(//) :: A -> A -> AX // _ = X_ // Y = Y

curry :: ((A,B) -> C ) -> A -> B -> Ccurry F A B = F (A,B)

uncurry :: (A -> B -> C ) -> (A,B) -> Cuncurry F (A,B) = F A B

fst :: (A,B) -> Afst (X,Y) = X

Page 195: Un lenguaje lógico funcional con restricciones1

195

snd :: (A,B) -> Bsnd (X,Y) = Y

fst3 :: (A,B,C) -> Afst3 (X,Y,Z) = X

snd3 :: (A,B,C) -> Bsnd3 (Y,X,Z) = X

thd3 :: (A,B,C) -> Cthd3 (Y,Z,X) = X

subtract :: real -> real-> realsubtract = flip (-)

even, odd :: int -> booleven X = (X ‘mod‘ 2) == 0odd = not . even

lcm :: int -> int -> intlcm X Y = if ((X==0) \/ (Y == 0)) then 0

else abs ((X ‘div‘ (gcd X Y)) * Y)

%%%% Standard list processing functions: %% %% %% %% %% %%

head :: [A] -> Ahead [X|_] = X

last :: [A] -> Alast [X] = Xlast [_,Y|Xs] = last [Y|Xs]

tail :: [A] -> [A]tail [_|Xs] = Xs

init :: [A] -> [A]init [X] = []init [X,Y|Xs] = [X|init [Y|Xs]]

nub :: [A] -> [A] %% remove duplicates from listnub [] = []nub [X|Xs] = [X| nub (filter (X /=) Xs)]

length :: [A] -> intlength [] = 0length [_|Xs] = 1 + length Xs

Page 196: Un lenguaje lógico funcional con restricciones1

196 APENDICE C. FUNCIONES DE USO COMUN (ARCHIVO MISC.TOY)

size :: [A] -> intsize = length . nub

reverse :: [A] -> [A] %% reverse elements of listreverse = foldl (flip (:)) []

member,notMember :: A -> [A] -> boolmember = any’ . (==) %% test for membership in listnotMember = all . (/=) %% test for non-membership

concat :: [[A]] -> [A] %% concatenate list of listsconcat = foldr (++) []

transpose :: [[A]] -> [[A]] %% transpose list of liststranspose = foldr

auxForTranspose[]

%whereauxForTranspose Xs Xss = zipWith (:) Xs (Xss ++ repeat [])

%% (\\) is used to remove the first occurrence of each element in the second%% list from the first list. It is a kind of inverse of (++) in the sense%% that (xs ++ ys) \\ xs = ys for any finite list xs of proper values xs.

infix 50 \\

(\\) :: [A] -> [A] -> [A](\\) = foldl del%where[] ‘del‘ Y = [][X|Xs] ‘del‘ Y = if X == Y then Xs

else [X|Xs] ‘del‘ Y

Page 197: Un lenguaje lógico funcional con restricciones1

Apendice D

Archivo toycomm.pl

/*

Created: 3-6-96Modified: 18.6.97Autor: Paco & JaimeDescription: *** STORES VERSION *** This module is neccesary for executing

programs in Toy. The system loads it automatically at thebegining. It contains all the common predicates to alltoy-programs.

/*************** CODE FOR HNF ***************/%:-dynamic hnf/4.

/*

% Para poner el contador de hnf’s hay que inicializar el contador con la llamada% bb_put(user:hnfCont,0) y activar este predicado. Despues de la ejecucion del% objetivo se puede recurerar el contador con bb_get(user:hnfCont,C).

hnf(E,H):-bb_get(user:hnfCont,CH),CH1 is CH+1,bb_put(user:hnfCont,CH1),fail.

*/

197

Page 198: Un lenguaje lógico funcional con restricciones1

198 APENDICE D. ARCHIVO TOYCOMM.PL

hnf(E,H,Cin,Cout):-var(E),!,(

var(H),!,H=E,Cin=Cout

;extractCtr(E,Cin,Cout1,CE),H=E,propagate(H,CE,Cout1,Cout)

).

hnf(’$$susp’(Fun,Args,R,S),H,Cin,Cout):-!,(S==hnf,!,hnf(R,H,Cin,Cout);H=R,S=hnf,hnf_susp(Fun,Args,H,Cin,Cout)).

hnf(T,H,Cin,Cin):-H=T.

unifyHnfs(H,L,Cin,Cout):-var(H),!,extractCtr(H,Cin,Cout1,CH),H=L,propagate(H,CH,Cout1,Cout).

unifyHnfs(H,H,Cin,Cin).

/*************** CODE FOR EQUAL ***************/

equal(L,R,Cin,Cout):-var(L),!,hnf(R,HR,Cin,Cout1),equalHnf(L,HR,Cout1,Cout).

Page 199: Un lenguaje lógico funcional con restricciones1

199

equal(R,L,Cin,Cout):-var(L),!,hnf(R,HR,Cin,Cout1),%hnf(L,HL,CC1,CV1,CC2,CV2),equalHnf(L,HR,Cout1,Cout).

equal(L,R,Cin,Cout):-constructor(L,C/N),!,functor(T,C,N),hnf(R,T,Cin,Cout1),eqFrontier(L,T,FL/[],FR/[]),equalList(FL,FR,Cout1,Cout).

equal(R,L,Cin,Cout):-constructor(L,C/N),!,functor(T,C,N),hnf(R,T,Cin,Cout1),eqFrontier(L,T,FL/[],FR/[]),equalList(FL,FR,Cout1,Cout).

% Both are suspended forms, but we don’t know if they are solved or not.

equal(’$$susp’(_,_,R,S),L,Cin,Cout):-S==hnf,!,equal(R,L,Cin,Cout).

equal(L,’$$susp’(_,_,R,S),Cin,Cout):-S==hnf,!,equal(R,L,Cin,Cout).

equal(L,R,Cin,Cout):-hnf(L,HL,Cin,Cout1),equal(HL,R,Cout1,Cout).

equalHnf(L,R,Cin,Cout):-var(L),!,binding(L,R,Cin,Cout).

equalHnf(R,L,Cin,Cout):-var(L),!,binding(L,R,Cin,Cout).

equalHnf(R,L,Cin,Cout):-eqFrontier(R,L,FR/[],FL/[]),!,equalList(FR,FL,Cin,Cout).

Page 200: Un lenguaje lógico funcional con restricciones1

200 APENDICE D. ARCHIVO TOYCOMM.PL

/*************** CODE FOR BINDING ***************/

binding(X,Y,Cin,Cout):-var(Y),!,unifyVar(X,Y,Cin,Cout).

binding(X,Y,Cin,Cout):-!,occursNot(X,Y,ShY,Lst),extractCtr(X,Cin,Cout1,CX),X=ShY,propagate(ShY,CX,Cout1,Cout2),equalList(Lst,Cout2,Cout).

% It may be improved because propagate has the information of ShY is a hnf and% all elements in CX are hnf’s

/*************** CODE FOR NOT EQUAL ***************/

notEqual(X,Y,Cin,Cout):-hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),notEqualHnf(HX,HY,Cout2,Cout).

% First of all, we check if the solver is activated. In such case the% the disequality constraint is over real numbers, we send it to the the solver% and forget it.

% &clprnotEqualHnf(X,Y,Cin,Cin):-

clpr_active,(isReal(X);isReal(Y)),!,{X=\=Y}.

notEqualHnf(X,Y,Cin,Cout):-var(X),!,notEqualVar(X,Y,Cin,Cout).notEqualHnf(Y,X,Cin,Cout):-var(X),!,notEqualVar(X,Y,Cin,Cout).

Page 201: Un lenguaje lógico funcional con restricciones1

201

notEqualHnf(R,L,Cin,Cout):-eqFrontier(R,L,FR/[],FL/[]),!,notEqualList(FR,FL,Cin,Cout); % eqFrontier falloCin=Cout.

% If Y is a variable (both are variables), we simply put the constraint into% the store

notEqualVar(X,Y,Cin,Cout):-var(Y),!,X\==Y,addCtr(X,Y,Cin,Cout1),addCtr(Y,X,Cout1,Cout).

% These two clauses allow to bind X=false in presence of a contraint of the form% X/=true (and X=true in presence of X/=false) without do anything more

notEqualVar(X,true,Cin,Cout):-!,hnf(X,false,Cin,Cout).notEqualVar(X,false,Cin,Cout):-!,hnf(X,true,Cin,Cout).

% In othrer case we make an occursNot in order to discover if it is a hnf or if% it has function calls (this is an artificial use of occursNot)

notEqualVar(X,Y,Cin,Cout):-occursNot(X,Y,ShY,Lst),!,contNotEqual(X,Y,ShY,Lst,Cin,Cout).

% It occursNot fail, then they are distinct automaticallynotEqualVar(_X,_Y,Cin,Cin).

% If the list produced by occursNot unifies with []/[], that is because Y is a% hnf, and we only add the constraintcontNotEqual(X,_,ShY,[]/[],Cin,Cout):-

!,addCtr(X,ShY,Cin,Cout).

Page 202: Un lenguaje lógico funcional con restricciones1

202 APENDICE D. ARCHIVO TOYCOMM.PL

% Y is not a hnf: we generate constructor symbols of the same type ensuring that% the disequality es satifiedcontNotEqual(X,Y,_,_,Cin,Cout):-

constructor(Y,C/_N,ArgsY),!,const(C,_,_,Dest),(genConstructor(Dest,Z,C1,_ArgsZ), % const with <>name and the same

% destination typeC\==C1,hnf(X,Z,Cin,Cout);genConstructor(Dest,Z,C,ArgsZ), % The same name and <>’s Argshnf(X,Z,Cin,Cout1),notEqualList(ArgsZ,ArgsY,Cout1,Cout)).

/*************** CODE FOR FUNCTION == ***************/

’$$eqFun’(X,Y,H,Cin,Cout):-H==true,!,equal(X,Y,Cin,Cout).’$$eqFun’(X,Y,H,Cin,Cout):-H==false,!,notEqual(X,Y,Cin,Cout).

’$$eqFun’(X,Y,H,Cin,Cout):-var(X),!,(H=true,equal(X,Y,Cin,Cout);H=false,notEqual(X,Y,Cin,Cout)).

’$$eqFun’(X,Y,H,Cin,Cout):-var(Y),!,(H=true,equal(X,Y,Cin,Cout);H=false,notEqual(Y,X,Cin,Cout)).

’$$eqFun’(X,Y,H,Cin,Cout):-hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),eqFunHnf(HX,HY,H,Cout2,Cout).

eqFunHnf(X,Y,H,Cin,Cout):-(var(X);var(Y)),!,’$$eqFun’(X,Y,H,Cin,Cout).

eqFunHnf(X,Y,H,Cin,Cout):-eqFrontier(X,Y,FrontierX/[],FrontierY/[]),!,eqFunAnd(FrontierX,FrontierY,H,Cin,Cout); % eqFrontier falloH=false,Cin=Cout.

Page 203: Un lenguaje lógico funcional con restricciones1

203

eqFunAnd([],[],true,Cin,Cin):-!.

% Non deterministic choice: a conjunction of equalities is true if all of them% are true and it is false if it is false one of them

eqFunAnd([X1|Rest1],[Y1|Rest2],H,Cin,Cout):-’$$eqFun’(X1,Y1,H1,Cin,Cout1),eqFunAnd_1(Rest1,Rest2,H1,H,Cout1,Cout).

eqFunAnd([_|Rest1],[_|Rest2],false,Cin,Cout):-notEqualList(Rest1,Rest2,Cin,Cout).

eqFunAnd_1(_,_,false,false,Cin,Cin).eqFunAnd_1(Rest1,Rest2,true,true,Cin,Cout):-

equalList(Rest1,Rest2,Cin,Cout).

/*************** CODE FOR FUNCTION /= ***************/

’$$notEqFun’(X,Y,H,Cin,Cout):-H==true,!,notEqual(X,Y,Cin,Cout).’$$notEqFun’(X,Y,H,Cin,Cout):-H==false,!,equal(X,Y,Cin,Cout).%{paco}29-11-96’$$notEqFun’(X,Y,H,Cin,Cout):-

’$$eqFun’(X,Y,Z,Cin,Cout),negate(Z,H).

negate(true,false) :- !.negate(false,true).

/*************** CODE FOR OCCURS CHECK ***************/

occursNot(X,Y,ShY,L/L):-var(Y),!,X\==Y,Y=ShY.occursNot(_,’$$susp’(E,Args,R,S),Z,[Z==’$$susp’(E,Args,R,S)|L]/L):-var(S),!.occursNot(X,’$$susp’(_,_,R,_),ShR,L/M):-!,occursNot(X,R,ShR,L/M).occursNot(X,T,ShT,L/M):-

T=..[Name|Args],lstOccursNot(X,Args,ShArgs,L/M),ShT=..[Name|ShArgs].

Page 204: Un lenguaje lógico funcional con restricciones1

204 APENDICE D. ARCHIVO TOYCOMM.PL

lstOccursNot(_,[],[],L/L).lstOccursNot(X,[Ar|Rest],[ShAr|RSh],L/M):-

occursNot(X,Ar,ShAr,L/L1),lstOccursNot(X,Rest,RSh,L1/M).

/*************** AUXILIAR CODE ***************/

equalList([],[],Cin,Cin):-!.equalList([Ar1|R1],[Ar2|R2],Cin,Cout):-

equal(Ar1,Ar2,Cin,Cout1),equalList(R1,R2,Cout1,Cout).

equalList([]/[],Cin,Cin):-!.equalList([(Z==Y)|L]/M,Cin,Cout):-

equal(Z,Y,Cin,Cout1),equalList(L/M,Cout1,Cout).

% Indeterministic choice for doing a pair false with independence of the restnotEqualList([X|R1],[Y|R2],Cin,Cout):-

notEqual(X,Y,Cin,Cout);notEqualList(R1,R2,Cin,Cout).

% Frontier of two terms. The predicate eqFrontier(T1,T2,L1/R1,L2/R2) extract the% shell of common constructors of T1 and T2. In the differece lists it puts the% common part. If the shell contains some distinct constructor for the terms,% eqFrontier fails automatically

eqFrontier(X,Y,[X|L1]/L1,[Y|L2]/L2) :-(var(X);var(Y)),!.

eqFrontier(X,Y,FX,FY) :-constructor(X,NameX,ArgsX),constructor(Y,NameY,ArgsY),!,NameX==NameY,eqFrontierList(ArgsX,ArgsY,FX,FY).

Page 205: Un lenguaje lógico funcional con restricciones1

205

eqFrontier(’$$susp’(Fun,Args,R,S),Y,FX,FY) :-!,(S==hnf,!,eqFrontier(R,Y,FX,FY);FX = [’$$susp’(Fun,Args,R,S)|L1]/L1,FY = [Y|L2] / L2).

eqFrontier(X,’$$susp’(Fun,Args,R,S),FX,FY) :-!,(S==hnf,!,eqFrontier(X,R,FX,FY);FY = [’$$susp’(Fun,Args,R,S)|L1]/L1,FX = [X|L2] / L2).

eqFrontierList([],[],L1/L1,L2/L2).

eqFrontierList([X|Xs],[Y|Ys],LX/MX,LY/MY) :-eqFrontier(X,Y,LX/L1,LY/L2),eqFrontierList(Xs,Ys,L1/MX,L2/MY).

% We have two constructor (one of arity two and the other of arity three). The% firts one simply check if what we pass is a hnf and then it returns its name% and arity. The second one returns the list of arguments two.

constructor(C,C/0):-number(C),!.constructor(T,C/N):-

functor(T,C,N),!,(const(C,_,_,_),!;funct(C,Ar,_,_,_),!,N<Ar).

constructor(C,C/0,[]):-number(C),!.constructor(T,C/N,Args):-

functor(T,C,N),!,(const(C,_,_,_),!;funct(C,Ar,_,_,_),!,N<Ar), % ArregladoT=..[_|Args].

Page 206: Un lenguaje lógico funcional con restricciones1

206 APENDICE D. ARCHIVO TOYCOMM.PL

genConstructor(TipDest,Cons,Name,Args):-const(Name,Ar,_,TipDest), % We look for the same destination typefunctor(Cons,Name,Ar), % build the termCons=..[Name|Args]. % extract the arguments (new vars.)

% STORE DEAL

propagate(_,[],Cin,Cin):-!.propagate(Y,[C|R],Cin,Cout):-

(var(C), % We don’t need to solve Y /= C,

% because C is a variable and it already had,% the disequality C /= Y.% Notice that C can not be Y

!,Cout1=Cin

;notEqualTerm(Y,C,Cin,Cout1)),

propagate(Y,R,Cout1,Cout).

% notEqualTerm: notEqual especialized for terms only with contructors% Because of eficience, we don’t make any kind of occur-check, and then% notEqualTerm(X,s(X)), instead of be trivially satisfiable as in the% notEqual(X,s(X)) case, inserts the constraint X /= s(X)

notEqualTerm(T1,T2,Cin,Cout):-var(T1),!,notEqualVarTerm(T1,T2,Cin,Cout).

notEqualTerm(T1,T2,Cin,Cout):-var(T2),!,notEqualVarTerm(T2,T1,Cin,Cout).

notEqualTerm(T1,T2,Cin,Cout):-constructor(T1,C1/A1,Args1),constructor(T2,C2/A2,Args2),(C1/A1\==C2/A2,!,Cout=Cin;notEqualTermList(Args1,Args2,Cin,Cout)).

Page 207: Un lenguaje lógico funcional con restricciones1

207

notEqualVarTerm(X,Y,Cin,Cout):-var(Y),!,X\==Y,addCtr(X,Y,Cin,Cout1),addCtr(Y,X,Cout1,Cout).

notEqualVarTerm(X,Y,Cin,Cout):-!,addCtr(X,Y,Cin,Cout).

notEqualTermList([X|R1],[Y|R2],Cin,Cout):-(notEqualTerm(X,Y,Cin,Cout);notEqualTermList(R1,R2,Cin,Cout)).

% CONSTRAINT HANDLING

% Insertion of a new constraint Var/=Term

addCtr(X,Term,[],[X:[Term]]):-!.addCtr(X,Term,[Y:Ctr|R],[Y:[Term|Ctr]|R]):-

X==Y,!.

addCtr(X,Term,[F|R],[F|R1]):-addCtr(X,Term,R,R1).

% Extraction of constraints asociated to a variable. We extract the constraints% asociated from the store and return them in the last argument

extractCtr(_,[],[],[]):-!.extractCtr(X,[V:W|R],R,W):-

X==V,!.

extractCtr(X,[V:W|R],[V:W|R1],L):-extractCtr(X,R,R1,L).

Page 208: Un lenguaje lógico funcional con restricciones1

208 APENDICE D. ARCHIVO TOYCOMM.PL

% Unification on two vars X Y: if they are not the same var, nothing. Else we% unify them and do the union of their constraints

unifyVar(X,Y,Cin,Cout):-X==Y,!,Cout=Cin.

unifyVar(X,Y,Cin,Cout):-extractTwoCtr(X,Y,Cin,Cout1,CX,CY),X=Y,!,update(X,CX,CY,Cout1,Cout).

% extracting constraints of two vars X Y in CX CY. This form of operation in% inify is equivalent to perform two extracCtr, but improves the way, because% we have got both Stores in one pass

extractTwoCtr(_,_,[],[],[],[]).extractTwoCtr(X,Y,[Z:CZ|R],Cout,CX,CY):-

(X==Z,!,CX=CZ,extractCtr(Y,R,Cout,CY)

;Y==Z,!,CY=CZ,extractCtr(X,R,Cout,CX)

).extractTwoCtr(X,Y,[Ctr|R],[Ctr|R1],CX,CY):-extractTwoCtr(X,Y,R,R1,CX,CY).

% Union of constraints asociated to vars X Y checking that beteween these% constraints is not X/=Y

update(Y,[],CY,Cin,Cout):-insertCtrs(Y,CY,Cin,Cout).

update(Y,[T|Ts],CY,Cin,Cout):-Y\==T, % comprobacion de no existe una restriccion X/=Y!,update(Y,Ts,[T|CY],Cin,Cout).

insertCtrs(_,[],Cin,Cin):-!. % esta clausula solo sirve para no meter% listas de restricciones vacias

insertCtrs(Y,CY,Cin,[Y:CY|Cin]).

Page 209: Un lenguaje lógico funcional con restricciones1

209

% REAL CONSTRAINTS% isReal(X) success iff X is a number or X is a var affected by some constraint

isReal(X):-number(X),!.isReal(X):-

var(X),linear:dump([X],_,L),!,L\==[].

% CONVERSION OF DISEQUALITY CONSTRAINTS (SYNTACTIC) TO REAL CONSTRAINTS% THIS CODE IS ONLY USED WHEN THE SYSTEM IS RUNNING WITH REALS

/*Disequality constraints beteween vars are dealt as syntactic ones. They are stored as any other one. When we throw a constraint to the solver that involves one of this vars, the disequality constraints that were affecting it pass to be real constraints instead of syntactic ones. At this point all disequality contraints that affect this var must be passed to solver, in order to allow it to look for inconsistences that may do it to fail.*/

toSolver(X,Cin,Cin):-nonvar(X),!.

toSolver(X,Cin,Cout):-extractCtr(X,Cin,Cout1,CX),passToSolver(X,CX,Cout1,Cout).

passToSolver(_,[],Cin,Cin).passToSolver(X,[Y|R],Cin,Cout):-

{X=\=Y},(

var(Y),!,toSolver(Y,Cin,Cout1),passToSolver(X,R,Cout1,Cout)

;passToSolver(X,R,Cin,Cout)

).

Page 210: Un lenguaje lógico funcional con restricciones1

210 APENDICE D. ARCHIVO TOYCOMM.PL

Page 211: Un lenguaje lógico funcional con restricciones1

Apendice E

Primitivas sin restriccionesaritmeticas (archivo primitives.pl)

/*This module contains the code for primitives. This functions haves a

direct tranlation into Prolog. Cin and Cout are the stores of disequalityconstraints and must be placed as in the following examples. Before the Prologoperation the hnf predicate must be called for each one argument.

Types for aritmethic functions are defined in a quite ad-hoc way herein order to allow (a very limited) overloading of arithmetic operations.The idea is the following: we represent the types ’int’ and ’real’by the terms ’num(int)’ and ’num(real)’, and we use ’num(A)’ forachieving overloading, when desired.*/

/*Nota: Los tipos de las primitivas se toman de aqui (no del standard) parapoder forzar el tipo de algunas funciones como / o sqrt y siempre devuelvanreal (num(float))

P.e. el + respeta la declaracion + :: num(A) -> num(A) -> num(A), lo que quieredecir que si los dos argumentos son int el resultado es int y si alguno de los

dos es float el resultado es float.En / tenemos / :: num(A) -> num(

*/

/*************** CODE PRIMITIVE FUNCTIONS ***************/

primInfix(/, left, 90).primInfix(*, right, 90).primInfix(+, left, 50).primInfix(-, left, 50).

211

Page 212: Un lenguaje lógico funcional con restricciones1

212APENDICE E. PRIMITIVAS SIN RESTRICCIONES ARITMETICAS (ARCHIVO PRIMITIVES.PL)

primInfix(^, noasoc, 98).primInfix(**, noasoc, 98).primInfix(<, noasoc, 30).primInfix(<=, noasoc, 30).primInfix(>, noasoc, 30).primInfix(>=, noasoc, 30).primInfix(==, noasoc, 10).primInfix(/=, noasoc, 10).primInfix(:,right,15).primInfix(’,’,right,12).

primitiveFunct(==, 2, 2, (A -> (A -> bool)), bool).primitiveFunct(/=, 2, 2, (A -> (A -> bool)), bool).

% Funciones unarias para enteros y reales

primitiveFunct(uminus, 1, 1, (num(A) -> num(A)), num(A)).’$uminus’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is -HX; errPrim.

primitiveFunct(abs, 1, 1, (num(A) -> num(A)), num(A)).’$abs’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is abs(HX); errPrim.

% Funciones reales unarias

primitiveFunct(sqrt, 1, 1, (num(_A) -> num(float)), num(float)).’$sqrt’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is sqrt(HX); errPrim.

primitiveFunct(ln, 1, 1, (num(float) -> num(float)), num(float)).’$ln’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is log(HX); errPrim.

primitiveFunct(exp, 1, 1, (num(float) -> num(float)), num(float)).’$exp’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is exp(HX); errPrim.

Page 213: Un lenguaje lógico funcional con restricciones1

213

primitiveFunct(sin, 1, 1, (num(float) -> num(float)), num(float)).’$sin’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is sin(HX); errPrim.

primitiveFunct(cos, 1, 1, (num(float) -> num(float)), num(float)).’$cos’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is cos(HX); errPrim.

primitiveFunct(tan, 1, 1, (num(float) -> num(float)), num(float)).’$tan’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is tan(HX); errPrim.

primitiveFunct(cot, 1, 1, (num(float) -> num(float)), num(float)).’$cot’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is cot(HX); errPrim.

primitiveFunct(asin, 1, 1, (num(float) -> num(float)), num(float)).’$asin’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is asin(HX); errPrim.

primitiveFunct(acos, 1, 1, (num(float) -> num(float)), num(float)).’$acos’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is acos(HX); errPrim.

primitiveFunct(atan, 1, 1, (num(float) -> num(float)), num(float)).’$atan’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is atan(HX); errPrim.

primitiveFunct(acot, 1, 1, (num(float) -> num(float)), num(float)).’$acot’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is acot(HX); errPrim.

primitiveFunct(sinh, 1, 1, (num(float) -> num(float)), num(float)).

Page 214: Un lenguaje lógico funcional con restricciones1

214APENDICE E. PRIMITIVAS SIN RESTRICCIONES ARITMETICAS (ARCHIVO PRIMITIVES.PL)

’$sinh’(X,H,Cin,Cout):-hnf(X,HX,Cin,Cout),number(HX) -> H is sinh(HX); errPrim.

primitiveFunct(cosh, 1, 1, (num(float) -> num(float)), num(float)).’$cosh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is cosh(HX); errPrim.

primitiveFunct(tanh, 1, 1, (num(float) -> num(float)), num(float)).’$tanh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is tanh(HX); errPrim.

primitiveFunct(coth, 1, 1, (num(float) -> num(float)), num(float)).’$coth’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is coth(HX); errPrim.

primitiveFunct(asinh, 1, 1, (num(float) -> num(float)), num(float)).’$asinh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is asinh(HX); errPrim.

primitiveFunct(acosh, 1, 1, (num(float) -> num(float)), num(float)).’$acosh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is acosh(HX); errPrim.

primitiveFunct(atanh, 1, 1, (num(float) -> num(float)), num(float)).’$atanh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is atanh(HX); errPrim.

primitiveFunct(acoth, 1, 1, (num(float) -> num(float)), num(float)).’$acoth’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),number(HX) -> H is acoth(HX); errPrim.

% operadores y funciones aritmeticos binarias para enteros y reales

Page 215: Un lenguaje lógico funcional con restricciones1

215

primitiveFunct(+, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).$+(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is HX + HY; errPrim).

primitiveFunct(-, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).$-(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is HX - HY; errPrim).

primitiveFunct(*, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).$*(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is HX * HY; errPrim).

primitiveFunct(min, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).’$min’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is min(HX,HY); errPrim).

primitiveFunct(max, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).’$max’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is max(HX,HY); errPrim).

% funciones reales binarias

primitiveFunct(/, 2, 2, (num(A) -> (num(A) -> num(float))), num(float)).$/(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is HX / HY; errPrim).

primitiveFunct(’**’, 2, 2, (num(_A) -> (num(float) -> num(float))), num(float)).’$**’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),

Page 216: Un lenguaje lógico funcional con restricciones1

216APENDICE E. PRIMITIVAS SIN RESTRICCIONES ARITMETICAS (ARCHIVO PRIMITIVES.PL)

(number(HX),number(HY) -> H is exp(HX,HY); errPrim).

primitiveFunct(log, 2, 2, (num(float) -> (num(float) -> num(float))), num(float)).’$log’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is log(HX,HY); errPrim).

% potencia con exponente natural

primitiveFunct(^, 2, 2, (num(A) -> (num(int) -> num(A))), num(A)).$^(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY), HY >= 0 -> H is exp(HX,HY); errPrim).

% HY >= 0 En otro caso, se podria sacar mensaje, e incluso abortar

primitiveFunct(div, 2, 2, (num(int) -> (num(int) -> num(int))), num(int)).’$div’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is float(HX // HY); errPrim).

primitiveFunct(mod, 2, 2, (num(int) -> (num(int) -> num(int))), num(int)).’$mod’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is float(HX mod HY); errPrim).

primitiveFunct(gcd, 2, 2, (num(int) -> (num(int) -> num(int))), num(int)).’$gcd’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> H is float(gcd(HX,HY)); errPrim).

primitiveFunct(round, 1, 1, (num(_A) -> num(int)), num(int)).’$round’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),(number(HX) -> H is round(HX); errPrim).

Page 217: Un lenguaje lógico funcional con restricciones1

217

primitiveFunct(trunc, 1, 1, (num(_A) -> num(int)), num(int)).’$trunc’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),(number(HX) -> H is float(integer(HX)); errPrim).

primitiveFunct(floor, 1, 1, (num(_A) -> num(int)), num(int)).’$floor’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),(number(HX) -> H is floor(HX); errPrim).

primitiveFunct(ceiling, 1, 1, (num(_A) -> num(int)), num(int)).’$ceiling’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),(number(HX) -> H is ceiling(HX); errPrim).

%Conversion de enteros a reales

primitiveFunct(toReal, 1, 1, (num(_A) -> num(float)), num(float)).’$toReal’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout),(number(HX) -> H=HX; errPrim).

primitiveFunct(<, 2, 2, (num(A) -> (num(A) -> bool)), bool).$<(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> (HX<HY,H=true;HX>=HY,H=false); errPrim).

primitiveFunct(>, 2, 2, (num(A) -> (num(A) -> bool)), bool).$>(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> (HX>HY,H=true;HX=<HY,H=false); errPrim).

primitiveFunct(<=, 2, 2, (num(A) -> (num(A) -> bool)), bool).$<=(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> (HX=<HY,H=true;HX>HY,H=false); errPrim).

Page 218: Un lenguaje lógico funcional con restricciones1

218APENDICE E. PRIMITIVAS SIN RESTRICCIONES ARITMETICAS (ARCHIVO PRIMITIVES.PL)

primitiveFunct(>=, 2, 2, (num(A) -> (num(A) -> bool)), bool).$>=(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout),(number(HX),number(HY) -> (HX>=HY,H=true;HX<HY,H=false); errPrim).

errPrim:-nl,write(’RUNTIME ERROR: Variables are not allowed in arithmetical operations. (/cflpr. should be active to do this)’),nl,!,fail.

Page 219: Un lenguaje lógico funcional con restricciones1

Apendice F

Primitivas con restriccionesaritmeticas (archivoprimitivesClpr.pl)

/*************** CODE PRIMITIVE FUNCTIONS ***************/

primInfix(/, left, 90).primInfix(*, right, 90).primInfix(+, left, 50).primInfix(-, left, 50).primInfix(^, noasoc, 98).primInfix(**, noasoc, 98).primInfix(<, noasoc, 30).primInfix(<=, noasoc, 30).primInfix(>, noasoc, 30).primInfix(>=, noasoc, 30).primInfix(==, noasoc, 10).primInfix(/=, noasoc, 10).primInfix(:,right,15).primInfix(’,’,right,12).

primitiveFunct(==, 2, 2, (A -> (A -> bool)), bool).primitiveFunct(/=, 2, 2, (A -> (A -> bool)), bool).

% Funciones unarias para enteros y reales

primitiveFunct(uminus, 1, 1, (num(A) -> num(A)), num(A)).’$uminus’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),{H = -HX},toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

219

Page 220: Un lenguaje lógico funcional con restricciones1

220APENDICE F. PRIMITIVAS CON RESTRICCIONES ARITMETICAS (ARCHIVO PRIMITIVESCLPR.PL)

primitiveFunct(abs, 1, 1, (num(A) -> num(A)), num(A)).’$abs’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),{H = abs(HX)},toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

% Funciones reales unarias

primitiveFunct(sqrt, 1, 1, (num(_A) -> num(float)), num(float)).’$sqrt’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),{H = pow(HX,1/2)},toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(ln, 1, 1, (num(float) -> num(float)), num(float)).’$ln’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is log(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(exp, 1, 1, (num(float) -> num(float)), num(float)).’$exp’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),{H = exp(HX)},toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(sin, 1, 1, (num(float) -> num(float)), num(float)).’$sin’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),{H = sin(HX)},toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(cos, 1, 1, (num(float) -> num(float)), num(float)).’$cos’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),{H = cos(HX)},

Page 221: Un lenguaje lógico funcional con restricciones1

221

toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(tan, 1, 1, (num(float) -> num(float)), num(float)).’$tan’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),{H = tan(HX)},toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(cot, 1, 1, (num(float) -> num(float)), num(float)).’$cot’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is cot(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(asin, 1, 1, (num(float) -> num(float)), num(float)).’$asin’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is asin(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(acos, 1, 1, (num(float) -> num(float)), num(float)).’$acos’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is acos(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(atan, 1, 1, (num(float) -> num(float)), num(float)).’$atan’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is atan(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(acot, 1, 1, (num(float) -> num(float)), num(float)).’$acot’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is acot(HX),

Page 222: Un lenguaje lógico funcional con restricciones1

222APENDICE F. PRIMITIVAS CON RESTRICCIONES ARITMETICAS (ARCHIVO PRIMITIVESCLPR.PL)

toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(sinh, 1, 1, (num(float) -> num(float)), num(float)).’$sinh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is sinh(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(cosh, 1, 1, (num(float) -> num(float)), num(float)).’$cosh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is cosh(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(tanh, 1, 1, (num(float) -> num(float)), num(float)).’$tanh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is tanh(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(coth, 1, 1, (num(float) -> num(float)), num(float)).’$coth’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is coth(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(asinh, 1, 1, (num(float) -> num(float)), num(float)).’$asinh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is asinh(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(acosh, 1, 1, (num(float) -> num(float)), num(float)).’$acosh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is acosh(HX),

Page 223: Un lenguaje lógico funcional con restricciones1

223

toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(atanh, 1, 1, (num(float) -> num(float)), num(float)).’$atanh’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is atanh(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(acoth, 1, 1, (num(float) -> num(float)), num(float)).’$acoth’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is acoth(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

% operadores y funciones aritmeticos binarias para enteros y reales

primitiveFunct(+, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).$+(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),{H = HX + HY},toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(-, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).$-(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),{H = HX - HY},toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(*, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).$*(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),{H = HX * HY},toSolver(HX,Cout2,Cout3),

Page 224: Un lenguaje lógico funcional con restricciones1

224APENDICE F. PRIMITIVAS CON RESTRICCIONES ARITMETICAS (ARCHIVO PRIMITIVESCLPR.PL)

toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(min, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).’$min’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),{H = min(HX,HY)},toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(max, 2, 2, (num(A) -> (num(A) -> num(A))), num(A)).’$max’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),{H = max(HX,HY)},toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

% funciones reales binarias

primitiveFunct(/, 2, 2, (num(A) -> (num(A) -> num(float))), num(float)).$/(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),{H = HX / HY},toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(’**’, 2, 2, (num(_A) -> (num(float) -> num(float))), num(float)).’$**’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),{H = exp(HX,HY)},toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(log, 2, 2, (num(float) -> (num(float) -> num(float))), num(float)).’$log’(X,Y,H,Cin,Cout):-

Page 225: Un lenguaje lógico funcional con restricciones1

225

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),number(HX),number(HY),H is log(HX,HY),toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

% potencia con exponente natural

primitiveFunct(^, 2, 2, (num(A) -> (num(int) -> num(A))), num(A)).$^(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),{HY >= 0}, % En otro caso, se podria sacar mensaje, e incluso abortar{H = exp(HX,HY)},toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(div, 2, 2, (num(int) -> (num(int) -> num(int))), num(int)).’$div’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),number(HX),number(HY),H is float(HX // HY),toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(mod, 2, 2, (num(int) -> (num(int) -> num(int))), num(int)).’$mod’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),number(HX),number(HY),H is float(HX mod HY),toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(gcd, 2, 2, (num(int) -> (num(int) -> num(int))), num(int)).’$gcd’(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),number(HX),number(HY),H is float(gcd(HX,HY)),toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),

Page 226: Un lenguaje lógico funcional con restricciones1

226APENDICE F. PRIMITIVAS CON RESTRICCIONES ARITMETICAS (ARCHIVO PRIMITIVESCLPR.PL)

toSolver(H,Cout4,Cout).

primitiveFunct(round, 1, 1, (num(_A) -> num(int)), num(int)).’$round’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is round(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(trunc, 1, 1, (num(_A) -> num(int)), num(int)).’$trunc’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is float(integer(HX)),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(floor, 1, 1, (num(_A) -> num(int)), num(int)).’$floor’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is floor(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

primitiveFunct(ceiling, 1, 1, (num(_A) -> num(int)), num(int)).’$ceiling’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H is ceiling(HX),toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

%Conversion de enteros a reales

primitiveFunct(toReal, 1, 1, (num(_A) -> num(float)), num(float)).’$toReal’(X,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),number(HX),H=HX,toSolver(HX,Cout1,Cout2),toSolver(H,Cout2,Cout).

% Operadores relacionales.

primitiveFunct(<, 2, 2, (num(A) -> (num(A) -> bool)), bool).

Page 227: Un lenguaje lógico funcional con restricciones1

227

$<(X,Y,H,Cin,Cout):-hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),(H=true,{HX<HY};H=false,{HX>=HY}),toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(>, 2, 2, (num(A) -> (num(A) -> bool)), bool).$>(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),(H=true,{HX>HY};H=false,{HX=<HY}),toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(<=, 2, 2, (num(A) -> (num(A) -> bool)), bool).$<=(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),(H=true,{HX=<HY};H=false,{HX>HY}),toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

primitiveFunct(>=, 2, 2, (num(A) -> (num(A) -> bool)), bool).$>=(X,Y,H,Cin,Cout):-

hnf(X,HX,Cin,Cout1),hnf(Y,HY,Cout1,Cout2),(H=true,{HX>=HY};H=false,{HX<HY}),toSolver(HX,Cout2,Cout3),toSolver(HY,Cout3,Cout4),toSolver(H,Cout4,Cout).

Page 228: Un lenguaje lógico funcional con restricciones1

228APENDICE F. PRIMITIVAS CON RESTRICCIONES ARITMETICAS (ARCHIVO PRIMITIVESCLPR.PL)

Page 229: Un lenguaje lógico funcional con restricciones1

Apendice G

Construccion del arbol definicional

/************************************************************************LLAMADA AL MODULO:

arbol(+Fun % funcion definida en el formato que devuelve el

% analisis sintactico pero con las variables como% vars reales y no salida del a. sintactico. Este% formato se genera y chequea semanticamente en% el modulo tradfun.ari

-Patron % Devuelve el patron generico de llamada. Sirve% unicamente con fines de depuracion

-Dds % Devuelve el arbol dds asociado a la funcion de% entrada.

************************************************************************/

/************************************************************************/% CALCULO DEL ARBOL DEFINITORIO./************************************************************************/

arbol(fun(Nombre,ArP,Reglas,_),Patron,Dds):-functor(Patron,Nombre,ArP), % formamos el patron de llamadaposicionesPatron(0,ArP,Vpos), % calculo de posiciones del patrondds(Patron,Reglas,Vpos,Dds). % formamos el arbol.

% devuelve una la lista de posiciones correspondiente a un patron no% instanciado, e.d.,[[1],[2],...,[N]], si el patron tiene N argumentos,% [] si tiene 0 argumentos.posicionesPatron(N,N,[]):-!.posicionesPatron(N,M,[[N1]|R]):-

229

Page 230: Un lenguaje lógico funcional con restricciones1

230 APENDICE G. CONSTRUCCION DEL ARBOL DEFINICIONAL

N1 is N+1,posicionesPatron(N1,M,R).

/************************************************************************/% CALCULO DE POSICIONES DEMANDADAS/************************************************************************/

% se le pasa la lista de reglas asociadas a una funcion, la lista de% posiciones de las variables en el patron y devuelve la lista de pares% regla/listaPC , donde listaPC esta formada por pares Posicion/constructora% donde constructora tiene la estructura Nombre/Aridad% Una posicion es en particular una lista de naturales.% Vpos es la lista de posibles posiciones demandadas (corresponden a las% posiciones de variables en el patron)

demandadas([rule(Cab,Cpo,Restr,_,Lin)|Rreglas],Vpos,[(rule(Cab,Cpo,Restr,_,Lin),Dem)|Rdemandadas]):-

demanda(Cab,Vpos,Dem),!,demandadas(Rreglas,Vpos,Rdemandadas).

demandadas([],_,[]).

% devuelve la lista de pares (posicion,constructora/aridad) que demanda% la regla de cabeza Cab.demanda(Cab,[Pos|Rpos],[(Pos,C)|Dem]):-

consEnPos(Cab,Pos,C), % miramos si hay una constructora!, % C en esa posiciondemanda(Cab,Rpos,Dem).

demanda(Cab,[_|Rpos],Dem):-demanda(Cab,Rpos,Dem).

demanda(_,[],[]).

% mira si en la posicion que se le pasa hay una constructora y la devuelve% en Nombre, junto con la aridad con la que aparece. Hay que tener en cuenta% que una funcion aplicada parcialmente tambien es una constructora.% Un numero tambien se considera constructora

consEnPos(Cab,[P],Nombre/Aridad):-!,

Page 231: Un lenguaje lógico funcional con restricciones1

231

arg(P,Cab,Arg),\+ var(Arg),(

esNumero(Arg,Nombre/Aridad),!;

functor(Arg,Nombre,Aridad),(

cdata(Nombre,_,_,_),!;

fun(Nombre,ArP,_,_),!,Aridad<ArP;

primitive(Nombre,_,_),!,ftype(Nombre,ArP,_,_),Aridad<ArP)

).

consEnPos(Cab,[P|R],Nombre):-arg(P,Cab,Arg),consEnPos(Arg,R,Nombre).

esNumero(N,N/0):-number(N).

/************************************************************************/% DDS/************************************************************************/

% algoritmo propio dds. Predicado ppal

dds(Patron,Reglas,Vpos,Dds):-demandadas(Reglas,Vpos,Dem), % calculamos las pos demandadas en Dem( % una alternativa dependiendo(noHayDem(Dem),!,hazTry(Patron,Reglas,Dds));(uniformDem(Dem,Vpos,Pos),!,hazCase(Patron,Dem,Pos,Dds));(hazOr(Patron,Dem,Dds))).

/*en Dem tenemos una lista de la forma:[(

rule(Cab,Cpo,Rest,_,Linea),[(Posicion,Constructora/Aridad)....]

),....]

*/

Page 232: Un lenguaje lógico funcional con restricciones1

232 APENDICE G. CONSTRUCCION DEL ARBOL DEFINICIONAL

% Recorre la lista de pares Reglas,PosDem y tiene exito si PosDem=[], para% todas las reglas.noHayDem([]).noHayDem([(_,[])|Xs]):-noHayDem(Xs).

% devuelve en Pos la primera posicion unif demandada en caso de existir% falla en otro caso.% hacemos un recorrido por todas las pos de Vpos hasta encontrar una que% este demandada por todas las reglas y fallamos si no la encotramos.

% uniformDem(_,[],_):-!,fail.

uniformDem(Dem,[P|_],P):-perteneceAtodas(P,Dem),!.

uniformDem(Dem,[_|Resto],PosUD):-uniformDem(Dem,Resto,PosUD).

% P es una lista de enteros que representa una posicion,% Dem es una lista de pares regla/listaPC, donde listaPc es a su vez una% lista de pares posicion/constructora

perteneceAtodas(_,[]).perteneceAtodas(Pos,[(_,ListaPc)|Dem]):-

pertenece(Pos,ListaPc),perteneceAtodas(Pos,Dem).

pertenece(Pos,[(P,_)|R]):-(P==Pos,!);(pertenece(Pos,R)).

/***************************************************************************CONSTRUCCION DEL CASE

***************************************************************************/

% tenemos una pos uniformemente demandada Pos y desarrollamos el case% primero asociamos las reglas con la constructora que demandan% QQ es una lista de la forma% [(Constructora/Aridad,[Reglas que la demandan en Pos]),...]

Page 233: Un lenguaje lógico funcional con restricciones1

233

hazCase(Patron,Dem,Pos,Dds):-asocConsReglas(Dem,Pos,[],QQ),construyeCase(Patron,Pos,QQ,Dds).

% asocConsReglas(lista pares regla/listaPc ,Posicion,In,Out),% listaPc esta formada por pares Posicion/constructora% busca el nodo% correspondiente a la posicion Pos en cada regla, y lo inserta en la lista% In de modo que al final en out tengamos una lista de pares% constructora/lista de reglas que tienen esa constructora en la pos Pos.

asocConsReglas([],_,L,L).asocConsReglas([(Regla,ListaPc)|Rdem],PosUD,Asoc,Rasoc):-

buscaConsDem(ListaPc,PosUD,Cons), % buscamos la cons=Nom/Ar asocinsertaRegla(Regla,Cons,Asoc,Asoc1), % la insertamos al final por

% mantener el orden en la tradasocConsReglas(Rdem,PosUD,Asoc1,Rasoc).

buscaConsDem([(Pos,Cons)|_],PosUD,Cons):-Pos==PosUD,!.buscaConsDem([_|Resto],PosUD,Cons):-buscaConsDem(Resto,PosUD,Cons).

% busca en la lista de reglas la constructora C. Si la encuentra, a~nade la% nueva regla a la lista asociada a C, y si no, crea un nuevo par al final.

insertaRegla(Regla,Cons,[],[(Cons,[Regla])]). % nuevo par al finalinsertaRegla(Regla,Cons,[(Cons,[L1|R1])|R],[(Cons,[L1|R11])|R]):-

!,insertaFinal(Regla,R1,R11). % mantenemos el orden de las reglas

% por conservar el orden en la traducc

insertaRegla(Regla,Cons,[L|R],[L|R1]):-insertaRegla(Regla,Cons,R,R1).

% inserta un elemento al final de una lista.insertaFinal(Regla,[],[Regla]).insertaFinal(Regla,[L|R],[L|R1]):-insertaFinal(Regla,R,R1).

% construye la estructura case(Pos,Patron,ListaCasos), donde lista de casos% esta formada por pares Constructora/lista de reglas

Page 234: Un lenguaje lógico funcional con restricciones1

234 APENDICE G. CONSTRUCCION DEL ARBOL DEFINICIONAL

construyeCase(Patron,Pos,Asociaciones,case(Pos,Patron,ListaCasos)):-hazListaCasos(Patron,Pos,Asociaciones,ListaCasos).

% devuelve la lista de ddt asociados a un case en forma de pares% (constructora, ddt asociado).hazListaCasos(_,_,[],[]).hazListaCasos(Patron,Pos,[(C/Ar,Reglas)|RestoAsoc],[(C,Caso)|RestoCasos]):-

nuevoPatron(Patron,Pos,C/Ar,NP),% las posiciones son deducibles. Esto es bastante mejorable

posVarsPatron(NP,Vpos),dds(NP,Reglas,Vpos,Caso),hazListaCasos(Patron,Pos,RestoAsoc,RestoCasos).

% genera un nuevo Patron a partir de uno dado, cambiando la variable de la% posicion Pos por la constructora Cons.

nuevoPatron(Patron,[],C/Ar,NArg):-var(Patron),functor(NArg,C,Ar).

nuevoPatron(Patron,[Pos|R],Cons,NP):-arg(Pos,Patron,Arg),nuevoPatron(Arg,R,Cons,Narg),cambiaArg(Patron,Pos,Narg,NP).

% cambiaArg es como el argrep de Arity

cambiaArg(Patron,Pos,Narg,NP):-Patron=..[Nom|Args],sustituyeArg(Pos,Args,Narg,Nargs),NP=..[Nom|Nargs].

sustituyeArg(1,[_|R],Narg,[Narg|R]).sustituyeArg(N,[Ar|R],Narg,[Ar|R1]):-N1 is N-1,sustituyeArg(N1,R,Narg,R1).

% devuelve la lista de posiciones de las variables de Patron. Una posicion% es una lista de naturales. devuelve [[]] si Patron es variable y [] si% el patron no tiene variables

Page 235: Un lenguaje lógico funcional con restricciones1

235

posVarsPatron(P,[[]]):-var(P),!.posVarsPatron(P,Vpos):-

P=..[_|Args],(Args==[],!,Vpos=[];listaPosVarsPatron(Args,1,Vpos)).

listaPosVarsPatron([],_,[]).listaPosVarsPatron([Ar|R],Nar,VposT):-

posVarsPatron(Ar,Vpos),encab(Nar,Vpos,VposAr),N1 is Nar+1,listaPosVarsPatron(R,N1,R1),concat(VposAr,R1,VposT).

encab(_,[],[]).encab(N,[L|R],[NL|NR]):-encabLst(N,L,NL),encab(N,R,NR).encabLst(N,[],[N]).encabLst(N,[E|R],[N,E|R]).

concat([],L,L).concat([X|R],L,[X|Ls]):-concat(R,L,Ls).

/***************************************************************************CONSTRUCCION DEL OR

***************************************************************************/

% construye un termino de la forma or(lista opciones).% le pasamos el patron, Vpos y la lista de pares regla/pos demandadas por ella

hazOr(Patron,Dem,or(LAlts)):-

% hacemos una particion del cto de reglas por la posicion que demandan% primero extraemos todas las que o no demandan ninguna posicion o su% cabeza no casa con el patron. Todas ellas las metemos en Reduce.% En Dem1 queda el resto de reglas

sacaReduce(Patron,Dem,Dem1,Reduce),

Page 236: Un lenguaje lógico funcional con restricciones1

236 APENDICE G. CONSTRUCCION DEL ARBOL DEFINICIONAL

% Con el resto de Reglas hacemos una particion por las posiciones que% demandan. En Particion tenemos una lista de pares (Pos,reglas que la% demandan). Realmente aparecen las reglas cuya primera posicion% demandada es Pos; esto es equivalente por el orden que tenemos a% tomar primero todas las que demandan la primera, luego todas las que% demandan la segunda...(siempre dentro de las posibles demandadas) y es% mas eficiente.

hazParticion(Patron,Dem1,Particion),

% y ahora... generamos los arboles correspondientes a cada alternativa% y a Reduce.

hazListaAlternativas(Patron,Particion,Reduce,LAlts).

% saca Reduce(Patron,In,Out1,Out2). En In le pasamos todas las reglas con las% posiciones que demandan. En out2 devuelve las que no demandan nada o las que% tienen una cabeza que no casa con el patron. En Out1 devolvemos las restantes

sacaReduce(_,[],[],[]).sacaReduce(Patron,[(Rule,LstPosDem)|R],[(Rule,LstPosDem)|R1],Reduce):-

casa(Rule,Patron),LstPosDem\==[],!,sacaReduce(Patron,R,R1,Reduce).

sacaReduce(Patron,[(Rule,_)|R],R1,[Rule|Reduce]):-sacaReduce(Patron,R,R1,Reduce).

casa(rule(Cab,_,_,_,_),Patron):- \+ (\+ Cab=Patron).

/*% comprueba que son unificables pero no les unifica.casa(rule(Cab,_,_,_,_),Patron):- \+ nocasa(Cab,Patron).

%&& guarreria con corte.nocasa(T1,T2):-T1=T2,!,fail.nocasa(_,_).*/

% hazParticion(Patron,In,Out). Le pasamos en In1 todas las reglas que% demandan alguna posicion y cuya cabeza casa con el patron.En Out devolvemos% la lista de pares Posicion/reglas que la demandan en primer lugar.

hazParticion(_,[],[]).

Page 237: Un lenguaje lógico funcional con restricciones1

237

hazParticion(Patron,[(Rule,[(Pos,Cons)|Rpos])|R],[(Pos,[(Rule,[(Pos,Cons)|Rpos])|RDem])|Part]):-

extraeReglasPos(Pos,R,Resto,RDem),hazParticion(Patron,Resto,Part).

% extraeReglasPos(Pos,Demanda,Resto,Out). Recorre la lista de Demanda y devuelve% en Out las que demandan Pos como primera poscion. Deja en Resto las restantes

extraeReglasPos(_,[],[],[]).extraeReglasPos(Pos,[(Rule,[(Pos,Cons)|Rpos])|R],R1,

[(Rule,[(Pos,Cons)|Rpos])|RDemPos]):-!,extraeReglasPos(Pos,R,R1,RDemPos).

extraeReglasPos(Pos,[Par|R],[Par|R1],DemPos):-extraeReglasPos(Pos,R,R1,DemPos).

% hace los casos que tiene en particion y luego hace el try que tiene en Reduce

hazListaAlternativas(_,[],[],[]).

hazListaAlternativas(Patron,[],Reduce,[Dds]):-hazTry(Patron,Reduce,Dds).

hazListaAlternativas(Patron,[(Pos,Reglas)|R],Reduce,[DdsP|DdsR]):-hazCase(Patron,Reglas,Pos,DdsP),hazListaAlternativas(Patron,R,Reduce,DdsR).

/************************************************************************/% CONSTRUCCION DEL TRY/************************************************************************/

hazTry(Patron,Reglas,try(Patron,L)):-hazListaTry(Patron,Reglas,L).

Page 238: Un lenguaje lógico funcional con restricciones1

238 APENDICE G. CONSTRUCCION DEL ARBOL DEFINICIONAL

hazListaTry(_,[],[]).hazListaTry(Patron,[rule(Cab,Cpo,Rest,_,_)|Rr],[si(Rest,Cpo)|Rrsi]):-

Cab=Patron,hazListaTry(Patron,Rr,Rrsi).

/************************************************************************/% PREDICADOS PARA DEPURACION/************************************************************************/

%% DIBUJITO DEL ARBOL

% Saca en pantalla una representacion "legible" (eso creo) del arbol.% Hay que pasarle una llamada generica o patron inicial de la funcion y% el propio arbol.

pinta(Patron,Dds):-nl,write(’Patron: ’),write(Patron),nl,nl,esc(Dds,0).

esc(case(Pos,P,L),N):-!,tab(N),write(’** case ’),write(Pos),write(’ Patron ’),write(P),nl,N1 is N+5,escListaCase(L,N1).

esc(or(L),N):-!,tab(N),write(’** or ’),nl,N1 is N+2,escListaOr(L,N1).

esc(try(Patron,L),N):-tab(N),write(’Patron: ’),write(Patron),nl,N1 is N+2,escListaTry(L,N1),nl.

escListaCase([],_):-!.escListaCase([(C,L)|R],N):-!,

tab(N),write(C),write(’: ’),nl,N1 is N+3,esc(L,N1),escListaCase(R,N).

escListaOr([],_):-!.escListaOr([L|R],N):-

Page 239: Un lenguaje lógico funcional con restricciones1

239

tab(N),esc(L,N),escListaOr(R,N).

escListaTry([],_):-!.escListaTry([si(Con,Cpo)|R],N):-!,

tab(N),write(Cpo),write(’ <= ’),write(Con),nl,escListaTry(R,N).

Page 240: Un lenguaje lógico funcional con restricciones1

240 APENDICE G. CONSTRUCCION DEL ARBOL DEFINICIONAL

Page 241: Un lenguaje lógico funcional con restricciones1

Apendice H

Generacion de codigo

/************************************************************************LLAMADA AL MODULO:

generaCodigoFinal(+Tree % arbol generado con el modulo dds.pl+NomFun % nombre de la funcion asociada al arbol-CodF % Codigo asociado a la funcion en forma de lista

de pares (Cabeza,cuerpo), donde cuerpo es una listade atomos

*************************************************************************/

/************************************************************************/% GENERACION DE CODIGO./************************************************************************Este modulo funciona con el arbol definitorio como entrada. Genera el codigoasociado a la funcion que describe el arbol dds y funciona analizando laestructura del del arbol en profundidad, por lo que la salida que produceesta solo parcialmente ordenada, en un principio, e.d., todo predicado (func)que es llamado por otro aparece despues de este, pero dos predicados del mismonombre no tienen pq aparecer contiguos. Esto puede provocar problemas enalgunos prolog por lo que al final les ordenaremos. No obstante,aprovecharemos la propiedad antes citada para hacer el orden completo.Para "subir las constructoras" y generar un codigo mas eficiente muchos delos predicados llevan como ultimo argumento la "cascara". Una cascaraasociada a un nodo puede tomar tres tipos de valor:

- un termino formado con una constructora. Significa que todos losnodos por debajo tienen una cascara comun que es la actual

- on: hasta ahora no hay restricciones sobre la cascara, por lo queal formar una nueva cascara con otra dada, automaticamente tomara el valorde la dada.

- off: no hay cascara comun para los nodos que estan por debajo y porlo tanto no es posible hacer esta optimizacion.

241

Page 242: Un lenguaje lógico funcional con restricciones1

242 APENDICE H. GENERACION DE CODIGO

Tambien se hace un unfolding de los predicados, e.d., si un predicadop llama a otro q y q solo tine una clausula, q desaparece y la llamada en pse sutituye por el cuerpo de q.

14-3-93Ademas se realizan otras dos optimizaciones:

- ** QUITADA (20-6-97) ** Indexacion por el argumento del que acabamosde hallar una forma normal de cabeza. P.e.

f(X,Y,H):-hnf(Y,HY),f_2(HY,X,H).el orden de los argumentos se altera en la llamada a f_2 con el fin de

aprovechar la indexacion de Sicstus (por el functor del primer argumento).- Se quitan las constructoras inutiles. P.e:

f(suc(X),Y,H):-hnf(Y,HY),f_2(HY,X,H).la constructora suc no se propaga a f_2, porque ya no aporta informacion

en f_2 y nos ahorramos el trabajo de la unificacion.

20-6-96Cuando una variable se unificaba al unificar la llamada a un predicado con lacabeza de este, no se comprobaba que dicha unificacion era posible de acuerdocon el almacen de restricciones. P.e.:

f(X,H,Cin,Cout):-hnf(X,HX,Cin,Cout1),f_1(HX,H,Cout1,Cout).f_1(a,H,Cin,Cout):- ....

La forma normal de cabeza de X puede ser una variable y se unifica con laconstructora a sin comprobar que esto es posible. Para corregirlo se genera el codigo:

f(X,H,Cin,Cout):-hnf(X,HX,Cin,Cout1),f_1(HX,H,Cout1,Cout).f_1(X,H,Cin,Cout):-unifyHnfs(X,a,Cin,Cout1),....

El codigo de unifyHnfs esta en toycomm.pl

Con esto desaparece la indexacion.

Otra cosilla: muchos predicados (los que contienen ramas or como el or paralelo)pueden generar codigo de la forma:

or....or(A,B,H,Cin,Cout):-hnf(B,HB,Cin,Cout1),unifyHnfs(HB,true,Cout1,Cout)....

Esto ocurre al hacer unfolding y este codigo es reemplazable (y se reemplaza) por:or....or(A,B,H,Cin,Cout):-hnf(B,true,Cin,Cout).

Esto se detecta al hacer los unfolding en las ramas case

Page 243: Un lenguaje lógico funcional con restricciones1

243

************************************************************************/

% Devuelve el codigo asociado la funcion NomFun con arbol Tree en CodFungeneraCodigoFinal(Tree,NomFun,CodF):-

generaCodigo(Tree,NomFun,[],Cod,_),ordenaCodigo(Cod,CodF).

/* generaCodigo(+Tree,+NomFun,+UltPosDem,-Cod,+-Cascara).- Tree es un arbol generado por dds.- NomFun es el nombre de la funcion para la que estamos generando codigo- UltPosDem es la ultima posicion que ha sido demandada. Inicialmente es

[] y (*** YA NO SIRVE PARA ESTO 20-6-96 *** sirve para hacer la optimizacion deindexar por el argumento del que acabamos de hallar una hnf

- Cod es la lista de clausulas generadas.- Cascara es la cascara explicada arriba para optimizar */

/******************************************************************************/% RAMAS OR/******************************************************************************/

% En el caso del or simplemente generamos codigo para cada una de las opciones% La primera clausula nunca unificara inicialmente con un arbol pero puede% hacerlo en sucesivas llamadas recursivas.

generaCodigo(or([]),_,_,[],on):-!.generaCodigo(or([Tree|R]),NomFun,UltPosDem,Cod,C):-

!,generaCodigo(Tree,NomFun,UltPosDem,Cod1,C1),generaCodigo(or(R),NomFun,UltPosDem,Cod2,C2),hazCascara(C1,C2,C),concat(Cod1,Cod2,Cod).

/******************************************************************************/% RAMAS CASE/******************************************************************************/

Page 244: Un lenguaje lógico funcional con restricciones1

244 APENDICE H. GENERACION DE CODIGO

/*generaCodigo(case(...),NomFun,UltPosDem,OutCodigo,OutCascara)Aqui generamos codigo para un arbol (o subarbol) de la forma case(...). En Nomfunllevamos el nombre del predicado que vamos a generar. Los sucesivos predicadostendran el nombre NomFun_Pos_Cons, donde Pos es la posicion demandada que apareceen el case y Cons es la constructora inutil que hemos quitado (si es que se hahecho). En OutCodigo devolvemos el codigo generado. En este predicado se generadirectamente solo una clausula, las demas se generan como resultado de la llamadaa generacodigoCasos. Esta clausula en ppio. tiene la forma Cab:-hnf(...),LLam.E.d. hacemos la fnc de la pos demandada y llamamos al predicadode Cabeza NomFun_Pos (Llam).

Despues estudiamos la posibilidad de hacer un unfolding, que sera factiblecuando tengamos un case con un solo caso y ademas este caso sea:

- otro case, o- un try con una sola alternativa.

En esta situacion en lugar de la llamada Llam colocamos el cuerpo de la unicaclausula cuya cabeza encaja con Llam. Ademas unificamos Llam=CabCaso, simplementepara unificar las variables que aparecen en ambos y hacer coherente el unfolding.

Si podemos hacer el unfolding tenemos en cuenta que el cuerpo de la clausulapuede comenzar por un unifyHnfs, en cuyo caso nos cargamos este unifyHnfs (ya haceel trabajo hnf) y hacemos coherentes los almacenes y resto de variables.

Si no tenemos la suerte de poder hacer el unfolding, respetamos las clausulasgeneradas en generaCodigoCasos y las dejamos tal cual.*/

generaCodigo(case(Pos,Patron,ListaCasos),NomFun,UltPosDem,[(Cab,Cpo)|RestoCod],C):-

!, % por si las moscasPatron=..[_Nom|Args], % destripamos el patron para sacar los

% argumentos

%construimos la cabeza de la clausula.construyeCabeza(NomFun,Args,H,UltPosDem,Cons,Var,Cin,Cout,Cab),

% el nombre del predicado de la llamadahazNombreFun(NomFun,Pos,Cons,NomFunLla),

% la llamada en siconstruyeLlamada(NomFunLla,Args,H,Pos,VarPos,HnfPos,Cout2,Cout,Llam),

% vemos si tenemos que hacer alguna unificacion de variable con

Page 245: Un lenguaje lógico funcional con restricciones1

245

% constructora, en cuyo caso metemos un unifyHnfs. Luego se hace la% forma normal de cabeza del argumento demandado y luego... el% RestoCuerpo(

var(Var),!,Cpo=[unifyHnfs(Var,Cons,Cin,Cout1),

hnf(VarPos,HnfPos,Cout1,Cout2)|RestoCpo];

Cpo=[hnf(VarPos,HnfPos,Cin,Cout2)|RestoCpo]),

% generamos el codigo para los subarboles del casegeneraCodigoCasos(ListaCasos,NomFunLla,Pos,[(CabCaso,CpoCaso)|Rcod],C),(

% aqui vemos si es posible hacer el unfolding(ListaCasos=[(_,case(_,_,_))];ListaCasos=[(_,try(_,[_]))]),!,(

% Si al hacer el unfolding nos va a quedar y unifyHnfs% y luego un hnf, dejamos solo el hnf, que ya hace todo% el trabajoCpoCaso=[unifyHnfs(_,L,_,CoutUnif)|RestoCpoCaso],!,

% unificacion de variables para hacer coherente la% eliminacion de unifyHnfsHnfPos=L,Cout2=CoutUnif,RestoCpo=RestoCpoCaso

;RestoCpo=CpoCaso % en vez de la llamada ponemos

% el cuerpo de la clausula a la% que llama

),

RestoCod=Rcod,Llam=CabCaso % para unificar variables

;% no es posible el unfolding: somos respetuososRestoCpo=[Llam],RestoCod=[(CabCaso,CpoCaso)|Rcod]

),

% si por debajo de este nodo tenemos una cascara comun, la ponemos en% vez de H, si no, nos aguantamos sin subir la constructora

Page 246: Un lenguaje lógico funcional con restricciones1

246 APENDICE H. GENERACION DE CODIGO

((C\==off,H=C)

;true

).

/* construyeCabeza: la cabeza de una clausula viene tiene como nombre el nombreque llevamos construido hasta ahora (con el inicial, las posiciones que se hanido demandando y las constructoras que se han ido quitando. Reemplazamos laconstructora Cons que se demanda en el patron por una nueva variable Var, queluego en el cuerpo de la clausula se unificaran mediante un unifyHnfs .reemplazaArgumentoLista extrae el termino (una constructora) que ocupa unaposcion dada y genera una nueva lista de argumetos en la que se ha sustituidodicho termino por una nueva variable Var. Ademas en la cabeza aparece comoultimo argumento H, que es el resultado de la evaluacion de la funcion.construyeCabeza devuelve ademas Cons que es el functor del termino por el quese indexa y que servira para concatenar su functor al predicado de llamada yllevar cuenta asi de las constructoras que se han ido quitando y no hayaambiguedades en el nombre de los predicados */

% esta primera clausula solo se usa cuando el arbol correspondiente a una% funcion comienza por case y solo se usa una vez

construyeCabeza(NomFun,Args,H,[],[],[],Cin,Cout,Cab):-concat(Args,[H,Cin,Cout],Aux),Cab=..[NomFun|Aux].

construyeCabeza(NomFun,Args,H,UltPosDem,Cons,Var,Cin,Cout,Cab):-reemplazaArgumentoLista(Args,UltPosDem,Var,Cons,Args1),variablesTermino(Args1,[]/LstVars),concat(LstVars,[H,Cin,Cout],Aux),Cab=..[NomFun|Aux].

/* construyeLlamada es parecido a construyeCabeza, salvo que ahora la posicionque interesa es aquella de la se acaba de hallar una hnf. */construyeLlamada(NomFunLla,Args,H,Pos,VarPos,HnfPos,Cin,Cout,Llam):-

reemplazaArgumentoLista(Args,Pos,HnfPos,VarPos,Args1),variablesTermino(Args1,[]/LstVars),concat(LstVars,[H,Cin,Cout],Aux),Llam=..[NomFunLla|Aux].

% Hacemos un recorrido por la lista de casos y devolvemos en una lista el% codigo que genera cada uno de ellos

Page 247: Un lenguaje lógico funcional con restricciones1

247

generaCodigoCasos([],_,_,[],on).generaCodigoCasos([(_,Tree)|R],NomFun,PosAnt,Cod,C):-

generaCodigo(Tree,NomFun,PosAnt,CodT,C1),generaCodigoCasos(R,NomFun,PosAnt,Rcod,C2),concat(CodT,Rcod,Cod),hazCascara(C1,C2,C).

% reemplazaArgumentoLista(Lst,Pos,ArgNew,Arg,LstNew)% Reemplaza en una lista de terminos, la posicion Pos, que contiene una% termino Arg por un nuevo ArgNew. La nueva lista se devuelve en LstNew.

reemplazaArgumentoLista([Vant|Rar],[1],V,Vant,[V|Rar]):-!.

reemplazaArgumentoLista([Ar|Rar],[1|Rpos],V,Vant,[Nar|Rar]):-!,Ar=..[Nom|Args],reemplazaArgumentoLista(Args,Rpos,V,Vant,Args1),Nar=..[Nom|Args1].

reemplazaArgumentoLista([Ar|Rar],[N|Rpos],V,Vant,[Ar|Rar1]):-N1 is N-1,reemplazaArgumentoLista(Rar,[N1|Rpos],V,Vant,Rar1).

% saca la lista de variables distintas de un termino ordenadas por orden de% aparicion en dicho termino

variablesTermino(T,Vin/Vout):-var(T),!,insertaVar(T,Vin/Vout).

variablesTermino(T,Vin/Vout):-T=..[_|Args],variablesLista(Args,Vin/Vout).

variablesLista([],Vin/Vin).variablesLista([C|R],Vin/Vout):-

variablesTermino(C,Vin/Vout1),variablesLista(R,Vout1/Vout).

insertaVar(V,[]/[V]).insertaVar(V,[C|R]/[C|R]):-

V==C,!.insertaVar(V,[C|R]/[C|R1]):-

Page 248: Un lenguaje lógico funcional con restricciones1

248 APENDICE H. GENERACION DE CODIGO

insertaVar(V,R/R1).

/******************************************************************************/% RAMAS TRY/******************************************************************************/

% El codigo asociado al try es la union de los codigos asociados a sus% alternativas.

generaCodigo(try(Patron,Alts),NomFun,UltPosDem,Cod,C):-generaCodigoAlts(Patron,Alts,NomFun,UltPosDem,Cod,C).

generaCodigoAlts(_,[],_,_,[],on).

generaCodigoAlts(Patron,[si(Restr,Val)|R],NomFun,UltPosDem,[(Cab,Cpo)|Rcod],C):-Patron=..[_|Args],

% constrimos la cabeza de la clausula igual que en el case

construyeCabeza(NomFun,Args,H,UltPosDem,Cons,Var,Cin,Cout,Cab),

% construimos el codigo asociado a las restriccioneshazRestr(Restr,ListaEqs,Cin1,Cout1,compilacion),(

% si lo que devuelve la funcion es una variable, hacemos su hnfvar(Val),insertaFinal(hnf(Val,H,Cout1,Cout),ListaEqs,Cpo1)

;% si no puede ser una constructora, que meteremos en H o una lla% mada a una funcion que ira al finalVal=..[Nombre|As],% en cualquier caso suspendemos las posibles llamadas a funcio% nes que aparezcan por dentro.meteSuspensionesLista(As,ValSusp,compilacion),(

% si es una constructora le metemos suspensiones y la% colocamos como ultimo argumento del predicado

esConstructor(Val,compilacion),H=..[Nombre|ValSusp],Cpo1=ListaEqs,

Page 249: Un lenguaje lógico funcional con restricciones1

249

Cout=Cout1;

% si es una funcion colocamos la llamada al final del% predicadoname(Nombre,NombreN),%concat(NombreN,Cons,NombreL),name(NombreFun,[36|NombreN]), % 36 es el ascii de $concat(ValSusp,[H,Cout1,Cout],Aux),Llam=..[NombreFun|Aux],insertaFinal(Llam,ListaEqs,Cpo1)

)),

% si se necesita se mete por delante un unifyHnfs igual que en el case

(var(Var),!,Cpo=[unifyHnfs(Var,Cons,Cin,Cin1)|Cpo1];Cpo=Cpo1,Cin1=Cin),

generaCodigoAlts(Patron,R,NomFun,UltPosDem,Rcod,C1),hazCascara(Val,C1,C).

% el ultimo argumento nos indica si estamos en tiempo de ejecucion o de compi% lacion, puesto que los cdata de comp son const en ej y los ftype son funct

meteSuspensiones(Term,Term,_):-var(Term),!.meteSuspensiones(Term,TermSusp,Tiempo):-

Term=..[Nom|Args],meteSuspensionesLista(Args,ArgsSusp,Tiempo),(

esConstructor(Term,Tiempo), % miramos si es Constructora!,TermSusp=..[Nom|ArgsSusp]

;name(Nom,L),name(NomF,[36|L]),TermSusp=..[’$$susp’,NomF,ArgsSusp,_,_]

).

meteSuspensionesLista([],[],_):-!.meteSuspensionesLista([L|R],[L1|R1],Tiempo):-

meteSuspensiones(L,L1,Tiempo),meteSuspensionesLista(R,R1,Tiempo).

% es constructora si es una constructora o si es una funcion aplicada

Page 250: Un lenguaje lógico funcional con restricciones1

250 APENDICE H. GENERACION DE CODIGO

% parcialmente. El parametro Tiempo indica si la llamda se realiza en tiempo% de compilacion o de ejecucion. En compilacion tenemos cdata(...) y fun(...)% y en ejecucion tenemos cons y funct. En este modulo se usa en tiempo de% compilacion pero cuando se lanzan objetivos, las suspensiones se meten en% tiempo de ejecucion.

esConstructor(Term,_):-number(Term).

esConstructor(Term,Tiempo):-functor(Term,Nom,Ar),(

(Nom==’$eqFun’;Nom==’$notEqFun’),Ar<2;

Tiempo==compilacion,(

cdata(Nom,_,_,_),!;fun(Nom,ArP,_,_),!,Ar<ArP;primitive(Nom,_,_),!,ftype(Nom,ArP,_,_),Ar<ArP

);

Tiempo==ejecucion,(const(Nom,_,_,_),!;funct(Nom,ArP,_,_,_),!,Ar<ArP)

).

% Por el momento las unicas restricciones permitidas son las de igualdad y% desigualdad, que se traducen en el predicado equal y notEqual resp.% Se puede optimizar mas aun el codigo. El caso de que uno de los argumentos% sea true se puede generalizar a que sea una constructora cualquiera y se% puede hacer un hnf orientado

hazRestr([],[],Cin,Cin,_):-!.hazRestr([T1==T2|R],[Restr|Rr],Cin,Cout,Tiempo):-

!,meteSuspensiones(T1,T1Susp,Tiempo),meteSuspensiones(T2,T2Susp,Tiempo),(

(T1Susp==true;T1Susp==false),

Page 251: Un lenguaje lógico funcional con restricciones1

251

nonvar(T2Susp),T2Susp=..[’$$susp’,NomFun,Args,_,_],!,concat(Args,[T1Susp,Cin,Cout1],ArgsFun),Restr=..[NomFun|ArgsFun]

;(T2Susp==true;T2Susp==false),nonvar(T1Susp),T1Susp=..[’$$susp’,NomFun,Args,_,_],!,concat(Args,[T2Susp,Cin,Cout1],ArgsFun),Restr=..[NomFun|ArgsFun]

;Restr=equal(T1Susp,T2Susp,Cin,Cout1)

),hazRestr(R,Rr,Cout1,Cout,Tiempo).

hazRestr([’/=’(T1,T2)|R],[notEqual(T1Susp,T2Susp,Cin,Cout1)|Rr],Cin,Cout,Tiempo):-

!,meteSuspensiones(T1,T1Susp,Tiempo),meteSuspensiones(T2,T2Susp,Tiempo),hazRestr(R,Rr,Cout1,Cout,Tiempo).

% CONSTRUCCION DE LAS CASCARAS% estos predicados sacan la cascara comun a dos terminos, e.d., estudia las% constructoras comunes de los dos terms. El flag on indica que el termino% admite cualquier cascara (depende del otro termino). Aparece como fin de% algunas llamadas recursivas. El flag off indica que en las ramas que hay% por debajo no ha sido posible construir cascara comun y por lo tanto ahora% tampoco

hazCascara(C1,C2,off):-(var(C1);var(C2)),!.hazCascara(off,_,off):-!.hazCascara(_,off,off):-!.hazCascara(C1,on,C):-

!,(esConstructor(C1,compilacion),C=C1;C=off).

hazCascara(on,C1,C):-!,(esConstructor(C1,compilacion),C=C1;

Page 252: Un lenguaje lógico funcional con restricciones1

252 APENDICE H. GENERACION DE CODIGO

C=off).hazCascara(C1,C2,C):-

(esConstructor(C1,compilacion),esConstructor(C2,compilacion),C1=..[Nom|Args1],C2=..[Nom|Args2],listaCascaras(Args1,Args2,Lc),!,C=..[Nom|Lc]

;C=off

).

listaCascaras([],[],[]):-!.listaCascaras([L1|R1],[L2|R2],[Lc|Rc]):-

cascara(L1,L2,Lc),listaCascaras(R1,R2,Rc).

% es igual que hazCascara, pero si hazCascara devuelve off (no tienen cascara% comun) aqui devolvemos una nueva variable, pq en este punto ya sabemos que% tienen cascara comun, y si internamente no la tienen hay que meter una var

cascara(C1,C2,C):-hazCascara(C1,C2,Cas),(

Cas==off,C=_

;C=Cas

).

% Concatena NomFun con la lista de numeros de pos separados por .’s y precedida% por _ Por ejemplo hazNombre(hola_1.2,[3,4,5],Sal) nos devuelve en% Sal hola_1.2_3.4.5hazNombreFun(NomFun,Pos,Cons,NomFun1):-

unePos(Pos,Res),name(NomFun,NomFunN),% 95 es el ascii de _

Page 253: Un lenguaje lógico funcional con restricciones1

253

(Cons==[],!,Aux=[95|Res]

;functor(Cons,F,_),name(F,ConsN),concat([95|Res],[95|ConsN],Aux)

),concat(NomFunN,Aux,Aux1),name(NomFun1,Aux1).

% Transforma una lista en la cadena formada por sus eltos separados por .’s%&&unePos([P],S):-!,name(P,S).unePos([P|R],Sal):-

name(P,S),unePos(R,Resto),concat(S,".",R1),concat(R1,Resto,Sal).

insertaLst(Var,[],[Var]).insertaLst(Var,[V|R],[V|R1]):-

(V==Var,!,R1=R;insertaLst(Var,R,R1)).

/************************************************************************/% ORDENACION DEL CODIGO.

ordenaCodigo(Cod,CodOr):-ordenaCodigoPares(Cod,[],CodPares),aplanaCodigo(CodPares,CodOr).

ordenaCodigoPares([],Ac,Ac).ordenaCodigoPares([(Cab,Cpo)|R],Ac,CodOr):-

functor(Cab,Nombre,_),insertaPred((Cab,Cpo),Nombre,Ac,Ac1),ordenaCodigoPares(R,Ac1,CodOr).

insertaPred((Cab,Cpo),Nombre,[],[(Nombre,[(Cab,Cpo)])]).insertaPred((Cab,Cpo),Nombre,[(Nombre,L)|R],[(Nombre,L1)|R]):-

!,insertaFinal((Cab,Cpo),L,L1).

Page 254: Un lenguaje lógico funcional con restricciones1

254 APENDICE H. GENERACION DE CODIGO

insertaPred(Par,Nombre,[Par2|R],[Par2|R1]):-insertaPred(Par,Nombre,R,R1).

aplanaCodigo([],[]).aplanaCodigo([(_,L)|R],Res):-aplanaCodigo(R,R1),concat(L,R1,Res).

/************************************************************************/

/************************************************************************/% PREDICADOS PARA DEPURACION/************************************************************************/

% SALIDA DEL CODIGO A PANTALLA DE FORMA + O - LEGIBLE

sacaCodFuns([]):-!.sacaCodFuns([CodFun|R]):-sacaCodFunN(CodFun),sacaCodFuns(R).

sacaCodFunN([(Cab,Cpo)|R]):-functor(Cab,Nom,_),write(’% CODIGO PARA LA FUNCION ’),write(Nom),nl,sacaCod([(Cab,Cpo)|R]).

sacaCod([]):-!.sacaCod([(Cab,Cpo)|R]):-!,write(Cab),write(’ :- ’),sacaCpo(Cpo),sacaCod(R).sacaCpo([]):-!,write(’.’),nl.sacaCpo([C|R]):-!,write(C),(R\==[],write(’, ’);true),sacaCpo(R).

Page 255: Un lenguaje lógico funcional con restricciones1

Apendice I

Salida de respuestas

El siguiente codigo es un extracto del archivo goals.pl.

/*****************************************************************************SALIDA DE SOLUCIONES

*****************************************************************************/

/* La salida de la solucion se hace asi (se intenta minimizar, esto esto es nointroducir nuevas variables que no sean estrictamente necesarias para dar larespuesta): primero mostramos todas las igualdades entre variables delobjetivo, respetando los nombres que les dio el usuario. Luego sacamos enpantalla las igualdes entre las vars del objetivo los terminos construidos alos que se han ligado. Notese que una variable no puede ligarsesimultaneamente a otra variable y a un termino construido, por lo que los doscasos anteriores son disjuntos.Si una variable no se ha ligado durante la ejecucion del objetivo a otravariable del objetivo ni a un termino construido de momento no hemos sacadoninguna informacion sobre ella.Por ultimo sacamos las restricciones de desigualdad y las restricciones sobrelos reales (si estamos trabajando con el resolutor). Para ello primeroextraemos las variables relevantes: las que aparecen en el objetivo mas lasque aparecen en restricciones no lineales sobre los reales. */

% En vars esta la lista de variables del objetivo con los nombres de usuario y% en Residuo esta todo el residuo que ha quedado tras la computacion:% desigualdades entre variables y restricciones sobre los reales.

sacaRespuesta(Vars,Cout,Residuo):-

current_output(Handle), % necesitamos el handle de la salida% donde queremos escribir porque escribe% atomo funciona asi.

nl,tab(6),write(’yes’),

255

Page 256: Un lenguaje lógico funcional con restricciones1

256 APENDICE I. SALIDA DE RESPUESTAS

% igualdades entre vars del objetivo. En L llevamos cuenta de las vars% aparecidas hasta el momento con sus nombres para posteriores% aparicionessacaVars(Vars,[]/Terms,[]/L),%nl,% igualdades entre vars y terminossacaTerms(Handle,Terms,L/L1),% residuosacaRestricciones(Handle,Vars,Cout,Residuo,L1/_).

% SALIDA DE IGUALDADES ENTRE VARIABLES

% sacaVars([(Nombre,VarProlog).....],[(Nombre,TermConstruido)],Lin/Lout).% Inicialmente Lin es []. Cuando aparece una variable simplemente la metemos% junto con su nombre en Lout. Cuando aparece otra variable (con otro nombre)% pero que es la misma variable prolog, sacamos una igualdad entre los nombres% Ademas vamos construyendo una lista de variables ligadas a terminos para% sacarlas luego.

sacaVars([],Terms/Terms,L/L).sacaVars([(Nom,Val)|R],Terms/Terms1,L/L2):-

var(Val),!,(yaEsta(Val,L,Nom1),!,L1=L,sacaIgualdadVars(Nom1,Nom);L1=[(Val,Nom)|L]),sacaVars(R,Terms/Terms1,L1/L2).

sacaVars([T|R],Terms/[T|Terms1],L/L1):-sacaVars(R,Terms/Terms1,L/L1).

yaEsta(X,[(Y,Nom)|R],Nom1):-(X==Y,!,Nom1=Nom;yaEsta(X,R,Nom1)).

sacaIgualdadVars(X,Y):-nl,tab(6),write(X),write(’ == ’),write(Y).

% SALIDA DE IGUALDADES DE VARIABLES Y TERMINOS

% Salida de variables ligadas a terminossacaTerms(_,[],L/L).

Page 257: Un lenguaje lógico funcional con restricciones1

257

sacaTerms(Handle,[(Nom,Val)|R],L/L2):-nl,tab(6),write(Nom),write(’ == ’),escribeAtomo(Handle,L/L1,Val),sacaTerms(Handle,R,L1/L2).

% SALIDA DE RESTRICCIONES

% Las restricciones se sacan: primero las desigualdades entre variables, luego% si estamos trabajando con el resolutor sacamos las restricciones sobre reales

sacaRestricciones(Handle,Vars,Cout,noclpr,L/M):-term_variables(Vars,Rel),sacaDesigs(Handle,Rel,Cout,[],_,L/M).

sacaRestricciones(Handle,Vars,Cout,clpr(Residuo),L/M):-extraeVarsRelClpr(Vars,Residuo,Rel),quitaRedundancias(Cout,Cout1),sacaDesigs(Handle,Rel,Cout1,[],_,L/M1),sacaRestrClpr(Rel,Residuo,M1/M).

% SALIDA DE DESIGUALDADES SINTACTICAS

sacaDesigs(_,_,[],_,_,L/L).sacaDesigs(Handle,Rel,[X:CX|R],Rin,Rout,L/M):-

(%var(X),esRelevante(X,Rel),!,sacaDesigsVar(Handle,Rel,X,CX,Rin,Rout1,L/M1),sacaDesigs(Handle,Rel,R,Rout1,Rout,M1/M)

;sacaDesigs(Handle,Rel,R,Rin,Rout,L/M)

).

Page 258: Un lenguaje lógico funcional con restricciones1

258 APENDICE I. SALIDA DE RESPUESTAS

sacaDesigsVar(_,_,_,[],Rin,Rin,L/L).sacaDesigsVar(Handle,Rel,X,[T|R],Rin,Rout,L/M):-

(esRelevante(T,Rel),insertaDesig(X,T,Rin,Rout1),!,nl,tab(12),write(’{ ’),escribeAtomo(Handle,L/M1,X),write(’ /= ’),escribeAtomo(Handle,M1/M2,T),write(’ }’),sacaDesigsVar(Handle,Rel,X,R,Rout1,Rout,M2/M)

;sacaDesigsVar(Handle,Rel,X,R,Rin,Rout,L/M)

).

insertaDesig(X,T,Rin,Rout):-X@<T,!,insertaDesig1(X,T,Rin,Rout);insertaDesig1(T,X,Rin,Rout).

insertaDesig1(X,Y,[],[(X,Y)]).insertaDesig1(X,Y,[(A,B)|R],[(A,B)|R1]):-

(A\==X;B\==Y),insertaDesig1(X,Y,R,R1).

% SALIDA DE RESTRICCIONES SOBRE LOS REALES

% Aqui hay un pequenio truco para sacar las restricciones asociadas a los reales% Nosotros queremos que el sistema haga la proyeccion, que se hace con el dump% pero en este momento no funcionaria directamente hacer un dump porque ya no% hay objetivos suspendidos y el "almacen de restricciones" esta vacio. Lo que% hacemos es relanzar al sistema el conjunto de restricciones sobre reales y% hacer entonces el dump. Ademas, como queremos dejar el sistema limpio, este% relanzamiento lo hacemos por medio de un call_residue.

sacaRestrClpr(Relevantes,Residuo,L/M):-call_residue(relanzaResiduo(Relevantes,Residuo,L/M),_).

relanzaResiduo(Relevantes,Residuo,L/L2):-% relanzamos el residuomap_call(Residuo),

Page 259: Un lenguaje lógico funcional con restricciones1

259

% hacemos una lista con los nombres asociados a las vars relevantes para% el propio dump de nombre a dichas variableshazListaNombresVarsRel(Relevantes,NomRel,L/L1),linear:dump(Relevantes,NomRel,ResClpr),% aqui insertamos la escritura de las restriccionesescribeRestriccionesClpr(ResClpr,L1/L2).

escribeRestriccionesClpr([],L/L).escribeRestriccionesClpr([C|R],L/L2):-

nominaVarsTermino(C,C1,L/L1),nl,tab(12),write(’{ ’),write(C1),write(’ }’),escribeRestriccionesClpr(R,L1/L2).

hazListaNombresVarsRel([],[],L/L).hazListaNombresVarsRel([Var|R],[Nom|RNom],L/L2):-

buscaInsertaVar(Var,L/L1,Nom),hazListaNombresVarsRel(R,RNom,L1/L2).

/********** NOMINACION DE VARIABLES DE UN TERMINO ***************/

/* Este predicado da nombre a todas las variables que aparecen en untermino. Si la variable ha aparecido anteriormente ya tendra unnombre que es el que se le dara y si no, se le da uno nuevo y sealmacena para posteriores apariciones. Las variables junto con susnombres se almacenan en una lista, que se va pasando de una clausulaa otra y se va completando. buscaInsertaVar se ocupa de ver si la

variable tiene nombre (ya ha aparecido), que es el que devolvera. Sino ha aparecido le da un nombre nuevo y la almacena junto con este

en la lista que se le pasa*/

nominaVarsTermino(X,Nom,L/L1):-var(X),!,buscaInsertaVar(X,L/L1,Nom).

nominaVarsTermino(T,T1,L/L1):-T=..[Nom|Args],(

Nom==’=\=’,!,Nom1=’/=’;

Page 260: Un lenguaje lógico funcional con restricciones1

260 APENDICE I. SALIDA DE RESPUESTAS

Nom==’=’,!,Nom1=’==’;

Nom1=Nom),nominaVarsListaTerms(Args,Args1,L/L1),T1=..[Nom1|Args1].

nominaVarsListaTerms([],[],L/L).nominaVarsListaTerms([T|R],[T1|R1],L/L2):-

nominaVarsTermino(T,T1,L/L1),nominaVarsListaTerms(R,R1,L1/L2).

/*****************************************************************************EXTRACCION DE VARIABLES RELEVANTES DE LA RESPUESTA

*****************************************************************************/

/* extraeVarsRel(Vars,Residuo,Rel).- Vars es la lista variables del objetivo y viene de la forma

[(Nombre,Term) ...], donde Nombre es el nombre de la variable y Term eltermino al que se ha ligado una vez que se ha resuelto el objetivo.

- Residuo es la lista de objetivos suspendidos que resulta de laejecucion. Para la extraccion de vars relevantes solo nos interesan losresiduos no lineales

- Rel es lo que devuelve el predicado y es una lista de variables(las relevantes).

Una variable es relevante si aparece en un termino que se haligado a una variable del objetivo (el termino puede ser una variableprolog) o bien, si aparece en una restriccion no lineal. De la primeraparte se encarga extraeVarsRelObj y de la segunda extraeVarsResiduo */

Page 261: Un lenguaje lógico funcional con restricciones1

261

extraeVarsRelClpr(Vars,Residuo,Rel):-extraeRestrNonLin(Residuo,NonLin),term_variables((Vars,NonLin),Rel).

extraeRestrNonLin([],[]).extraeRestrNonLin([(_-(nonlin:C))|R],[nonlin:C|R1]):-

!,extraeRestrNonLin(R,R1).

extraeRestrNonLin([_|R],R1):-extraeRestrNonLin(R,R1).

% TERMINOS RELEVANTES

esRelevante(Term,L):-var(Term),!,member(Term,L).esRelevante(Term,L):-

Term=..[_|Args],esRelevanteLst(Args,L).

esRelevanteLst([],_).esRelevanteLst([Term|R],L):-

esRelevante(Term,L),esRelevanteLst(R,L).

% estos predicados sirven para depurar un poco la salida. Lo que hacen es quitar% repeticiones de restricciones repetidas.

quitaRedundancias([],[]).quitaRedundancias([V:C|R],[V:C1|R1]):-

quitaRedundanciasLista(C,C1),quitaRedundancias(R,R1).

Page 262: Un lenguaje lógico funcional con restricciones1

262 APENDICE I. SALIDA DE RESPUESTAS

quitaRedundanciasLista([],[]).quitaRedundanciasLista([L|R],R1):-

member(L,R),!,quitaRedundanciasLista(R,R1).

quitaRedundanciasLista([L|R],[L|R1]):-quitaRedundanciasLista(R,R1).

%miembro(X,[Y]):-!,X==Y.%miembro(X,[L|R]):-(X==L,!;miembro(X,R)).

Page 263: Un lenguaje lógico funcional con restricciones1

Bibliografıa

[AAF+98] E. Albert, M. Alpuente, M. Falaschi, P Julian, and G. Vidal. Improvingcontrol in functional logic program specialization. In To appear in Proc.SAS’98. Springer LNCS, 1998.

[AB94] K.R. Apt and R. Bol. Logic programming and negation: A survey.Journal of Logic Programming, 19&20, 1994.

[AJ93] J. M. Almendros-Jimenez. Diseno de un sistema de tipos con polimor-fismo parametrico y desarrollo de un inferidor para hobabel. Trabajode Investigacion de Tercer Ciclo, Dpto. de Informatica y Automatica,Universidad Complutense de Madrid, Jun. 1993.

[Ant92] S. Antoy. Definitional trees. In Proc. of the 3rd International Conferenceon Algebraic and Logic Programming, pages 143–157. Springer LNCS632, 1992.

[Apt90] K.R Apt. Logic programming. In J van Leeuwen, editor, Handbook ofTheoretical Computer Science, volume B, pages 495–574. Elsevier, 1990.

[AG94] P Arenas-Sanchez and A. Gil-Luezas. User’s manual for bablog. Te-chnical report, Depto. de Informatica y Automatica, UCM, October1994.

[AGL94] P. Arenas-Sanchez, A. Gil-Luezas, and F.J. Lopez-Fraguas. Combininglazy narrowing with disequality constraints. In Proc. of the 6th In-ternational Symposium on Programming Language Implementation andLogic Programming, pages 385–399. Springer LNCS 844, 1994.

[AHL+96] P. Arenas-Sanchez, T. Hortala-Gonzalez, P. Lopez-Fraguas, andE. Ullan-Hernandez. Real constraints within a functional logic langua-ge. In Join Conference on Declarative Programming, APPIA-GULP-PRODE’96, pages 451–462, 1996.

[AR97a] P. Arenas-Sanchez and M. Rodrıguez-Artalejo. A semantic frameworkfor functional logic programming with algebraic polymorphic types. InProc. CAAP’97. Springer LNCS, 1997.

[AR97b] P. Arenas-Sanchez and M. Rodrıguez-Artalejo. A lazy narrowing cal-culus for functional logic programming with algebraic polimorfic types.In ILPS’97, pages 53–69. MIT Press, 1997.

263

Page 264: Un lenguaje lógico funcional con restricciones1

264 BIBLIOGRAFIA

[BCM89] P.G. Bosco, C. Cecchi, and C. Moiso. An extension of wam for k-leaf: a wam-based compilation of conditional narrowing. In Proc. SixthInternational Conference on Logic Programming (Lisboa), pages 318–333. MIT Press, 1989.

[BMP+90] R. Barbuti, P. Mancarella, D. Pedreschi, and F. Turini. A transfor-mational approach to negation in logic programming. Journal of LogicProgramming (8), pages 201–228, 1990.

[Car95] B. Carlson. Compiling and Executing Finite Domain Constraints. PhDthesis, Upsala University, 1995.

[CF93] P.H. Cheong and L. Fribourg. Implementation of narrowing: The prolog-based approach. In K.R. Apt, J.W. de Bakker, and J.J.M.M. Rutten,editors, Logic programming languages: constraints, functions, and ob-jects, pages 1–20. MIT Press, 1993.

[Cha88] D. Chan. Constructive negation based on the completed database. InProc. 5th Conference on Logic Programming & 5th Symposium on LogicProgramming (Seattle), pages 111–125, 1988.

[Che90] P.H. Cheong. Compiling lazy narrowing into prolog. Technical report25, LIENS, Paris, 1990. To appear in Journal of New Generation Com-puting.

[Cla78] K.L. Clark. Negation as failure. In H. Gallaire and J. Minker, editors,Logic and Data Bases, pages 293–322. Plenum Press, 1978.

[CM87] W.F. Clocksin and C.S. Mellish. Programming in Prolog. Springer,third rev. and ext. edition, 1987.

[Coh90] J. Cohen. Constraint logic programming languages. Communicationsof the ACM, 33(7):52–68, 1990.

[Col90] A. Colmerauer. An introduction to prolog iii. Communications of theACM, 33(7):69–90, 1990.

[CR98] R. Caballero-Roldan. Parsers logico funcionales. Trabajo de Investiga-cion de Tercer Ciclo, Dpto. de Sistemas Informaticos y Programacion,Universidad Complutense de Madrid,, Sep. 1998.

[CLS97] R. Caballero-Roldan, F.J. Lopez-Fraguas, and J. Sanchez-Hernandez.User’s manual for T OY. Technical Report 97/57, Depto. SIP, UCMMadrid, 1997.

[DM79] N. Dershowitz and Z. Manna. Proving termination with multiset orde-rings. Communications of the ACM, 22(8):465–476, 1979.

[DM82] L. Damas and R. Milner. Principal type-schemes for functional pro-grams. In Proc. 9th Annual Symposium on Principles of ProgrammingLanguages, pages 207–212, 1982.

Page 265: Un lenguaje lógico funcional con restricciones1

BIBLIOGRAFIA 265

[FHK+93] T. Fruhwirth, A. Herold, V. Kuchenhoff, T. Le Provost, P. Lim, E. Mon-froy, and M. Wallace. Constraint logic programming – an informal in-troduction. Technical report ecrc-93-5, ECRC, 1993.

[Gin91] M.L. Ginsberg. Negative subgoals with free variables. Journal of LogicProgramming, 11:271–293, 1991.

[GM86] E. Giovannetti and C. Moiso. A completeness result for e-unificationalgorithms based on conditional narrowing. In Proc. Workshop on Foun-dations of Logic and Functional Programming, pages 157–167. SpringerLNCS 306, 1986.

[G94] F.C. Gonzalez Moreno. Programacion Logica de Orden Superior conCombinadores. PhD thesis, DIA-UCM, Madrid, 1994.

[GHL+96] J.C. Gonzalez-Moreno, M.T. Hortala-Gonzalez, F.J. Lopez-Fraguas,and M. Rodrıguez-Artalejo. A rewriting logic for declarative program-ming. In Proc. ESOP’96, pages 156–172. Springer LNCS 1058, 1996.

[GHL+98] J.C. Gonzalez-Moreno, T. Hortala-Gonzalez, F. Lopez-Fraguas, andM. Rodrıguez-Artalejo. An approach to declarative programming basedon a rewriting logic. To appear in Journal of Logic Programming, 1998.

[GHR97] J.C. Gonzalez-Moreno, M.T. Hortala-Gonzalez, and M. Rodrıguez-Artalejo. A higher order rewriting logic for functional logic program-ming. In ICLP’97, pages 153–167. MIT Press, 1997.

[Gro96] The Programming Systems Group. SICStus Prolog User’s Manual. Swe-dish Institute of Computer Science, PO Box 1263. S-164 28 Kista, Swe-den, 3# 5 edition, October 1996.

[Gro97] The Programming Systems Group. SICStus Prolog User’s Manual. Swe-dish Institute of Computer Science, PO Box 1263. S-164 28 Kista, Swe-den, 3# 6 edition, November 1997.

[Han93] M. Hanus. Analysis of nonlinear constraints in clp(∇). In Proc. TenthInternational Conference on Logic Programming, pages 83–99. MITPress, 1993.

[Han94] M. Hanus. The integration of functions into logic programming: Fromtheory to practice. Journal of Logic Programming, 19&20:583–628,1994.

[Han95a] M. Hanus. Compile-time analysis of nonlinear constraints in clp(∇).New Generation Computing, 13(2):155–186, 1995.

[Han95b] M. Hanus. Efficient translation of lazy functional logic programs intoprolog. In Proc. Fifth International Workshop on Logic Program Synt-hesis and Transformation, pages 252–266. Springer LNCS 1048, 1995.

[He97] M. Hanus (ed.). Curry: An integrated functional logic language. Availa-ble at http://www-i2.informatik.rwth-aachen.de/~hanus/curry,1997.

Page 266: Un lenguaje lógico funcional con restricciones1

266 BIBLIOGRAFIA

[HFP97] P. Hudak, J. H. Fasel, and J. Peterson. A Gentle Introduction to Haskell-version 1.4-, March 1997.

[HLS+97] T. Hortala-Gonzalez, F.J. Lopez-Fraguas, J. Sanchez-Hernandez, andE. Ullan-Hernandez. Declarative programming with real constraints.Technical report, SIP-5997, 1997.

[HJM+91] N. Heintze, J. Jaffar, S. Michaylov, P. Stuckey, and R. Yap. The CLP(R)Programmer’s Manual, Version 1.1. IBM Thomas J. Watson ResearchCenter, Yorktown Heights, 1991.

[HKM95] M. Hanus, H. Kuchen, and J.J. Moreno-Navarro. Curry: A truly func-tional logic language. In Proc. ILPS’95 Workshop on Visions for theFuture of Logic Programming, 1995.

[Hol95] C. Holzbaur. Ofai clp(q,r) manual, edition 1.3.3. Technical report,Austrian Research Institute for Artificial Intelligence, Vienna, 1995.

[Hon92] H. Hong. Non-linear real constraints in constraint logic programming.In Proc. of the 3rd International Conference on Algebraic and LogicProgramming, pages 201–212. Springer LNCS 632, 1992.

[Hus92] H. Hussmann. Nondeterministic algebraic specifications and noncon-fluent term rewriting. Journal of Logic Programming, 12:237–255, 1992.

[Hus93] Hussmann. Non-Determinism in Algebraic Specifications and AlgebraicPrograms. Birkhauser Verlag, 1993.

[JJ97] M.P Jones and Peterson J.C. Hugs 1.4. the nottingham and yale haskelluser’s system. Technical report, University of Nottingham and Univer-sity of Yale, April 1997.

[JL87] J. Jaffar and J.-L. Lassez. Constraint logic programming. In Proc. ofthe 14th ACM Symposium on Principles of Programming Languages,pages 111–119, Munich, 1987.

[JM94] J. Jaffar and M.J. Maher. Constraint logic programming: A survey.Journal of Logic Programming, 19&20:503–581, 1994.

[JMS+92a] J. Jaffar, S. Michaylov, P.J. Stuckey, and R.H.C. Yap. An abstractmachine for clp(∇). In Proc. SIGPLAN Conference on ProgrammingLanguage Design and Implementation, pages 128–139. SIGPLAN Noti-ces, Vol. 27, No. 7, 1992.

[JMS+92b] J. Jaffar, S. Michaylov, P.J. Stuckey, and R.H.C. Yap. The clp(∇)language and system. ACM Transactions on Programming Languagesand Systems, 14(3):339–395, 1992.

[Jon] M.P. Jones. An Introduction to Gofer.

[Jon94] M.P. Jones. Qualified types: theory and practice. Cambridge Universitypress, 1994.

Page 267: Un lenguaje lógico funcional con restricciones1

BIBLIOGRAFIA 267

[KLM+92] H. Kuchen, F.J. Lopez-Fraguas, J.J. Moreno-Navarro, andM. Rodrıguez-Artalejo. Implementing a lazy functional logic lan-guage with disequality constraints. In Proc. of the 1992 JointInternational Conference and Symposium on Logic Programming. MITPress, 1992.

[Kun87] K. Kunen. Negation in logic programming. Journal of Logic Program-ming, 4:289–308, 1987.

[L92] F.J. Lopez Fraguas. A general scheme for constraint functional logicprogramming. In Proc. of the 3rd International Conference on Algebraicand Logic Programming, pages 213–227. Springer LNCS 632, 1992.

[L94] F.J. Lopez Fraguas. Programacion Funcional y Logica con Restriccio-nes. PhD thesis, DIA-UCM, Madrid, 1994.

[LR91] F.J. Lopez Fraguas and M. Rodrıguez-Artalejo. An approach to cons-traint functional logic programming. Technical report dia/91/4, Uni-versidad Complutense, Madrid, 1991.

[LLR93] R. Loogen, F. Lopez-Fraguas, and M. Rodrıguez-Artalejo. A demanddriven computation strategy for lazy narrowing. In Proc. of the 5thInternational Symposium on Programming Language Implementationand Logic Programming, pages 184–200. Springer LNCS 714, 1993.

[Llo94] J.W. Lloyd. Combining functional and logic programming languages. InProc. of the International Logic Programming Symposium, pages 43–57,1994.

[Llo95] J.W. Lloyd. Declarative programming in escher. Technical report cstr-95-013, University of Bristol, 1995.

[LW91] R. Loogen and S. Winkler. Dynamic detection of determinism in func-tional logic languages. In Proc. of the 3rd Int. Symposium on Program-ming Language Implementation and Logic Programming, pages 335–346.Springer LNCS 528, 1991. Extended version to appear in TheoreticalComputer Science, 1995.

[LW95] R. Loogen and S. Winkler. Dynamic detection of determinism in func-tional logic languages. Theoretical Computer Science 142, pages 59–87,1995.

[M94] J.J. Moreno-Navarro. Default rules: An extension of constructive ne-gation for narrowing-based languages. In Proc. Eleventh InternationalConference on Logic Programming, pages 535–549. MIT Press, 1994.

[M96] J.J. Moreno-Navarro. Extending constructive negation for partial func-tions in lazy functional-logic languages. In Proc. 5th InternationalWorkshop on Extensions of Logic Programming, pages 213–227. Sprin-ger LNAI 1050, 1996.

Page 268: Un lenguaje lógico funcional con restricciones1

268 BIBLIOGRAFIA

[MR89] J.J. Moreno-Navarro and M. Rodrıguez-Artalejo. Logic programmingwith functions and predicates: The language babel. Technical reportdia/89/3, Universidad Complutense, Madrid, 1989.

[MR92] J.J. Moreno-Navarro and M. Rodrıguez-Artalejo. Logic programmingwith functions and predicates: The language babel. Journal of LogicProgramming, 12:191–223, 1992.

[O’K90] R.A. O’Keefe. The Craft of Prolog. Cambridge, MIT Press, 1990.

[PH97] J. Peterson and K. (eds) Hammond. Report on the Programming Lan-guage Haskell: a Non-strict, Purely Functional Language -version 1.4-,January 1997.

[PJ93] J. Peterson and M.P. Jones. Implementing type classes. In Proc. ofACM SIGPLAN SYmposium on Programming Language Design andImplementation (PLDI’93), pages 227–236. ACM SIGPLAN NoticesVol. 28, No. 6, 1993.

[Red85] U.S. Reddy. Narrowing as the operational semantics of functional lan-guages. In Proc. IEEE Internat. Symposium on Logic Programming,pages 138–151, Boston, 1985.

[SS86] L. Sterling and E. Shapiro. The Art of Prolog. MIT Press, 1986.

[Stu91] P.J. Stuckey. Constructive negation for constraint logic programming.In Proc. LICS’91, pages 328–339, 1991.

[V89] P. Van Hentenryck. Constraint Satisfaction in Logic Programming. MITPress, 1989.

[Wad85] P. Wadler. How to replace failure by a list of successes. In FunctionalProgramming and Computer Architecture. Springer LNCS 201, 1985.