Continuations avec Scala
Tags: Scala, CPS July 16, 2010

Une des nouveautés de Scala 2.8 est le plugin qui ajoute le support des continuations. Je vous présente un exemple dans la suite de ce post.

L’exemple qui suit est repris depuis ici

Ce code affiche

Essayons de comprendre pourquoi. Le premier code exécuté (la fonction “main”) est :

Ce qui est équivalent à :

Ce qui est équivalent à :

Rappelons qu’un “shift” ne peut être rencontré que dans le contexte d’un “reset”. Lorsqu’un shift est rencontré, une fonction est créée de façon à ce qu’un argument remplace l’appel à “shift” :

Si le type de retour de la fonction peut être inféré de façon classique (“println” donc “Unit”), c’est plus compliqué pour le type de l’argument “arg”. Or cette fonction que j’ai appelée “myCont1” va être passée comme argument à la fonction contenue dans le “shift” qui prend en argument “k : Int => Unit”. Donc le type de “arg” est “Int”.

Maintenant ce qui se passe est simple. La fonction contenue dans “shift” est appelée avec en argument la fonction “myCont1”. Si on regarde le code :

On s’aperçoit que l’argument “k” qui référence “myCont1” est sauvegardé mais jamais utilisé. Comme “producerCont” est null, il ne se passe rien d’autre. Après ça, le code exécuté est celui situé après le “reset”, c’est-à-dire l’autre “reset” :

Le premier “println” affiche “Producing: 1”. Ensuite, la fonction “produce” est appelée. Si on la remplace, comme précédemment, par sa définition, on obtient la fonction suivante :

Cette fonction est passée à la fonction contenue dans le “shift” défini dans la fonction “produce”. Si on regarde le code :

On constate que la fonction “myCont2” est sauvegardée dans “producerCont”. Puis on appelle la fonction “consumerCont”. Rappelez-vous, nous avions sauvegardé dans “consumerCont” la fonction “myCont1”. C’est donc cette dernière qui est exécutée avec pour argument “i” qui est égal à 1. Si on regarde le code de “myCont1”, on voit que “println” est appelé pour afficher “Consuming: 1”.

Le reste de “myCont1” s’exécute et un nouvel appel à “consume” est effectué. Comme précédemment, une fonction “myCont3” sera créée. Elle contiendra la suite de “myCont1”, c’est-à-dire :

Cette fois-ci cependant, “producerCont” ne sera pas “null” puisqu’elle référence “myCont2”. “myCont2” sera donc appelée, permettant ainsi la production d’une nouvelle valeur (“produce(2)”) qui elle-même appellera “consumerCont” qui consommera une valeur (“consume”) et qui elle-même appellera “producerCont”, permettant ainsi la production d’une nouvelle valeur, etc. La condition d’arrêt est quand “producerCont” est appelée alors que toutes les valeurs ont été produites :

Arrivé à ce stade, la suite de la fonction “main” est exécutée et le programme se termine.

Voilà, j’espère que ce petit aperçu vous a montré la puissance de cette construction syntaxique. On a ainsi implémenté des co-routines très facilement.