Planche : 1


Enregistrements, puis objets


Planche : 2


A
Enregistrements
B
Objets
C
Conclusion du cours

Planche : 3


Faire des paquets

dx
dt
 = k x
dy
dt
 = k y
dz
dt
 = k z

Noté

d
u
 
dt
 = k 
u
 
,   avec
u
 
 = 


x
y
z



En mathématiques deux outils,

Et en informatique


Planche : 4


Enregistrement

Une position sur la terre est une paire latitude × longitude.

Une erreur idiote : let x = (2.208, 47.800) au lieu de let x = (47.800, 2.208).

Un enregistrement est d’abord un n-uplet dont les composantes sont nommées,

latitude=47.800; longitude=2.208; }

Le programmeur n’a plus besoin de se souvenir que la latitude vient en premier, il peut écrire :

longitude=2.208; latitude=47.800; }

Cela évite bien des erreurs idiotes et conduit à des programmes plus clairs : x.latitude au lieu de fst x.


Planche : 5


D’abord une commodité

Dans les languages usuels, les enregistrements expriment le produit (cartésien) de façon commode.


Planche : 6


Une commodité supplémentaire

Ajoutons les altitudes aux positions sur la terre.

type pos2 = { latitude:float ; longitude:floatz:float; }

Monter d’un mètre :

let up pos =
  { latitude = pos.latitude ;
    longitude = pos.longitude ;
    z = pos.z +. 1.0 ; }

C’est assez lourd, on peut abréger en :

let up pos = { pos with z = pos.z +. 1.0 }

Planche : 7


Ajouts à la syntaxe abstraite de PCF

Une sorte supplémentaire : les étiquettes (comme des variables, mais ne sont jamais liées).

Des symboles supplémentaires qui fabriquent des termes.

Définition de l’AST de PCF en Caml.

type t = …
Rec of (label * tlist
Access of t * label
CopyWith of t *  (label * tlist (* Généralisé *)

Planche : 8


Fabriquer un enregistrement

Syntaxe concrète :

x=1 ; y=2 ; }

Syntaxe abstraite :

type t = … | Rec of (label * tlist | …

Et une nouvelle règle d’interprétation.

E ⊢ t1v1           ⋯           E ⊢ tnvn
E ⊢ { ℓ1  =  t1 ;  ⋯ ℓn  =  tn;  }↪{ ℓ1  =  v1 ;  ⋯ ℓn  =  vn;  }

Donc une nouvelle valeur (notée { ℓ1  =  v1 ;  ⋯ ℓn  =  vn;  }).

Note : Dans un contexte typé (simple), la valeur d’un enregistrement serait un n-uplet.

La taille n et la position des champs sont définis à partir d’une déclaration de type enregistrement.


Planche : 9


Accéder à un champ

Syntaxe concrète

Let pos = …  In
v
.x + v.y

Syntaxe abstraite :

type t = … | Access of t * label | …
E ⊢ t↪{ … ℓ  =  v ;  … }
E ⊢ t.ℓ↪v

Planche : 10


La copie (avec changement)

Syntaxe concrète

Let pos = …  In
pos With x = 3 }

Syntaxe abstraite :

type t = … | CopyWith t * label * t | …
E ⊢ t1↪{ … ℓ  =  v1 ;  … }           E ⊢ t2v2
E ⊢ {t1 With ℓ  =  t2}↪{ … ℓ  =  v2 ;  … }

Planche : 11


Définition par « sucre syntaxique »

Sur le transparent précédent nous avons « défini »

{t With ℓ1  =  t1 ;  ⋯ ℓn  =  tn;  }
def
=
 
     { ⋯ {t With ℓ1  =  t1} ⋯  With ℓn  =  tn}

« Sucre syntaxique » veut dire qu’il n’y pas de nœud correspondant à la construction définie dans l’AST de PCF.

La nouvelle construction existe slt. dans la syntaxe concrète.

Par abus de langage, on peut considérer un AST généralisé.

type t = … | CopyWith t * (label * tlist | …

Et que la sémantique de la nouvelle construction se déduit facilement (ici la nouvelle règle est lourde).


Planche : 12


Une restriction injustifiée ?

En PCF le code suivant est pour le moment interdit :

{ { x=1 } With y=2 }

Pourquoi ? À cause de la sémantique de CopyWith, (justifiée par l’implémentation efficace dans le cas typé.)

Mais, pour PCF non-typé, on ajoute facilement la règle :

E ⊢ t1r           r = { ⋯ }           ℓ ∉L(r)           E ⊢ t2v2
E ⊢ {t1 With ℓ  =  t2}↪{ ⋯ ℓ=v2 }

Avec la notation L({ ℓ1  =  v1 ;  ⋯ ℓn  =  vn;  }) = { ℓ1, … , ℓn }.


Planche : 13


Encore un peu plus loin

Dans le cadre non-typé, l’accès t.ℓ réussit dès que t s’évalue en un enregistrement qui possède le champ ℓ.

E ⊢ t↪{ … ℓ  =  v ;  … }
E ⊢ t.ℓ↪v

Comment transporter cette bonne propriétée à PCF typé :

type pos2 = { x:floaty:float; }
  …
type pos3 = { x:floaty:floatx:float; }
  …
let getx p = p.x (* getx s'applique à pos2 et pos3 ? *)

Il faut une forme de sous-typage.

Genre getx prend un p qui comprend au moins le champ x (type pos = {x:float;...}) et pos2 et pos3 sont des sous-type pos.


Planche : 14


Le point sur le typage des enregistrements


Planche : 15


Détour — Appel par nom

Le principe général est d’évaluer aussi tard que possible.

Les règles sont donc logiquement.

E ⊢ { ℓ1  =  t1 ;  ⋯ ℓn  =  tn;  }
{ ℓ1  =  
<
<
t1E>
>
 ;  ⋯ ℓn  =  
<
<
tnE>
>
;  }
E ⊢ t
{ … ℓ  =  
<
<
t1E1>
>
 ;  … }
          E1 ⊢ t1v
E ⊢ t.ℓ↪v

Il y a peu à dire en fait.

Let x = { a = fact 10 } In
x
.a + x.a

(fact 10 évalué deux fois en appel par nom.)


Planche : 16


Programmation objet

Une petite entreprise X.

D’une part un salarié à payer.

let travailleur = { nom = "luc" ; salaire = … }

D’autre part des commandes.

let commande = { quoi = "feutre" ; combien=1 ; prix=… }

Éditer des fiches de paie/des bons de commande


Planche : 17


En « PCF»

Rien n’empêche d’ajouter un champ « imprimer », à la fois aux salariés et aux commandes :

let travailleur = {
  … ;
  imprimer = fun w ->
    Printf.printf "%s: %i\n" w.nom w.salaire ;
}

let commande = {
  … ;
  imprimer =
    fun o ->
      Printf.printf "%s: %i X %i\n" o.quoi o.combien o.prix ;
}

Et pour imprimer que x soit un salarié ou une commande :

x.imprimer x

Planche : 18


La même chose en pur PCF

Let tortue = { x=0 ; step=Fun t -> { t With x = t.x+1 } }
Let lapin = { x=0 ; step=Fun l -> { l With x = l.x+5 } }

Faire avancer un animal :

Let go = Fun a -> a.step a

Et on a (avec un toplevel approprié).

PCF> go lapin;;
 - = { x=5 ; step=<fun> }
PCF> go tortue;;
 - = { x=1 ; step=<fun> }
PCF> go (go lapin) ;;
 - = { x=10 ; step=<fun> }

Planche : 19


Systématiser

Objet : enregistrement dont tous les champs sont des méthodes.

Méthode :

Notre tortue n’est pas encore tout à fait un objet :

Let tortue = { x=0 ; step=Fun t -> { t With x = t.x+1 } }

Mais elle possède bien une méthode step.


Planche : 20


Aider à pratiquer le style « objet »

Créer un objet :

Obj x -> { ℓ1  =  t1 ;  ⋯ ℓn  =  tn;  }{ ℓ1  =  Fun x -> t1 ;  ⋯ ℓn  =  Fun x -> tn;  }

On peut voir Obj comme « sucre syntaxique » (i.e. existe slt dans la syntaxe concrète).

Ou comme nouvelle construction de la syntaxe abstraite, dont la sémantique est :

E ⊢ Obj x -> { … ℓi  =  ti ;  … }↪{ … ℓi  =  <xtiE> ;  … }

Invoquer une méthode :

t#ℓ ∼ Let o = t In o.ℓ o

Éventuellement, sémantique :

E ⊢ to       o={ … ℓ  =  <xt1E1> ;  … }       E1 ⊕ [x = o] ⊢ t1v
E ⊢ t#ℓ↪v

Redéfinition (Ajout) de méthode

Obj x -> {t1 With ℓ  =  t2}{t1 With ℓ  =  Fun x -> t2}

Éventuellement, sémantique (assez évidente).

E ⊢ t1↪{ … ℓ  =  ? ;  … }
E ⊢ Obj x -> {t1 With ℓ  =  t2}↪{ … ℓ  =  <xt2E> ;  … }
E ⊢ t1r           r = { ⋯ }           ℓ ∉L(r)
E ⊢ Obj x -> {t1 With ℓ  =  t2}↪{ ⋯ ℓ=<xt2E> }

Planche : 21


Obliger à l’objet

Supprimer les constructions des enregistrements de la syntaxe (mais conserver les enregistrements dans la sémantique).

Mais alors comment écrire la tortue « objet ». Facile ?

Let tortue = Obj self {
   x = 0 ;
   step = Obj s { self With x = self#x+1 } ;
}

Qui veut dire :

Let tortue = {
   x = Fun self -> 0 ;
   step = Fun self -> { self With x = Fun s -> self#x+1 } ;
}

On note que dans un language comme Java, self est implicite et s’appelle this.


Planche : 22


Essai (de la tortue)

PCF> let t = tortue#step;;
 val t = { x=<fun> ; step=<fun> }
PCF> t#x;;
 - = 1

Qui s’explique, puisque t est

x= <sself#x+1E> ; ⋯ }

E = [ self = { x=<self0E0> ; ⋯} ; …]

Ou si on préfère : t#x est « Fun s -> self#x+1 » pris dans un environnement où self est un objet dont la méthode x vaut Fun _ -> 0

Et, miracle, tt = t#step vaut bien 2.

Car tt#x est « Fun s -> self#x+1 » pris dans un environnement où self vaut t (et donc self#x renvoie 1).


Planche : 23


PCF purement objet

Est quand même un peu trop radical, il devient plus raisonable si on considère des constructions impératives :

Let tortue =
  Let x = Ref 0 In
  Obj self { x = !x ; step = x := !x+1 ; }

(Possible car la variable x et la méthode x habitent deux espaces de noms différents).

Mais en fait, sans champs mutable cela reste assez bizarre.

Nous en restons là, car PCF objet est quand même exemplaire de la programmation objet.


Planche : 24


Liaison tardive

Comparer

Let a = 4 In
Let
 f = Fun y -> y+a In
Let
 a = 5 In
f
 6

Résultat : 10 (liaison statique). Et

Let o = Obj self {
  a = 4 ;
  f = Fun y -> y + self#a ;
In
Obj
 s { o With a = 5 }#f 6

Résultat : 11 (liason tardive de self).


Planche : 25


Méthodes abstraites

En fait la méthode a n’a pas besoin d’exister quand on définit f.

Let o = Obj self {
  f = Fun y -> y + self#a ;
In
Obj
 s { o With a = 5 }#f 6

Évidemment, dans un contexte typé, il faudra sans doute déclarer a.


Planche : 26


Objets de Java

En Java les objets ne sont pas définis directement mais en instanciant des classes, par ex.

abstract class Animal {
  int x = 0 ;
  int getx() { return this.x ; }
  abstract void step() ;
  static void go(Animal a) { a.step() ; }
}

Et par exemple :

class Tortue extends Animal {
  void step () { this.x = this.x+1 ; }
}
class Lapin extends Animal {  void step () { x = x+5 ; } }

L’ajout/redéfinition de méthode opère donc plus sur les classes que sur les objets.


Planche : 27


Java la suite

Un objet de la classe Animal est représenté en machine disons comme une paire :

Il y a une table des champs par objet et une table des méthodes par classe.

go n’a pas besoin de savoir si a est une Tortue ou un Lapin, il lui suffit de savoir que step est en seconde position.

La methode step est une fonction qui prend un premier argument implicite (paramètre formel this), qui vaut a dans l’invocation a.step().


Planche : 28


Approcher l’héritage de Java

En java on peut écrire :

class TortueRapide extends Tortue {
  void step() { super.step() ; super.step() ; }
}

Et on obtient une tortue deux fois plus rapide.

En PCF objet c’est nettement plus tordu :

Let tortue_rapide =
  Let tortue_step = tortue.step In
  Obj this
    { tortue With step = tortue_step (tortue_step this) }

On peut donc conjecturer qu’en Java super.step appelle la méthode step de la classe parent sur… this.


Planche : 29


On a vu…


Planche : 30


On a pas tout vu !

Nous avons laissé de côté bien des sujets :


Planche : 31


La suite


Planche : 32


Et en attendant


Ce document a été traduit de LATEX par HEVEA