From mboxrd@z Thu Jan 1 00:00:00 1970 Received: (from weis@localhost) by pauillac.inria.fr (8.7.6/8.7.3) id QAA21572 for caml-redistribution; Mon, 2 Jun 1997 16:51:04 +0200 (MET DST) Received: from concorde.inria.fr (concorde.inria.fr [192.93.2.39]) by pauillac.inria.fr (8.7.6/8.7.3) with ESMTP id QAA21018 for ; Mon, 2 Jun 1997 16:15:40 +0200 (MET DST) Received: from pauillac.inria.fr (pauillac.inria.fr [128.93.11.35]) by concorde.inria.fr (8.8.5/8.7.3) with ESMTP id QAA22351; Mon, 2 Jun 1997 16:15:32 +0200 (MET DST) Received: (from weis@localhost) by pauillac.inria.fr (8.7.6/8.7.3) id QAA21013; Mon, 2 Jun 1997 16:15:31 +0200 (MET DST) From: Pierre Weis Message-Id: <199706021415.QAA21013@pauillac.inria.fr> Subject: Re: Instruction return In-Reply-To: <3390602C.20BB@easynet.fr> from Pascal Zimmer at "May 31, 97 10:30:20 am" To: zimmer@easynet.fr (Pascal Zimmer) Date: Mon, 2 Jun 1997 16:15:31 +0200 (MET DST) Cc: caml-list@inria.fr MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Sender: weis > Serait-il possible d'ajouter une instruction return au langage CAML afin > de pouvoir sortir directement d'une boucle, comme c'est le cas en C ? Je > pense qu'une telle instruction ameliorerait beaucoup la lisibilite du > code. Oui c'est possible et c,a ne pose aucun proble`me pour le reste du langage. Il faut seulement se persuader que c'est vraiment ne'cessaire. > Par exemple, cela permettrait de retourner la valeur d'une fonction au > cours d'une boucle for, alors que pour l'instant on doit utiliser une > boucle while avec un compteur sous forme de pointeur. > > Ainsi, une fonction classique de recherche d'une valeur x dans un tableau > non trie v s'ecrit: > > let rech x v = > let n = vect_length v in > let i = ref 0 in > while !i < n & v.(!i) <> x do i := !i + 1 done; > not (!i = n);; > > Avec une instruction return, on pourrait l'ecrire ainsi: > > let rech x v = > let n = vect_length v in > for i=0 to n-1 do > if v.(i) = x then return true > done; false;; > > Cette derniere forme sans pointeur pour le compteur i est tout de meme > plus claire ! Pour sortir d'une boucle il n'est pas besoin d'une telle instruction: il suffit d'utiliser le me'canisme d'exceptions du langage; lorsqu'on veut arre^ter la boucle pre'mature'ment on lance une exception, et l'on prote`ge la boucle a` l'aide d'un try ... with. Il existe d'ailleurs une exception spe'cialement pre'de'finie pour ce faire: l'exception Exit. On e'crit donc d'habitude let rech x v = try let n = vect_length v in for i=0 to n-1 do if v.(i) = x then raise Exit done; false with Exit -> true;; Bien su^r cela ne suffit pas pour rendre la valeur d'une fonction au milieu d'une boucle, sauf a` de'finir une exception spe'cifique a` chaque fonction, sur le mode`le: exception Return of bool;; let rech x v = try let n = vect_length v in for i=0 to n-1 do if v.(i) = x then raise (Return true) done; false with Return b -> b;; C'est e'videmment un peu lourd (et ca ne marche pas pour les fonctions polymorphes). C'est pourquoi on pre'fe`re alors conside'rer que la boucle est trop complexe pour une boucle for ou while et qu'il est temps d'utiliser des outils adapte's a` e'crire des boucles arbitrairement complexes, c'est-a`-dire les fonctions re'cursives. let rech x v = let rec boucle i = if i < vect_length v then if v.(i) = x then true else trouve (i + 1) else false in boucle 0;; ou encore, apre`s un peu de re'e'criture: let rech x v = let rec boucle i = i < vect_length v && (v.(i) = x || boucle (i + 1)) in boucle 0;; Ce qui est encore plus bref (plus simple?) que la boucle for et posse`de l'immense avantage d'e^tre comple`tement ge'ne'ral et de ne pas ne'cessiter d'instruction supple'mentaire. > Bien entendu, le typeur devra verifier que le type renvoye par return est > le meme que celui renvoye par la derniere instruction de la fonction. > Une telle possibilite (renvoi par return ou par la derniere instruction) > est par exemple utilisee par le langage de programmation de MAPLE, sauf > qu'il n'y a aucun controle de type. Il suffit pour cela de typer le corps d'une fonction en e'tendant l'environnement avec une liaison pour l'identificateur return de type 'a -> 'a; on doit enfin unifier 'a avec le type obtenu pour le corps de la fonction. La compilation correspond en gros a` ge'ne'rer un goto. Il n'y a donc pas de re'elle difficulte'. > J'aimerais donc savoir si une telle instruction est possible ou s'il y a > incompatibilite avec le reste du langage. C'est donc possible, et ce n'est pas incompatible, mais est-ce ne'cessaire ? De surcroi^t, il n'est pas certain que cette construction facilite vraiment la lecture, dans la mesure ou` la valeur de retour des fonctions serait alors bien moins facile a` localiser, puisque qu'elle pourrait apparai^tre n'importe ou` dans le corps de la fonction. (conside'rez me^me un programme sans boucles: let f x = let y = if ... then return 1 else 2 in if y > x then x + 1 else y;; (dans certains cas) la valeur de retour de f est obtenue (et rendue) pendant l'e'valuation de l'expression de'finissant y ...) Cela dit l'argument est faible, car il est toujours facile d'e'crire des programmes illisibles, me^me avec les meilleures constructions possibles dans le langage (comme en Caml :). Peut-e^tre avez-vous des exemples encore plus convaincants ? > [English: Is it possible to add a 'return' instruction to the CAML > language, like in C ?] Yes it is possible, but is it necessary ? To escape from loops, use the predefined exception Exit, and enclose the loop inside a try with: let rech x v = try let n = vect_length v in for i=0 to n-1 do if v.(i) = x then raise Exit done; false with Exit -> true;; To return the result of a function from inside a loop, it's a bit more tricky: you must define first an exception that could carry the result (in your case a boolean): exception Return of bool;; Then you enclose the body of the function within a try with construct: let rech x v = try let n = vect_length v in for i=0 to n-1 do if v.(i) = x then raise (Return true) done; false with Return b -> b;; In fact the best way of writing complex loops is via recursive function definitions: let rech x v = let rec boucle i = i < vect_length v && (v.(i) = x || boucle (i + 1)) in boucle 0;; This is even shorter than the version with the return construct, and uses a more general concept that the return construct. Last, the addition of the return construct could be considered harmful, since the returned value of functions could now be easily hidden anywhere in the body of the function. (consider the following program: let f x = let y = if ... then return 1 else 2 in if y > x then x + 1 else y;; the returned value of f is computed and returned during the evaluation of the expression that defines y...) However this is poor, since it is easy to write bad programs, even if you have the best constructs in the language (as in Caml :) Have you other examples that could convince us to adopt the return construct ? Pierre Weis INRIA, Projet Cristal, Pierre.Weis@inria.fr, http://pauillac.inria.fr/~weis/