← Projets

Évaluateur-Typeur de Lambda-Calcul

Sorbonne UniversitéTypage et Analyse Statique (TAS)Oct. — Nov. 2024

Implémentation d'un évaluateur et typeur pour le lambda-calcul en Scala, couvrant la représentation des termes par AST, l'alpha-conversion, la substitution, la réduction en stratégie Left-to-Right Call-by-Value (LtR-CbV) et l'inférence de types par génération d'équations et unification. Le projet inclut également des extensions vers les traits impératifs (références mutables) et le polymorphisme faible.

Architecture du projet

Le code est organisé en modules distincts reflétant les étapes du pipeline d'évaluation :

  • astPTerm (termes : Var, Abs, App, Let, IfZero…) et PType (types : TypeArr, RefType, ListType, ForAll…) via sealed trait pour une extensibilité contrôlée.
  • utilsAlphaConverter (renommage des variables liées avec compteur global), PrettyPrinterTerm et PrettyPrinterType (affichage lisible), TypeUtils.
  • evalSubstitution, LtRCbVReducer, Normalizer (réduction jusqu'à forme normale ou détection de divergence via timeout).
  • typesTypeInference (génération d'équations de typage), Unification (résolution), TypeVarGenerator.

Composantes implémentées

Alpha-conversion et substitution

L'alpha-conversion utilise un compteur global pour garantir des noms de variables uniques, évitant les captures lors des substitutions. La substitution gère explicitement les variables liées et libres — les cas complexes d'imbrication dans Abs et Let ont causé des échecs dans certains tests, identifiés comme dues à une gestion insuffisante des conflits de noms en contexte multi-niveaux.

Réduction LtR-CbV et normalisation

La réduction suit la stratégie Left-to-Right Call-by-Value : les arguments sont évalués avant application. La normalisation itère la réduction jusqu'à atteindre une forme normale ou déclencher un timeout (détection de divergence). Les termes simples passent tous les tests ; les termes combinant Ref, Assign et Deref échouent partiellement en raison d'une synchronisation inadéquate de l'état mémoire mutable entre opérations.

Inférence de types par unification

L'inférence repose sur la génération d'équations de typage suivie de leur résolution par unification (cas explicites pour TypeArr, ListType, RefType). C'est la partie la plus défaillante du projet : 12 tests sur 14 échouent, principalement à cause d'un ordre incorrect dans les équations d'unification et d'une gestion incomplète de ForAll et WeakType (polymorphisme faible / non-expansivité).

Bilan et retours d'expérience

ComposanteRésultat
AST des termes (Var, Abs, App)Réussi
Pretty printersRéussi
Alpha-conversionRéussi
SubstitutionRéussi
Réduction LtR-CbVPartiel — échecs sur termes complexes
NormalisationPartiel — fonctionne pour cas simples
Inférence de typesÉchec — 12/14 tests échouent
UnificationÉchec — 4/9 tests échouent
Polymorphisme faible (WeakType)Non implémenté
Traits impératifs (Ref, Deref, Assign)Partiel

Deux décisions ont pesé sur le résultat final :

  • Migration OCaml → Scala en milieu de projet — motivée par l'envie d'explorer Scala, cette décision a introduit un coût d'adaptation non négligeable sur les modules d'inférence et d'unification, pour lesquels OCaml aurait été plus naturel.
  • Absence de TDD — sans tests écrits en amont, la correction d'un module entraînait fréquemment des régressions dans d'autres, particulièrement visible entre la réduction LtR-CbV et l'inférence de types.
4 modulesast · utils · eval · types
LtR-CbVstratégie de réduction
Unificationinférence de types

Stack

ScalaScala
SBT

Encadrant

Romain Demangeon

Maître de conférences — Sorbonne Université / LIP6