Inférence de type en Prolog

Pour ce TP vous pouvez travailler avec n'importe quel interpréteur Prolog, par exemple:

  • SWISH-Prolog (recommandé)
    • allez dans le notebook pré-rempli
    • dans le menu File/Save demandez un fork du notebook
    • faites des sauvegardes régulièrement
    • si vous voulez bien passer un compte Google à Swish-Prolog pour vous identifier, vous aurez quelques fonctionalités supplémentaires
  • Tau-Prolog
  • sur les machines du département, avec la commande swipl ou prolog
  • sur vos machines perso, vous pouvez aussi installer gnu-prolog
  • il y a plein d'extensions Visual Code pour faire du Prolog

Le but de ce TP est d'écrire un programme Prolog permettant de vérifier et d'inférer le type d'expressions Caml. On ne regardera qu'un petit fragment de Caml (que l'on appelera "micro Caml"), donné par la grammaire ci-dessous:

<expr> ::= 
    <var>
|   fun <var> -> <expr>
|   <expr> <expr>
|   (<expr>, <expr>)
|   fst
|   snd
|   true
|   false
|   <int>
|   <expr> + <expr>
|   <expr> = <expr>
|   if <expr> then <expr> else <expr>
|   ()
|   if <expr> then <expr>
|   <expr> ; <expr>

avec des types définis par la grammaire

<type> ::=
    <vartype>
|   <type> -> <type>
|   <type> * <type>
|   bool
|   int
|   unit

Étape 1 - le fragment fonctionnel

On se concentre d'abord sur le fragment vu en cours

<expr> ::= 
    <var>
|   fun <var> -> <expr>
|   <expr> <expr>


<type> ::=
    <vartype>
|   <type> -> <type>

On a vu comment on pouvait représenter une expression par un terme Prolog à l'aide des symboles de fonction fun/2 et app/2. On a vu la relation "l'expression $e$ est de type $\tau$ sous l'hypothèse de typage $\Gamma$ pour les variables libres de $e$", que l'on a notée $\Gamma\vdash e:\tau$, et on a vu comment axiomatiser cette relation en Prolog pour certaines expressions $e$ :

% Axiome
type([X, Alpha | _], X, Alpha).

% Affaiblissement
type([X, Alpha | Gamma], E, Tau) :- type(Gamma, E, Tau).

% Intro ->
type(Gamma, fun(X,E), arrow(Tau1,Tau2)) :-
	type([X, Tau1 | Gamma], E, Tau2).

% Elim ->
type(Gamma, app(E1, E2), Tau) :-
    type(Gamma, E1, arrow(Tau2, Tau)),
    type(Gamma, E2, Tau2).

Question 1.1 - vérifier le type d'une expression

Le terme Prolog fun(x,fun(y,app(y, x))) représente l'expression Caml fun x -> fun y -> y x. Trouvez son type avec Caml. Ce type se modélise par le terme Prolog arrow(Alpha,arrow(arrow(Alpha,Beta),Beta). Vérifiez que la requête

?- type([], fun(x,fun(y,app(y, x))), arrow(Alpha,arrow(arrow(Alpha,Beta),Beta))).

termine avec succès (on utilise le programme Prolog pour vérifier un type).

Question 1.2 - inférer le type d'une expression

Quel terme représente l'expression ci-dessous?

(fun x -> x (fun y -> y)) (fun z -> z) (fun t -> t)

À l'aide d'une requête appropriée, calculez son type avec Prolog, et vérifiez le résultat avec Caml (on utilise le programme pour inférer un type).

Dans la suite on fera toujours des tests d'inférence de types, mais n'hésitez pas à faire vos propres tests en vérification de type, en spécifiant le type attendu, comme à la question 1.

Étape 2 - le fragment fonctionnel, en mieux

Si vous ne parvenez pas à faire cette étape, vous pouvez quand même faire les étapes suivantes.

Question 2.1 - unification avec occurs check

Corrigez l'axiomatisation à l'aide du prédicat unify_with_occurs_check/2.

Indication: lisez rapidement ceci, en particulier la partie sur occurs check

Vérifiez qu'après avoir corrigé Prolog ne sait pas typer le terme qui représente l'expression Caml fun x-> x x.

Question 2.2 - analyse du problème avec les variables introduites plusieurs fois

Inférez le type de l'expression

fun x -> fun x -> x x

et observez que, comme mentioné dans le cours, Prolog parvient à typer cette expression, mais pas Caml.

Question 2.3 - correction du problème

Corrigez la règle d'affaiblissement pour ne pas avoir besoin de supposer que l'expression à typer n'introduit jamais deux fois la même variable avec fun. Idée: on n'applique pas la règle d'affaiblissement lorsque la règle d'axiome a échoué parce que les types n'étaient pas unifiables: si on a $\Gamma,x:\tau_1\vdash x:\tau_2$ et qu'on ne parvient pas à unifier $\tau_1$ et $\tau_2$, alors l'expression n'est pas typable, il ne faut pas aller chercher plus loin dans $\Gamma$, puisque s'il y a d'autres déclarations de types pour $x$ dans $\Gamma$, ce sont des $x$ qui sont "masqués" par le $x$ actuel.

Étape 3 - les couples

On s'intéresse maintenant aux expression Caml définies par la grammaire ci-dessous

<expr> ::= 
    <var>
|   fun <var> -> <expr>
|   <expr> <expr>
|   (<expr>, <expr>)
|   fst
|   snd

<type> ::=
    <vartype>
|   <type> -> <type>
|   <type> * <type>

On va modéliser les fonctions prédéfinies Caml fst et snd par des constantes Prolog fst/0 et snd/0, et on utilisera un symbole de fonction couple/2 pour représenter un couple Caml (e1,e2) par le terme Prolog couple(E1, E2). On utilisera enfin le symbole de fonction Prolog product/2 pour représenter le type produit tau1 * tau2 par le terme Prolog product(Tau1, Tau2).

Question 3.1 - axiomatisation des couples

Ajoutez trois règles (ou axiomes) Prolog à la définition de la relation type.

type(Gamma, fst, ...) :- ...
type(Gamma, snd, ...) :- ...
type(Gamma, couple(E1, E2), ...) :- ...

Question 3.2 - test

Testez votre code avec les requêtes suivantes

?- type([],fst, Tau)
% renvoie [Tau <- arrow(product(Alpha1, Alpha2), Alpha1)]

?- type([],snd, Tau)
% renvoie [Tau <- arrow(product(Alpha1, Alpha2), Alpha2)]

?- type([],fun(x,couple(x,x)), Tau)
% renvoie [Tau <- arrow(Alpha,product(Alpha, Alpha)]

?- type([],fun(x,couple(app(snd, x),app(fst, x)), Tau)
% renvoie [Tau <- arrow(product(Alpha1,Alpha2),product(Alpha2, Alpha1)]

Question 3.3 - test avancé

Cherchez plusieurs (au moins deux) expressions Caml (dans la grammaire ci-dessus) qui ne sont pas typables. Vérifiez avec les requêtes Prolog correspondantes que l'expression n'est effectivement pas typable. Vous laisserez les requêtes en commentaire.

Étape 4 - micro Caml

On s'intéresse au fragment de Caml mentionné en début de sujet. On fixe les symboles de fonction suivants en Prolog: true/0, false/0, entier/1, plus/2, egal/2, if/3, if/2, unit/0, seq/2 pour représenter les expressions Caml. Par exemple, l'expression Caml

if x=(1+y) then ( (); true ) else false

sera représentée le terme Prolog

if(egal(x,plus(entier(1),y)),seq(unit,true),false)

De plus, on introduit les symboles de fonction et de constantes Prolog suivants: bool/0, int/0, unit/0 pour représenter les types "de base" de micro Caml.

Question 4.1 - axiomatisation de micro Caml

Étendez l'axiomatisation de la relation type pour prendre en compte ces nouveaux éléments.

Question 4.2 - test

Testez votre code sur les expressions Caml suivantes; vérifiez votre résultat avec Caml, toutes les expression ci-dessous ne sont pas typables, votre code doit inférer le type quand il est inférable, et déclarer l'expression non typable lorsqu'elle n'est pas typable.

fun x -> x=1
fun x -> fun y -> x=y
fun x -> x=x+x
fun x -> fun y -> fun z -> if x then y else z
fun x -> if x then x else true
fun x -> if x then x else 1
fun x -> x ()
fun x -> fun y -> ( x; y )
fun x -> fun y -> if x then ( y (); 1 ) else 2
fun x -> fun y -> if x then y ()
fun x -> if x then x ()