Le XXIème siècle sera typé ou ne sera pas

ou Comment j'ai appris à aimer les types

Arnaud Bailly - @dr_c0d3

September 24, 2019

Plan


Introduction

Pourquoi ?

Objectif

Motivation

Explication


Fonctions pures

  • Des fonctions sans effets de bord
  • De préférences totales…
  • De première classe dans le langage…
  • Qui sont interprétées par le runtime qui encapsule l’ensemble des effets

Fonctions pures

selectEntries : (Account -> Bool) -> Vect k Entries -> Balance

isLeapYear : Year -> Bool

toDate : String -> String -> String -> Either String Date

Types de données algébriques

  • Décrit les données comme engendrées par une structure algébrique
  • Types construits par composition au moyen d’opérateurs union et produit
  • Un type est une fonction dans le domaine des types, un constructeur de types
  • Permet au compilateur de “garantir” la totalité d’une fonction

Types de données algébriques

data Balance : Type where
  Zero : Balance

  Bal : (n : Amount) -> (d : Direction) -> Balance

Types de données algébriques récursifs

data List a = Nil | Cons a (List a)

Interfaces

  • Interface offerte par un type de données
  • Permet d’encapsuler l’implémentation concrète
  • Peut exprimer des relations complexes entre plusieurs types de données
  • Plus que des interfaces à la java

Interfaces

interface ToSExp a where
  toSExp : a -> SExp

ToSExp SExp where
  toSExp = id

ToSExp Unit where
  toSExp () = SList []

Familles de types

  • Relation entre types
  • Rend le domaine d’une fonction dépendant du codomaine
  • Peut être ouvert (relation extensible) ou fermé (le codomaine est fini)


type family Answer q = a | a -> q where
  Answer QCM          = Int
  Answer Grade        = Double
  Answer OpenQuestion = Text

En Haskell, les familles des types sont une construction spéciales permettant de définir des relations entre types


Factors : UnitType -> Type
Factors Armored       = StdFactors
Factors Artillery     = Arty
Factors AntiTank      = Pak
Factors HQ            = Arty
Factors SupplyColumn  = Unit
  • En Idris, les fonctions peuvent prendre des types et en retourner!
  • la frontière entre types et valeurs est perméable, au moins syntaxiquement

Types existentiels

  • Encapsuler un ensemble de types dans un autre type de données
  • Renforce la distinction entre type abstrait et concret en rendant l’implémentation inaccessible
  • Garantit la localité des opérations sur un type: la variable de type est nécessairement locale

Types existentiels

data CmdREPL : (segment : GameSegment) -> Type where
  Cmd : (cmd : Command segment) -> CmdREPL segment
  Qry : (ToSExp res) => (qry : Query res) -> CmdREPL segment

On cache le type exact de res en n’exposant que son interface (ToSExp)


data BookOfAccounts : Type where
  BookTransactions : (txs : Vect k Transaction) ->
                     BookOfAccounts

On cache le paramètre de type k qui indique la longueur du vecteur ce qui le rend inaccessible de “l’extérieur”

GADTs

  • Applicable aux types paramétrés
  • Chaque constructeur d’un type peut retourner une type différent
  • Permet de spécialiser le paramètre en fonction du contexte dans lequel le type est appelé à s’insérer

GADTs

data Query : (result : Type) -> Type where
  SupplyPath : (unitName : String)
             -> Query (Either QueryError (List Pos))

  TerrainMap : Query Map

En pattern-matchant sur le constructeur, on sait quel est le type associé result ce qui permet de traiter ce résultat

Hole-Driven Development

  • Utiliser l’inférence de type pour compléter le code par la structure attendue par le compilateur
  • Permet de s’assurer dés le départ qu’une fonction est totale ou a minima que tous les cas en input sont couverts
  • Permet à l’éditeur de compléter automatiquement le code
  • Guide l’implémentation et parfois permet de la déduire automatiquement

Hole-Driven Development

Eq Balance where
  (==) b b' = ?hole

Pour définir l’égalité entre 2 Balance on part d’une équation minimale


- + Accounting.Core.hole [P]
 `--                     b : Balance
                        b' : Balance
     --------------
      Accounting.Core.hole : Bool
  • Au chargement le compilateur infère le type du “trou” en fonction du contexte dans lequel il s’insère

  • On peut ensuite remplir le trou petit à petit avec les différents cas

Types dépendants

  • Faire tomber la barrière entre les types et les valeurs
  • Un type peut être définit par une fonction dépendant de la valeur d’un paramètre
  • Introduit une hiérarchie potentiellement infinie de types (quel est le type de Type ?)
  • Le vérificateur de types utilise la même sémantique que le runtime

Types dépendants

record GameUnit where
  constructor MkGameUnit
  nation : Nation
  unitType : UnitType
  name : String
  move : Nat
  hit : Bool
  combat : Factors unitType

Le type de combat est une fonction de la valeur de unitType

Type égalité

  • Exprimer l’égalité entre deux types comme un type
  • Le type devient une assertion logique, une proposition qui doit être vérifiée par l’implémentation
  • La seule valeur qui habite ce type est Refl qui est donc une preuve que l’égalité est vérifiée

Type égalité

neighbours1_test : (neighbours (Hex 3 3) = [ Hex 2 3, Hex 3 2
                                           , Hex 4 3, Hex 4 4
                                           , Hex 3 4, Hex 2 4] )
neighbours1_test = Refl
  • les types égalité peuvent servir à définir des “tests” exécutés par le compilateur
  • en combinaison avec les “Trous”, cela permet de faire du TDD dans le système de types!

Type égalité

data Entries : Type where
  MkEntries : (entries : Vect n Entry) ->
              { auto need2Entries : LTE 2 n } ->
              { auto balanced : balance entries = Zero } -> Entries
  • ils servent surtout à exprimer des propriétés, des assertions qui sont vérifiées par le compilateur

Types = Proposition

  • Mise en oeuvre concrète de Curry-Howard
  • Les types sont des propositions, les programmes des preuves
  • Écrire un programme c’est démontrer que les types sont habités, que l’on peut effectivement construire des valeurs des types donnés
  • De la conception de logiciels considérée comme l’énonciation de théorèmes…

Types = Proposition

notPosZIsNotAbsZ : ((y = Pos 0) -> Void) -> ((absZ y = 0) -> Void)
notPosZIsNotAbsZ = contrapositive absZIsPosZ

Proof that if a number is not 0, its absolute value is not 0 This is the contrapositive proof to absZIsPosZ @y: a Relative number which is not 0

Types = Proposition

contrapositive : (a -> b) -> (Not b -> Not a)
contrapositive = flip (.)

Exprime la contraposée d’une proposition: Si a implique b, alors non b implique non a

Types = Proposition

absZIsPosZ : (absZ y = 0) -> (y = Pos 0)
absZIsPosZ {y = (Pos Z)}     Refl = Refl
absZIsPosZ {y = (Pos (S _))} Refl impossible
absZIsPosZ {y = (NegS _)}    Refl impossible

Proof that if the absolute value of a number is 0, then this number is 0 @y: a relative number which we take absolute value of

Conclusion


Conclusion

Pub

Colophon

(Quelques) Références

(Quelques) Références

Credits

Credits

Feedback

Questionnaire

https://forms.gle/C1h63ymmoqdceeme7