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.