1.9 Impression des types et des erreurs
Comme les variables de types sont codées à l'aide d'entiers, il serait particulièrement
désagréable d'avoir un affichage de types dépendant de cette numérotation.
Pour cela, pour chaque expression globale est crée une liste d'association des numéros de variables
de types et des symboles pour les types polymorphes.
Les constantes de types sont affichées directement par la fonction print_consttype
:
# let
print_consttype
=
function
Int_type
->
print_string
"int"
|
Float_type
->
print_string
"float"
|
String_type
->
print_string
"string"
|
Bool_type
->
print_string
"bool"
|
Unit_type
->
print_string
"unit"
;;
val print_consttype : consttype -> unit = <fun>
La fonction var_name
retourne une chaîne de caractères unique pour chaque entier
passé en argument :
# let
string_of_char
c
=
let
s
=
" "
in
s.[
0
]
<-
c;
s;;
val string_of_char : char -> string = <fun>
# let
var_name
n
=
let
rec
name_of
n
=
let
q,
r
=
((n
/
2
6
),
(n
mod
2
6
))
in
if
q=
0
then
string_of_char(Char.chr
(9
6
+
r))
else
(name_of
q)^
(string_of_char(Char.chr
(9
6
+
r)))
in
"'"
^
(name_of
n);;
val var_name : int -> string = <fun>
La fonction principale est l'impression d'un schéma de type. Le seul problème est
de trouver le nom d'une variable de types. la fonction locale names_of
retourne
les variables quantifiées, chacune associée à une chaîne de caractères particulière.
Si une variable de type n'est pas donc cet ensemble une exception est déclenchée, car il ne peut avoir
d'expression globale contenant des variables de type non quantifiées.
# let
print_quantified_type
(Forall
(gv,
t))
=
let
names
=
let
rec
names_of
=
function
(n,[]
)
->
[]
|
(n,
(v1::lv))
->
(var_name
n)::(names_of
(n+
1
,
lv))
in
(names_of
(1
,
gv))
in
let
var_names
=
List.combine
(List.rev
gv)
names
in
let
rec
print_rec
=
function
Var_type
{contents=
(Instanciated
t)}
->
print_rec
t
|
Var_type
{contents=
(Unknown
n)}
->
let
name
=
(
try
List.assoc
n
var_names
with
Not_found
->
raise
(Failure
"Non quantified variable in type"
))
in
print_string
name
|
Const_type
ct
->
print_consttype
ct
|
Pair_type(t1,
t2)
->
print_string
"("
;
print_rec
t1;
print_string
" * "
;
print_rec
t2;
print_string
")"
|
List_type
t
->
print_string
"(("
;
print_rec
t;
print_string
") list)"
|
Fun_type(t1,
t2)
->
print_string
"("
;
print_rec
t1;
print_string
" -> "
;
print_rec
t2;
print_string
")"
in
print_rec
t;;
val print_quantified_type : quantified_type -> unit = <fun>
L'impression d'un type simple utilise l'impression des chémas de type en créant pour
l'occasion une quantification sur toutes les variables libres du type.
# let
print_type
t
=
print_quantified_type
(Forall(free_vars_of_type
([],
t),
t));;
val print_type : ml_type -> unit = <fun>
Il est fort intéressant d'afficher les causes d'un échec de typage, comme
par exemple les types mis en cause lors d'une erreur de typage ou le nom
d'une variable indéfinie.
La fonction typing_handler
reçoit en argument une fonction de typage, un environnement et
une expression à typer. Elle capture les exceptions dues à une erreur de typage, affiche un
message plus clair et déclenche une nouvelle exception.
# let
typing_handler
typing_fun
env
expr
=
reset_unknowns();
try
typing_fun
env
expr
with
Type_error
(Clash(lt1,
lt2))
->
print_string
"Type clash between "
;
print_type
lt1;
print_string
" and "
;
print_type
lt2;
print_newline();
failwith
"type_check"
|
Type_error
(Unbound_var
s)
->
print_string
"Unbound variable "
;
print_string
s;
print_newline();
failwith
"type_check"
;;
val typing_handler : ('a -> 'b -> 'c) -> 'a -> 'b -> 'c = <fun>