Les Types et les Classes

Types

Quelques types simples bornés : (), Bool, Char, Int, Float, Double. Un type simple non borné : Integer. On utilise les procédés de constructions que sont la liste [] , la fonction ->, le couple (,), le triplet (,,), etc..., pour construire d'autres types. On peut préciser le typage d'une expression à l'aide de l'opérateur :: comme suit

'a' :: Char
succ :: Integer->Integer
[1,2,3] :: [Integer]
('b',4) :: (Char, Integer)
"abcd" :: [Char]

La signature d'une variable consiste à préciser son type. Par exemple :

x :: (Integer, [Integer])
x = (2 , [1,2])

La variable ne peut être signée qu'une seul fois. Cette signature signifie que x est un couple composé d'un entier et d'une liste d'entiers. La définition construit la donnée structurée

Types polymorphes

Les variables de types universellement quantifié (représenté ici en rouge), et les 3 procédés de construction de type que sont la liste [], la Fonction -> et le tuple qui se décline en le couple (,), le triplet (,,) ,etc..., permettent de définir des types polymorphes. Ces types polymorphes représentent des familles de types, et forment une arborescence de familles de types emboitées selon la relation d'inclusion.

a représente la famille de type la plus générale, c'est une variable de type sur laquel ne pèse aucune condition. Nous pourrions l'écrire b car son nom n'a pas d'importance, ce qui compte est qu'il n'y a aucune condition sur b.

Type polymorphe

Exemple de Type
Exemple de donnée
Liste
[a]
[Int]
[1,2,3]
Fonctions
a -> b
Int -> Int
\x->x*x
Couple
(a,b)
(Int, Int)
(1,2)
Triplet
(a,b,c)
(Int, Int, Int)
(1,2,3)

le type [Int] est un type inclus dans le type polymorphe [a] , le type Int->Int est inclus dans a->a. Notez que [2, 'k'] n'est pas une donnée valide de type polymorphe [a], car il n'existe pas de type contenant à la fois 2 et 'e'.

Un type seul ne possède pas de constructeur apriorie, mais on peut néanmoins utiliser une définition récurcive.

x :: a
x = x

La variable x est alors une boucle sans fin qui se lance lorsque on tente de l'évaluer. Par contre des définitions de la forme y = (y,y) ou y = [y] ne sont pas possible, on utilisera pour cela d'autres types dits récurcifs.

Chaque expression d'une donnée possède un type principal. C'est le type le moins générale qui couvre toutes les expressions possibles de ses constructeurs. Ainsi l'élément (x,[x]) avec la définition de x précédente, possède comme type principal (a,[b]). L'existence d'un type principal unique est une caractéristique du système de typage Hindley-Milner qui est à la base du système de typage de Haskell, ML, Miranda et de plusieurs autres langages (principalemement fonctionnels).

Tuples

Un tuple est un couple ou un triplet ou un n-uplet d'éléments quelconques entre parenthèse. Le constructeur de couple est (,). Le constructeur de triplet est (,,). Etc... Et le constructeur du tuple vide est ().

Un tuple de taille 1 n'est pas un tuple. L'expression (x) est identique à x. Les parenthèses sans virgules servent à prioriser une opération comme dans l'exemple (2+3)*5. Un tuple de taille zéro correspond à un élément particulier noté () qui possède le type noté (). L'élément [(),x] avec la définition de x précédente, est de type [()].

Listes

Les listes dans Haskell sont composés d'éléments de même type. Il s'en suit qu'une expression [x,y,z] peut ne pas être valide si les types de x, y et z sont incompatibles, ils peuvent néanmoins être plus ou moins généraux, ainsi en reprenant la définition de x précédente, [x,[x]] aura le type [[a]] et [x,()] aura le type [()]

La liste est composée d'éléments de même type, la liste vide [] possède le type [a]

Dans Haskell, la liste [1,2,3] est strictement identique à la liste 1:(2:(3:[])), où [] est la liste vide et : est l'opérateur infixe qui ajoute un élément au début d'une liste. 5:[1,2,3] est identique à [5,1,2,3]. L'opérateur : et la constante [] sont respectivement l'équivalent de cons et nil dans Lisp. L'opérateur : est conventionnellement évalué par la droite. Aussi nous pouvons écrire cette liste par 1:2:3:[]

L'opérateur : et la constante [] sont deux constructeurs de données.

Le Lien

Haskell n'a pas d'opérateur d'affectation. Les variables sont définies par des équations, leurs valeurs sont immuables et l'ordre dans lesquelles sont posées ces équations n'a donc pas d'importance.

Une définition se présente comme une équation. Le terme gauche de l'équation constitue la tête et contient le motif qui doit correspondre avec la donnée construite dans la partie droite de l'équation. La tête doit n'être composée que de constructeurs et que de nouvelles variables non encore définies et distinctes.

x:s = [1,2,3]             x est défini égal à 1 et y égale à [2,3]
(u,v,5) = (x,s,5)         u est défini égal à 1 et v égale à [2,3]
(w,5) = (1,2)             u est de type Integer, et toute tantative d'évaluer u génère une erreur.
(y,_) = (x,s)             y est défini égal à 1, le symbôle _ est un joker qui peut être remplacé par ce que l'on veut.

Si une variable est présente à la fois dans la tête et le corps et de façon non masqué par un joker _, les variables définie par l'équation sont alors toutes des boucles sans fin.

Une variable peuvt avoir une signature qui détermine son type. Les constructeurs utilisés pour construire la donnée contenue dans la variable doivent toujours être reconnue par son type. Une liste vide peut posséder un type plus sophistiqué comme par exemple :

y :: [([(a,b)],[a])]
y = [([],[])]

...

Fonctions polymorphes

Différentes méthodes de construction de fonction :

f x

 

 

 

Les types polymorphes définie par constructeurs

constructions de type sont la liste [], la fonction ->, le couple (_,_), le triplet (_,_,_), etc...

Les types définie par constructeurs

Type défini par l'utilisateur à l'aide d'une déclaration data : le mot clef data suivi d'un nouveau constructeur de type choisie par l'utilisateur, suivie par aucune, une ou plusieurs variables de type : a, ou a b, ou a b c,...

data P a b = C a | D (a,b) | E a a | F[a] | G
  deriving (Eq,Ord,Show,Read)

Dans cette exemple P est un constructeur de type, et C, D, E, F, G sont des constructeurs de données. Les constructeurs de type sont évalués lors de la compilation, alors que les constructeurs de données sont évaluée lors de l'exécution. Le constructeur C appliqué à une donnés de type a, construit une donnée structuré de type P a b.


x = C 5
y = E 2 3

y = D (2,'q')
z = F [1,3,5,7]
t = G

x, y, z, t :: P Integer Char

Le type P Integer Char est une spécialisation du type P a b

x<y => True

K est un constructeur, c'est une fonction qui appliqué à une donnée de type a construit la donnée de type P a.

x = K 5 6

f x = K x (x+1)
g x y = K y x
h = K

f, g et h sont des fonctions. Le constructeur K est une fonction de base qui va construire la donnée structurée, les autres ne sont pas de base, elles ne font qu'appeler le constructeur K.

on peut définir la façon dont le système va afficher l'élément :

instance Show a => Show (P a) where
  show x = "P(" ++ (show (px x)) ++ "," ++ (show (py x)) ++ ")"

data P a = CP (a,a)
  deriving (Eq,Ord,Show,Read)

Les types récurcifs

Exemple du type des graphes composé de sommet de type a

data Graphe a = G a [G a]
  deriving (Eq,Ord,Show,Read)

 

 

 

 

 

 

(.) :: (b -> c) -> (a -> b) -> a -> c

Mots réservés : case, class, data, default, deriving, do, else, if, import, in, infix, infixl, infixr, instance, let, module, newtype, of, then, type, where, _

Opérateur réservés : .. : :: = \ | <- -> @ ˜ =>

varid    nom de variable : x, y, xA, x2, x', x''3, _A
conid    nom de constructeur : A, B, Ax, A2, A', A''3


varsym nom d'opérateur variable : commence par un symbôle parmis ! # $ % & * + . / < = > ? @ \ ^ | - ~ et suivi de tous unicode symbôle de ponctuation autre que _ : " '
consym nom d'opérateur constructeur : De même mais commençant par :

varid   varriable   x, y, xA, x2, x', x''3, _A
conid   constructeur  A, B, Ax, A2, A', A''3
tyvar   variable de type a,b,c,d,a1,a2
tycon Constructeur de type A, B, Ax, A2, A', A''3
tycls Classe de type
modid Module

Md1.Md2.+
Md1.Md2.x
Md1.Md2.C

error "abcdef"
undefined

x op y == (op) x y
-x == negate(x)
(+) x y = x + y
f x y == x `f` y
(+a) == \ x->x+a
(a+) == \ x->a+x

infix (non associatif)
infixl (associatif left)
infixr (associatif rigth)

infixr 5 `op1`, `op2`, +\
op1 x y = x*y
(+\) x y = x+y
x /+ y = [x,y,x]

\p1 p2 p3 -> e
\x1 x2 x3 -> case (x1,x2,x3) of (p1,p2,p3) -> e

if e1 then e2 else e3 == case e1 of { True ->e2; False ->e3}

[1,2,3] == 1:(2:(3:[]))
[1,2,3] ==1:2:3:[]

[x..]   == enumFrom x
[x,y..]   == enumFromThen x y
[x..z]   == enumFromTo x z
[x,y..z]   == enumFromThenTo x y z

[x | s<-[[1,2],[3,4],[5,6,7]], x<-s, (even x) && (lenght s ==2)]    => [2,4]

let {p1=e1; p2=e2) in e0    ==    let (~p1,~p2)=(e1,e2)  in e0
let p = e1 in e0   ==   case e1 of ~p -> e0 (où pas de variable libre p n'apparait dans e1)
let p = e1 in e0   ==   let p = fix(\~p -> e1) in e0

case e of { (x,8)->x ; (x,9) |True -> x | (5,x) | x>5 -> 40| otherwise ->10}

do ?

data S = S1 { x :: Int } | S2 { x :: Int } -- OK
data T = T1 { y :: Int } | T2 { y :: Bool } -- BAD

data T a = A {x,y :: a} |B {x :: a, z,t:: Char} deriving(Eq,Ord,Show,Read)

A 5 6
A{y=6,x=5}
A{y=6}{x=5}
e{x=7}   == case e of {A _ t -> A 7 y; B _ z t ->B 7 z t}

f xs@(x:s) = (xs,x,s)

(\ ~x->0) undefined => 0
(\ ~[x]->0) [] => 0

 

 

 

Bool deriving (Read, Show, Eq, Ord, Enum, Bounded) && || not
Char deriving (Read, Show, Eq, Ord, Enum, Bounded) toEnum, fromEnum
[a] deriving (Read, Show, Eq, Ord, Monad, Functor, MonadPlus) [] :
(a,b) deriving Eq, Ord, Bounded,Read, and Show (,) x y => (x,y)     (,) Bool Int => (Bool,Int) (2-tuples): fst, snd, curry, and uncurry
() deriving (Eq, Ord, Bounded, Enum, Read, Show)
Type de fonction : id, const, (.), flip, ($), until.