Janvier 1995 { Journees Francophones des Langages Applicatifs { JFLA95 Une Interface Graphique Distribuee pour le -Calcul supportee par des Serveurs Fonctionnels V.M. Gulas, A. Valderruten fcigulias, [email protected] Departamento de Computacion Facultad de Informatica Campus de Elvi~na 15071 La Coru~na, Espa~na Dans ce papier nous introduisons l'approche fonctionnelle dans un contexte de developpement de systemes distribues heterogenes ou s'integrent d'autres paradigmes de programmation. Nous y degageons le r^ole de la Programmation Fonctionnelle dans la realisation d'architectures client-serveur, de facon a exploiter les deux caracteristiques fondamentales des programmes fonctionnels: simplicite d'implementation a partir des specications et verication de la correction. La methodologie proposee est appliquee pour developper avec le langage CAML Light une interface graphique distribuee pour le -Calcul. Mots cles: Architectures distribuees, Interaction Programmation ImperativeDeclarative, Interfaces Utilisateur, -Calcul, Programmation Fonctionnelle. 1. Serveurs Fonctionnels L'interconnexion de systemes est un concept d'actualite lorsque nous parlons de systemes informatiques. Une des solutions les plus utilisees pour la distribution de ressources autant physiques (CPU, imprimante,...) que logiques (donnees, programmes,...) est de suivre une architecture client-serveur: un serveur de ressources, place sur un point connu du reseau, est capable de traiter des requ^etes emises par des clients a partir de n'importe quel autre point du reseau. Un serveur ne se limite donc pas a traiter des requ^etes a propos de ressources physiques (par exemple, une imprimante), mais il peut egalement accepter des requ^etes logiques (par exemple, demande d'evaluation d'une expression 1 V.M. Gulas, A. Valderruten quelconque). Nous aborderons ce dernier type de serveurs en etudiant la facon dont nous pouvons exploiter les atouts de la programmation fonctionnelle dans leur developpement. Mais qu'est-ce que le paradigme fonctionnel peut-il apporter a l'implementation de serveurs? Une reponse peut ^etre obtenue en etudiant quelques unes des caracteristiques du logiciel fonctionnel: 1. Correction: Un des objectifs recherches par la programmation fonctionnelle se situe dans la suppression des eets de bord, qui sont a la base de la programmation imperative. L'elaboration de programmes libres d'eets de bord permet de demontrer plus facilement la correction des algorithmes utilises [4] (il faut noter que nous parlons toujours de demontrer la correction de l'algorithme et non pas de valider son fonctionnement a l'aide de tests qui essayent de prendre en compte un grand nombre de cas, mais qui jamais peuvent assurer que le logiciel est correct). Le besoin d'assurer la correction du logiciel augmente avec sa criticite. En ce qui nous concerne, la programmation fonctionnelle est un outil approprie a la realisation de serveurs dans des domaines ou le besoin de logiciels libres d'erreurs est important. 2. Developpement de prototypes: La nature declarative de la programmation fonctionnelle reduit de facon considerable le temps de developpement d'un produit, car elle permet de: raner la conception, a l'aide de realimentations successives dans l'elaboration de prototypes ranes (paradigme du prototypage du Genie Logiciel). developper (en utilisant ou non un paradigme declaratif) des applications clientes d'un serveur deja implemente: (a) avec ce serveur, les developpeurs d'applications clientes disposent d'une interface parfaitement denie, ainsi que de la possibilite d'utiliser les services oerts par le serveur pour ses jeux de test. (b) si le serveur avec noyau fonctionnel n'est pas capable de satisfaire nos contraintes d'ecacite, nous pouvons le reconstruire en utilisant un langage imperatif, developpement qui peut ^etre parallele a celui des applications clientes. Ceci est possible car l'elaboration du prototype fonctionnel a permis de xer l'interface clientserveur des les premieres phases de conception. 2 Une Interface Graphique Distribuee pour le -Calcul... 3. Integration technologique: Les limitations en termes d'ecacite du code produit par les compilateurs de langages fonctionnels peuvent ^etre reduites si nous disposons de machines avec des architectures orientees a leur support. Un domaine de recherche tres actif actuellement est l'etude de la parallelisation du code produit par des compilateurs de langages declaratifs, et en particulier, de langages de nature fonctionnelle, pour l'exploitation de machines massivement paralleles. La parallelisation est plus simple et naturelle a cause de la suppression des eets de bord, qui se traduit en une reduction importante des dependances de donnees. Pourtant, la decision d'ajouter a un systeme informatique une machine orientee a ce type de code reste encore dicilement justiable en termes economiques. Mais deja, ce probleme l'est moindre dans un environnement heterogene, ou les composantes du systeme informatique partagent leur charge en fonction de leur specialisation. En conclusion, nous pouvons utiliser le meilleur des deux mondes, declaratif et imperatif, au lieu de nous restreindre au declaratif seul, surtout lorsque nous avons des aspects qui sont consideres critiques du point de vue de la correction. 2. Le modele Client-Serveur avec des Serveurs Fonctionnels La programmation declarative (dans notre cas, la fonctionnelle) s'integre dans une architecture client-serveur par l'intermediaire des serveurs fonctionnels. Dans le modele obtenu (gure 1), nous pouvons dierencier trois parties: C^ote serveur, ou reside le serveur, composante logicielle chargee de repondre aux requ^etes provenant d'un point quelconque du reseau (nous parlerons de services oerts par un serveur). C^ote client, ou resident les clients, composantes logicielles necessitant des services oerts par le serveur an de realiser leurs activites. Protocole de communication, represente une interface bien denie qui permet une interaction complete entre clients et serveur. 3 V.M. Gulas, A. Valderruten Noyau Fonctionnel Requête Paramètres Réponse Interface FL-OS File d’attente Côté Serveur Protocole de Communication Côté Client ... Client 1 Client n Client 2 Figure 1: Modele Client-Serveur ou le serveur possede un noyau fonctionnel Le point cle du schema se trouve c^ote serveur. La correction de l'ensemble est fortement liee a celle du serveur. Une erreur dans une application cliente, en principe, n'a pas d'incidence sur les autres clients ou sur le serveur. D'ou le besoin de garantir la correction du logiciel serveur. Le serveur est forme de deux grands blocs: Noyau fonctionnel, ou les requ^etes des clients sont reellement traitees. Il s'agit de la partie critique du serveur et par consequent nous devons garantir sa correction. Ceci nous suggere de construire le noyau en utilisant le paradigme fonctionnel [3]. Interface FL-OS, c'est l'interface existante entre le noyau fonctionnel et le systeme operatoire sous-jacent; elle represente un isolant qui separe la purete fonctionnelle des details dans l'interaction avec les clients. Le noyau fonctionnel peut ^etre vu comme un programme implementant n fonctions: 4 F Une Interface Graphique Distribuee pour le -Calcul... f1 : p11 p21 ::: pm1 1 f2 : p12 p22 ::: pm2 2 .. .. . . fn : p1n p2n ::: pmn n r1 r2 .. .. . . ! rn ! ! ou les fi representent les n fonctions, avec mi parametres chacune, qui produisent comme resultat ri ; le parametre j de la fonction i est note pji . Les applications clientes peuvent acceder chacune de ces n fonctions (via l'interface FL-OS), constituant les n services oerts par le serveur. Programme Fonctionnel 1 m f ( p ,..., p i ) = r i i i i Noyau Fonctionnel Interface LF-SO Code d’entrée pretraité Code de sortie pretraité Données en entrée Données en sortie Requête f i Réponse r i Paramètres p j i File d’attente des requêtes File d’attente des réponses Figure 2: Traitement de l'information dans un serveur avec noyau fonctionnel L'interface FL-OS est charge de convertir les paquets d'information provenant des clients en appels aux fonctions du programme F et de transfomer les resultats en paquets qui seront envoyes au demandeur, comme decrit dans la gure 2. A l'interface sont associees deux les d'attente, une pour les requ^etes des clients qui ne sont pas encore traitees, et une autre pour les resultats qui ne sont pas encore envoyes a leur destinataire. M^eme si nous supposons que la discipline de gestion des deux les d'attente est FIFO, d'autres algorithmes (par exemple, avec priorites) peuvent ^etre envisages car l'absence d'eets de bord nous 5 V.M. Gulas, A. Valderruten garantit l'independance par rapport a l'ordre des requ^etes provenant des dierents clients. Un des problemes principaux qui appara^t a l'heure de construire un serveur avec un noyau fonctionnel se situe precisement dans la realisation de l'interface FL-OS, puisque nous devons assurer la purete fonctionnelle tout en la mettant en relation avec le monde imperatif. Il serait interessant de generer automatiquement le code de l'interface a partir des services oerts par le noyau fonctionnel: nous introduisons cette possibilite dans le paragraphe 3.3. Le format des paquets de donnees qui circulent dans le serveur est completement deni par le protocole de communication. Dans ce protocole les requ^etes, les parametres et les reponses doivent ^etre codes avec un format intelligible pour les clients et les serveurs. En general, adopter la representation interne du serveur est une strategie pauvre car elle conditionne les interfaces a suivre une implementation concrete du langage fonctionnel, perdant l'independance necessaire aux modications futures du noyau (par exemple, la reecriture avec un langage imperatif). Une meilleure solution est d'utiliser un format intermediaire qui s'adapte a l'implementation des clients et a celle des serveurs. 3. Une Interface Distribuee pour le Calcul Un des principaux arguments contre les developpements fonctionnels se situe dans la diculte d'elaborer une entree/sortie fonctionnelle ecace, sans compromettre la purete des programmes avec l'introduction d'eets de bord. Malgre tout, en general, les langages fonctionnels proposent des bibliotheques pour le developpement d'interfaces, ou l'interaction avec l'environnement se realise a l'aide d'eets de bord. Notre proposition pretend inverser le probleme: nous construisons une interface graphique, en utilisant n'importe quel paradigme de programmation (declaratif ou imperatif), mais a l'aide d'un protocole lui permettant d'interagir avec un noyau fonctionnel pur qui s'attaque aux aspects critiques du probleme. Un cas particulier specialement interessant des architectures clientserveur est celui des interfaces utilisateur. Ici l'interface n'est qu'un client qui emet des requ^etes au logiciel qui resoud le probleme et auquel elle est en train d'enrober. An d'illustrer cette proposition nous decrirons par la suite une interface graphique pour une implementation du -Calcul. Dans la gure 3 nous presentons un schema general qui montre l'architecture utilisee pour 6 Une Interface Graphique Distribuee pour le -Calcul... Client Serveur Implémentation du λ -Calcul Interface Graphique CAML Light Aïda/Le-Lisp CAMLface AIDAface Sockets TCP/IP AIX 3.2.5 Solaris 1.1 CAMLHOST=mortadelo (SUN SPARC 10) AIDAHOST=landro (IBM RS/6000) Figure 3: Schema de l'interface distribuee pour le -Calcul implementer notre interface. Plusieurs aspects conditionnent les choix de conception: Le noyau du programme est par nature fonctionnel; l'evaluateur de -expressions a ete developpe a l'aide du langage de programmation fonctionnelle CAML Light [11], developpe par l'INRIA dans le cadre du projet FORMEL. L'interface graphique, l'editeur de -expressions, a ete developpe en utilisant l'outil AIDA de Ilog [15], qui a un comportement typiquement imperatif. Les deux programmes, l'editeur et l'evaluateur, resident physiquement dans des machines dierentes, c'est-a-dire, nous parlons d'une interface distribuee dans le reseau. Dans la gure 4 nous pouvons observer le scenario pour un cas particulier: par un soucis de simplicite dans cet exemple on dispose d'un evaluateur d'expressions (serveur) pour chaque editeur (client), ce qui rend innecessaire les les d'attente pour les requ^etes et les reponses. 3.1. Developpement du -Serveur avec un noyau fonctionnel Le noyau fonctionnel F correspond a une simple implementation avec CAML Light du -Calcul, decrite par Curien dans [5], et dans laquelle, 7 V.M. Gulas, A. Valderruten Serveur λ -Serveur Requête Paramètres Réponse CAMLface Côté Serveur TCP/IP Sockets Protocole de Communication Côté Client AÏDAface Requête Paramètres Réponse Interface Graphique Client Figure 4: Interaction entre le -Serveur et le -E diteur les -termes sont transformes avec la notation abstraite de De Bruijn [6] pour simplier la -reduction, en employant une strategie d'evaluation lazy. Le -Serveur ore a ses clients potentiels deux services dierents, representes par les deux fonctions suivantes: evaluation de -expressions eval: lexp list ! lexp list comparaison de -expressions comp: lexp list ! bool ou le type bool est le type predeni boolean, le type list est le type des listes d'elements de type et le type lexp est un type concret CAML qui represente les -termes avec la syntaxe concrete: type lexp = VAR of string | LAM of string*lexp | APP of lexp*lexp;; 8 Une Interface Graphique Distribuee pour le -Calcul... Le service d'evaluation eval realise sur chaque element de la liste de -expressions trois operations: passage de syntaxe concrete a notation de De Bruijn (DB: string list ! lexp ! Lambda, ou string list est la liste de variables libres de lexp et Lambda est un type concret CAML qui represente la syntaxe abstraite de De Bruijn), obtention de la forme normale de l'expression avec la notation abstraite (fn: Lambda ! Lambda) et conversion de la notation abstraite une nouvelle fois vers la syntaxe concrete des -expressions (concrete: string list ! Lambda ! lexp, o u string list sont les variables libres dans l'expression originale). Si nous considerons la fonction vl: lexp ! string list qui determine les variables libres d'une -expression et la fonction map: ( ! ) ! list ! list, qui etant donnees une fonction f et une liste produit une nouvelle liste resultat de l'application de la fonction f a chacun des elements de la liste, une implementation de eval peut ^etre la suivante: let eval_item l = concrete libres (fn (DB libres l)) where libres = vl l;; let eval liste = map eval_item liste;; Le service d'evaluation comp permet la comparaison de -expressions; pour cela nous pouvons comparer la forme normale de la premiere expression de la liste en entree avec le reste de la liste: let rec comp_reste l = function [] -> true | (a::l') -> (l = (eval_item a)) & (comp_reste l l');; let comp = function (a::l) -> comp_reste (eval_item a) l | _ -> failwith "comp";; L'interface FL-OS, que nous avons appelle CAMLface dans cette implementation particuliere, est une combinaison de code CAML avec un fragment reduit de code C, qui dans l'ensemble realise les fonctions suivantes: 1 Initialiser le serveur, transformant le programme CAML en un processus daemon 1 habilitant un port de communication pour les futurs clients. Recevoir des paquets avec le format du protocole de communication et transformer ces donnees vers le format interne CAML, pour realiser les calculs demandes. Processus qui reste "endormi" dans l'attente d'une requ^ete d'un client 9 V.M. Gulas, A. Valderruten Construire et envoyer des paquets vers les clients avec les resultats, c'est-a-dire, transformer les donnees a partir du format interne CAML vers le langage intermediaire utilise par le protocole de communication. 3.2. Developpement des interfaces clientes λ-Panel λ-Éditeur Éditeur de texte Figure 5: Interface graphique pour le -Calcul L'application cliente de notre exemple est une interface graphique qui ore a l'utilisateur la possibilite de visualiser et d'editer des -termes sous forme d'arbres. Elle integre la possibilite d'emettre des requ^etes d'evaluation et de comparaison d'expressions au serveur, en employant le protocole de communication decrit dans le paragraphe 3.3. Cette implementation a ete realisee avec XWindows, sur une station de travail IBM RISC/6000, en utilisant l'outil AIDA de Ilog, ou l'entree/sortie se realise avec des eets de bord. Dans la gure 5 nous pouvons apprecier l'aspect de l'application cliente construite. Nous distingons trois groupes de fen^etres clairement dierenciees: Le -E diteur, fen^etre d'edition nous permettant de visualiser, creer ou modier des -expressions sous forme d'arbre. Parmi les fonctions d'edition supportees nous pouvons citer: couper, copier, coller et eacer des sous-arbres de la -expression, conversion entre 10 Une Interface Graphique Distribuee pour le -Calcul... Arbre d’édition Espace d’édition Figure 6: Arbre d'edition dans le -E diteur les notations lineaire et en arbre d'un sous-arbre, reorganisation automatique et manuelle des noeuds de l'arbre, evaluation (requ^ete au serveur fonctionnel), conversion vers le format LaTEX via l'editeur de texte... La gure 7 presente la construction de l'expression (x:x) y. ? !? ? ! (x:?) ? ! (x:x) ? ! (x:x) y Figure 7: Construction de (x:x) y Le -Panel, fen^etre qui groupe un ensemble de -expressions en syntaxe concrete, de facon a maintenir l'organisation de l'information. Entre autres fonctionnalites, elle nous permet la selection de multiples expressions an de les evaluer ou les comparer. L'E diteur de Texte, simple editeur de texte qui permet d'ajouter des expressions creees avec le -E diteur. 11 V.M. Gulas, A. Valderruten liste d’expressions Panneau de contrôle Figure 8: Le -Panel Expression à évaluer Résultat de l’évaluation Boutons de contrôle Figure 9: Resultat d'une requ^ete d'evaluation 12 Une Interface Graphique Distribuee pour le -Calcul... 3.3. Un protocole de communication pour la generation automatique de l'interface FL-OS Le protocole de communication permet l'interaction entre le serveur et les clients. La communication se realise via les fonctionnalites de communication entre processus (IPC: Interprocess communication) fournies par le systeme operatoire sous-jacent, dans notre cas les sockets TCP/IP, abstraction qui cache la communication entre processus sous le concept de chier. Quand le processus serveur est active, il habilite l'un de ces ports de communication an de permettre le dialogue avec les clients. Ainsi, quand un client souhaite envoyer une requ^ete, il envoi le paquet de donnees correspondant vers ce point qui doit ^etre connu, et il attend la reponse de la part du serveur. Une description plus complete des sockets TCP/IP, ainsi que le probleme de communiquer aux clients le socket du serveur, se trouve dans [14], [2],[13]. Un probleme plus interessant reside dans la facon de coder les requ^etes, les parametres et les resultats dans les paquets de communication. Nous devons remarquer une nouvelle fois que par rapport a l'independance de l'implementation il n'est pas souhaitable de choisir ni la representation interne du serveur (CAML), ni celle du client (LeLisp/AIDA), mais plut^ot adopter une representation intermediaire, facilement manipulable. Il existe des standards pour le codage de types de base (entiers, cha^nes de caracteres...) qui garantissent cette independance, mais lorsque nous considerons les types complexes presents dans les langages fonctionnels nous sommes obliges de creer notre propre protocole. L'idee est d'obtenir deux fonctions (encode et decode) chargees de transformer n'importe quel type de donnees en une suite d'octets. Idealement, ces fonctions auraient le type CAML suivant: encode : decode : string ! ! string en veriant que pour tout x de type t on a decode (encode x)) x. Dans un langage comme Haskell [10], cette fonction peut ^etre denie avec la surcharge de la paire de fonctions (encode, decode) en utilisant les type classes. L'abscence de ce mecanisme de denition de familles de types, qui permet le polimorphisme ad-hoc cite, nous oblige a une implementation manuelle pour chaque type qui participe dans le processus de communication. Nous proposons d'introduire deux fonctions externes (developpees en langage de bas niveau) chargees de transformer la representation interne des types CAML en une cha^ne de caracteres. Pour representer un type t 13 = V.M. Gulas, A. Valderruten nous utiliserons une cha^ne constituee d'un premier caractere identiant la structure du type suivie du code correspondant. Nous utiliserons les dierentes fonctions denies dans les modules d'interface C de CAML Light (mlvalues.h, alloc.h, memory.h) pour l'acces a la structure interne. Nous devons denir alors: Une representation pour les types de base: { Pour les entiers, l'etiquette (ENCODE_INTEGER) suivie de leur representation standard (macros htons, ntohs de l'interface TCP/IP de C). { Pour les cha^nes de caracteres, l'etiquette (ENCODE_STRING) suivie de la cha^ne de caracteres se terminant par '\0'. Les autres types de base (char, oat...) ont une representation analogue. Une representation pour les types complexes (types produit et types concrets): Les tuples et les types concrets se representent en memoire a l'aide d'arbres. La seule dierence interne existante entre la representation d'un tuple et la representation d'un constructeur d'un type concret se trouve dans l'etiquette identiant le noeud (dans le tuple cette etiquette -TAG- prends la valeur 0, alors que le noeud representant un constructeur est etiquete avec un entier qui determine son ordre dans la denition du type). Le codage associe suit la structure suivante: { E tiquette identiant la donnee comme un type complexe (ENCODE_TUPLA); { Arite du noeud; { Valeur du champ TAG de la structure d'origine; { Codage de chacun des champs (arite fois). La representation qui en resulte equivaut a un parcours prexe de l'arbre. Les fonctions s'integrent dans les programmes CAML a l'aide d'un chier d'interface .mli: value decode : string -> 'a = 1 "decode";; value encode : 'a -> string = 1 "encode";; 14 Une Interface Graphique Distribuee pour le -Calcul... A partir des fonctions de plus haut niveau dans le noyau, le code de l'interface LF-SO est genere de facon dependante des services requis. Ce code est compose d'une fonction pour chacun des services requis, de la forme: let <nom_fonction>_serv sock args = let (x1,x2,...,xn) = decode args in let resultat = encode (<nom_fonction> x1 x2 ... xn) in transmet sock resultat;; ou <nom_fonction> est la fonction associee au service, n le nombre d'arguments de la fonction, x1, x2,...,xn des variables denies dans le contexte de la fonction pour garder le codage des arguments (en forme de tuple n-aire) et transmet est une fonction, denie a partir du module unix [12], chargee d'envoyer le resultat en utilisant un port TCP/IP (sock). Les services pour l'implementation du -Calcul (fonctions eval: lexp list ! lexp list et comp: lexp list ! bool) deviennent: let eval_serv sock args = let x1 = decode args in let resultat = encode (eval x1) in transmet sock resultat;; let comp_serv sock args = let x1 = decode args in let resultat = encode (comp x1) in transmet sock resultat;; A chaque service on associe un identicateur (un entier) qui s'ajoute au paquet de donnees pour specier la fonction que l'on desire utiliser. La fonction process utilise cette information pour determiner le service qui est demande. let services = [eval_serv; comp_serv];; let process sock req args = (nth req services) sock args;; ou nth est la fonction qui etant donnes un entier n et une liste l retourne le n-ieme element de la liste. Dans le cas ou les applications clientes sont developpees en CAML, les fonctions d'acces aux services peuvent ^etre denies avec le schema: let <nom_fonction> sock (x1:t1) (x2:t2) ... (xn:tn) = (decode (make_request sock <service_id> (encode (x1,x2,...xn))):t);; 15 V.M. Gulas, A. Valderruten ou <nom_fonction> est le service de type t1 ! t2 ... ! tn ! t; sock le descripteur du port utilis e dans la communication avec le serveur et make_request: file_descr ! int ! string ! string la fonction chargee de realiser la demande. Pour notre exemple, l'instance du schema serait: let eval sock (x1:lexp list) = (decode (make_request sock 0 (encode x1)):lexp list);; let comp sock (x1:lexp list) = (decode (make_request sock 1 (encode x1)):bool);; Les applications clientes developpees avec d'autres langages (comme c'est le cas de notre interface) doivent acceder au port de communication et envoyer des requ^etes en utilisant ce langage d'echange pour les donnees. 4. Conclusions Nous avons analise les avantages qui resultent de l'introduction du paradigme fonctionnel dans une architecture heterogene, en particulier dans la construction du noyau d'un serveur dans un modele clientserveur. Ce modele hybride permet de liberer la programmation fonctionnelle des aspects qui ne sont pas critiques, par exemple les interfaces utilisateur, centrant l'attention du code fonctionnel a la partie du probleme critique en termes de correction. L'interface graphique presentee est un exemple d'un des domaines ou une architecture de ce type peut ^etre interessante. Cette interface est un outil pour l'enseignement du -Calcul qui peut ^etre tres apprecie comme introduction a la programmation fonctionnelle. Nous avons introduit les concepts qui permettent d'automatiser la construction de l'interface entre le noyau fonctionnel et le systeme operatoire (l'interface FL-OS) a l'aide de schemas qui doivent ^etre instancies selon le cas a traiter. Le travail que nous menons actuellement suit les orientations suivantes: L'automatisation de la construction de l'interface FL-OS, a partir des elements denis dans ce papier. L'elaboration d'interfaces graphiques pour des programmes fonctionnels existants, ne disposant pas de ces facilites. 16 Une Interface Graphique Distribuee pour le -Calcul... L'utilisation du modele dans un processus de developpement d'applications distribuees supporte par un langage fonctionnel. 5. Remerciements Nous voulons exprimer notre reconnaissance a M. J.L. Freire Nistal, Doyen de la Faculte d'Informatique de l'Universite de La Coru~na, pour son support a la realisation de ce travail. Ce papier a ete partiellement subventionne par la Xunta de Galicia - XUGA10501B93. References [1] Alfred V. Aho, Ravi Sethi, Jerey D. Ullman. Compilers: Principles, Techniques and Tools. Addison-Wesley Publishing Company, Massachusetts, U.S.A. 1986. [2] Maurice J. Bach. The Design of the UNIX Operating System. PrenticeHall, Englewood Clis, New Jersey, U.S.A. 1986. [3] J.P. Ban^atre, S.B. Jones, D. Le Metayer. Prospects for Functional Programming in Software Engineering Springer-Verlag. 1991. [4] P. Casteran. Construction de programmes C.P.S. sous Coq. Journees francophones des langages applicatifs, pg.171-183. 1994 [5] P.L. Curien. Cathegorical Combinators, Sequential Algorithms and Functional Programming. Research Notes in Theoretical Computer Science Pitman. John Wiley and Sons. 1986. [6] N. De Bruijn. Lambda-Calculus notation with nameless dummies, a tool for automatic formula manipulation. Indag. Math., 1962. [7] Jose L. Freire Nistal. Elementos de programacion funcional y categoras. Va lactea editorial. 1990. [8] J.L. Freire, V.M. Gulas, J.M. Molinelli. Utilizacion de la programacion funcional para la construccion de servidores en entornos heterogeneos. Actes de la Joint Conference on Declarative Programming GULPPRODE'94. Vol.II pp 351-365. 1994. [9] Vctor M. Gulas Fernandez. Interfaz graca para una implementacion del -calculo, construda sobre entornos heterogeneos utilizando una arquitectura cliente-servidor. Projet n de carriere, Universidad de La Coru~na. 1994. [10] Paul Hudak, Philip Wadler. Report on the Functional Programming Language Haskell. Technical Report YALEU/DCS/RR-666, Department of Computer Science, Yale University. 1988 [11] Xavier Leroy, Michel Mauny. The CAML Light system, release 0.5. Documentation and User's Manual. Projet Formel, INRIA Rocquencourt. 1992. 17 V.M. Gulas, A. Valderruten [12] Xavier Leroy. The UNIX Interface for CAML Light. Annexe au manuel de CAML Light release 0.6. Projet Formel, INRIA Rocquencourt. 1994. [13] Richard Stevens. UNIX Network Programming. Prentice-Hall, Englewood Clis, New Jersey, U.S.A. 1990. [14] Andrew S. Tanenbaum Computer Networks. Prentice-Hall, Englewood Clis, New Jersey, U.S.A. 1989. [15] AIDA 1.65 User's Manual ILOG, France. 1992. 18