Précédent Index Suivant

6.3   Continuation Passing Style (CPS)

Cette transformation permet d'expliciter le contrôle de l'exécution du programme en donnant à chaque fonction un argument supplémentaire correspondant à la continuation de son calcul. Cette continuation est une fermeture qui s'appliquera au résultat de la fonction. Cette technique a été initialement utilisée pour le compilateur Lisp (Rabbit[]). SML/NJ ([]) l'utilise dans sa dernière version (0.66).

Nous allons l'étudier sur un exemple en utilisant la syntaxe Caml. Soit une fonction count qui prend un prédicat et une liste et retourne le nombre d'éléments de la liste qui vérifient ce prédicat, et une fermeture countZeros qui correspond à l'application partielle de count à un prédicat qui retourne true si son argument vaut 0, et false sinon.

let count pred = 
  let rec f a = if null a 
            then 0 
            else if pred(hd a) 
                 then 1+f(tl a)
                 else f (tl a)
  in f
;;

let countZeros = count (function i -> if i=0 
                                      then true 
                                      else false)
;;
La transformation modifie chaque fonction en lui rajoutant un argument supplémentaire (la continuation) et revient de la fonction en appelant la continuation :
let count (pred,c1) = 
  let rec f (a,c2) = 
    if null a 
    then c2(0) 
    else 
     let c3 b = 
       if b  
       then 
         let  c4(i) =c2(1+i)
         in 
           f(tl(a),c4)
       else
         let c5(i)=c2(i)
         in f(tl(a),c5)
     in 
       pred((hd a),c3)
  in c1(f)
;;

let   countZeros  = ...
;;
Le contrôle de la fonction count est devenu explicite. Ce résultat se prête à plusieurs optimisations. Ici la continuation c5 est inutile car identique à c2 et peut donc être supprimée.
La variable countZeros devient :
let countZeros m = let g i = if i=0 then true else false in count g m
;;
et peut donc se réécrire ainsi :
let countZeros (m,c0) = 
  let g (i,c1) = 
    if i=0 
    then c1(true) 
    else c1(false) 
 in 
    let c3(a) = a
    in 
      let c2(a) = (a (m,c3))
      in
        c0(count(g,c2))
;;
En fait, il aurait été plus astucieux d'expanser dans le corps de countZeros la fonction count car elle est suffisamment petite et non-récursive.

Comme le but de cette transformation est aussi d'aplatir le programme, une phase de conversion des fermetures est utile pour ranger les variables libres dans un enregistrement (premier argument de la fonction), à la manière du l-lifting. Un appel d'une fonction a trois arguments : l'enregistrement des variables libres de la fermeture, les arguments utilisateur, et la continuation.


Précédent Index Suivant