Types en OCaml¶
le type
unit
:()
(rien)les types construits prédéfinis (chaînes de caractères, n-uplets, listes chaînées, tableaux) ou à définir (cf plus tard)
les types fonction :
T -> T'
NB : le type des expressions est vérifié avant que les calculs soient exécutés
Les opérateurs numériques¶
Les opérateurs numériques pour float
et int
sont différents.
+
,-
,*
,/
,mod
pour lesint
+.
,-.
,*.
,/.
,**
pour lesfloat
NB : il n'y a pas de conversions implicites en OCaml
2 + 2;;
2 + 2;;
- : int = 4
- : int = 4
signifie : l'expression calculée est de type int
et vaut 4
Plus généralement :
nom : type = valeur
. Ici, on n'a pas défini de nom particulier ...
2.0 + 2.0;;
2.0 + 2.0;;
File "[39]", line 1, characters 0-3: 1 | 2.0 + 2.0;; ^^^ Error: This expression has type float but an expression was expected of type int
Erreur : l'opérateur +
attend de opérandes de type int
2.0 +. 2.0;;
2.0 +. 2.0;;
- : float = 4.
Les opérations de conversions se font avec des appels fonctions
NB : programmation fonctionnelle : on dit plutôt appliquer une fonction
int_of_float 2.0 + 2;;
int_of_float 2.0 + 2;;
- : int = 4
la fonction int_of_float
: convertit un float
en int
(on récupère un int
à partir (of) d'un float
)
NB : l'application de fonction se fait sans parenthèses
NB : l'application de fonction est prioritaire par rapport aux opérations numériques.
Dans le doute, on écrirait (int_of_float 2.0) + 2
Exceptions¶
Le langage OCaml dispose d'un mécanisme d'exceptions (comme Python)
3/0;;
Exception: Division_by_zero.
Raised by primitive operation at unknown location
Called from file "toplevel/toploop.ml", line 208, characters 17-27
L'expression est correctement typée (division entière avec deux opérandes entiers)
C'est à l'éxécution que le calcul s'interrompt
Les opérateurs de comparaison¶
ATTENTION :
=
,<>
: égalité, inégalité<
,>
,<=
,>=
: ...
NB : ce sont les mêmes opérateurs pour les types de base ...
NB : ... mais on ne peut pas comparer expressions de types différents
2 < 3;;
2 < 3;;
- : bool = true
2.0 < 3.0;;
2.0 < 3.0;;
- : bool = true
2.0 < 3;;
2.0 < 3;;
File "[54]", line 1, characters 6-7: 1 | 2.0 < 3;; ^ Error: This expression has type int but an expression was expected of type float
Les opérateurs booléens¶
- ATTENTION :
not
,&&
,||
NB : évaluation paresseuse (comme en Python, en C ...)
Exemple 1 : variable globale¶
(* Ceci est un commentaire *)
let x = "Hello world !";;
(* Ceci est un commentaire *)
let x = "Hello world !";;
val x : string = "Hello world !"
un commentaire
(* ...*)
let
signifie "soit" au sens mathématiquelet x = expr;;
définition de variable globalex
initialisée àexpr
;;
termine une phrase OCaml
On a défini une variable nommée x
, de type string
qui vaut "Hello world !"
NB : string
le type chaîne de caractère
NB : le type a été inféré (= déduit) par le système (ici, c'était facile ...)
x;;
x;;
- : string = "Hello world !"
x
est bien un variable globale : elle est toujours accessible-
: signifie qu'on n'a pas défini de nouvelle variablecette expression est de type
int
et vaut"Hello world !"
En OCaml, une variable est toujours initialisée
let z;;
let z;; (* CODE FAUX : il manque la valeur ...*)
File "[19]", line 1, characters 5-7: 1 | let z;; ^^ Error: Syntax error
En OCaml, une variable n'est pas modifiable (sauf ...) : immuable ou non-mutable
NB: quelques cas particuliers (cf OCaml impératif)
NB: ça change totalement la façon de programmer ! Et les contre-sens sont fréquents chez les débutants
x = "Bonjour le monde !";;
x = "Bonjour le monde !";;
- : bool = false
ATTENTION : =
est l'opérateur de comparaison
Cette expression est de type bool
et est fausse (ce sont deux chaînes de caractères différentes)
Exemple trompeur¶
on peut redéfinir une variable de même nom
let x = 1;;
let x = x + 1;;
x;;
let x = 1;;
let x = x + 1;;
x;;
val x : int = 1
val x : int = 2
- : int = 2
x
est une nouvelle variable initialisé avec la valeur de l'ancienne augmentée de 1- l'ancienne variable est inaccessible
Exemple 2 : variable locale¶
let z = 3 in z * z;;
let z = 3 in z * z;;
- : int = 9
let z = expr1 in expr2 ;;
définition de la variable localez
, initialisée avecexpr1
, puis utilisée pour calculerexpr2
la variable de
z
n'est utilisable que pour calculerexpr2
(portée locale)le résultat n'a pas de nom, c'est un entier (
int
) qui vaut 9
NB : là encore, le type a été inféré (un peu moins facile)
z;;
z;;
File "[30]", line 1, characters 0-1: 1 | z;; ^ Error: Unbound value z
z
était bien une variable locale. Elle n'est plus accessible
Une variable locale peut venir "éclipser" une variable globale
let x = 3;; (* x : variable globale *)
let x = 5 in x + 1;; (* x: variable locale*)
x;; (* c'est la variable globale *)
let x = 3;; (* x : variable globale *)
let x = 5 in x + 1;; (* x: variable locale*)
x;; (* c'est la variable globale *)
val x : int = 3
- : int = 6
- : int = 3
Exemple 3 : fonction d'une variable¶
let f x = x * x;;
let f x = x * x;;
val f : int -> int = <fun>
let f x = expr
définit une fonctionf
de paramètrex
qui calculeon a défini une variable globale
f
de typeint -> int
qui vaut ... une fonctionNB : type
int -> int
inféré par OCaml !
ATTENTION : pas de parenthèse autour des paramètres
ATTENTION : pas de
return
Appel de fonction (application)¶
f 4;;
f 4;;
- : int = 16
ATTENTION : un appel de fonction en OCaml se fait sans parenthèses
Une fonction a un type¶
f;;
f;;
- : int -> int = <fun>
int_of_float;;
int_of_float;;
- : float -> int = <fun>
Certaines fonctions ne "calculent" rien¶
print_int;;
- : int -> unit = <fun>
print_int
affiche unint
à l'écran ("effet")
print_int 5;;
- : unit = ()
La valeur renvoyée est ()
(rien) de type unit
Fonctions locales¶
let f x = x * x in f 3 + f 4 = f 5
let f x = x * x in f 3 + f 4 = f 5
- : bool = true
la fonction locale
f
qui calcule le carré d'un nombre est appliquée 3 foisl'expression
f 3 + f 4 = f 5
(égalité entre 9 + 16 et 25 ?) s'évalue en true
Inférence de type¶
Comment OCaml déduit-il le type de
f
?
let f x = x * x;;
val f : int -> int = <fun>
*
est la multiplication entière- donc
x
doit être entier (type du paramètre) - donc
x * x
est entier (type de la valeur calculée)
Donc f : int -> int
Quel est le type de la fonction
id
?
let id x = x;;
let id x = x;;
val id : 'a -> 'a = <fun>
Polymorphisme : cette fonction peut s'appliquer à différents types de valeur
'a
est appelé variable de type, il représente un type quelconque
id 42;;
id 1.3;;
id "Hello World !";;
- : int = 42
- : float = 1.3
- : string = "Hello World !"
id ();;
- : unit = ()
Exemple 4 : fonctions de deux variables¶
let sum x y = x + y;;
val sum : int -> int -> int = <fun>
À lire ainsi :
- les deux premiers
int
sont les types des paramètres, - le dernier
int
est le type du résultat
NB : sum
est une variable globale
NB : on déduit son type car +
opére sur des entiers
Application (sans parenthèses)¶
sum 5 4;;
- : int = 9
Attention aux priorités :
- l'application est prioritaire sur les opérateurs numériques
- dans le doute, parenthéser
sum 4 1 * 3;;
sum 4 1 * 3;;
- : int = 15
sum 4 (1 * 3);;
sum 4 (1 * 3);;
- : int = 7
sum (4 + 1) 3;;
sum (4 + 1) 3;;
- : int = 8
Les opérateurs "infixes"¶
NB : infixe qui se place au milieu (préfixe:avant, postfixe:après)
Ce sont des fonctions de deux variables.
HP : utiliser l'opérateur noté entre parenthèses pour l'utiliser comme une fonction
(+);;
- : int -> int -> int = <fun>
(+) 4 5;;
- : int = 9
(<);;
- : 'a -> 'a -> bool = <fun>
Polymorphe !
(=);;
- : 'a -> 'a -> bool = <fun>
Exemple 5 : fonctions de n variables¶
let f arg1 arg2 ... argn = expr
: fonction globalelet f arg1 arg2 ... argn = expr1 in expr2
: fonction locale
Exemple 6 : utilisation des modules¶
OCaml dispose de nombreux modules qui contiennent de nombreuses variables et fonctions.
Par exemple : le module Random
contient la fonction float
Random.float x
renvoie un nombre aléatoire entre 0 et x
convention OCaml :
- un nom de module commence par une majuscule
- un nom de variable ne doit pas commencer par une majuscule
Random.float;;
- : float -> float = <fun>
Random.float 1.0;;
- : float = 0.703106837148469
open Random;; (* permet d'utiliser directement les fonctions du module *)
float 1.0;;
- : float = 0.122128067547237706
NB : comme from random import *
en Python
Exemple 7 : fonctions de 0 variable¶
Comment définir une fonction sans paramètre ? par exemple la fonction random
qui renvoie en float
entre 0 et 1 ?
let random = expr
?
INCORRECT : c'est la définition d'une variable
expr
est évalué tout de suite
CORRECT let random () = expr
expr
n'est évalué qu'au moment de l'appelrandom ()
let random = Random.float 1.0;; (* INCORRECT *)
val random : float = 0.918342174320491
random;; (* Pas très aléatoire ce nombre ...*)
- : float = 0.918342174320491
random ();; (* INCORRECT *)
File "[104]", line 1, characters 0-6: 1 | random ();; (* INCORRECT *) ^^^^^^ Error: This expression has type float This is not a function; it cannot be applied.
let random () = Random.float 1.0;;
val random : unit -> float = <fun>
random ();;
- : float = 0.791841585710404638
random ();;
- : float = 0.932074421228699213
Exercices¶
Exercice 1¶
Définir deux fonctions globales :
carre:int->int
cube:int->int
Est-ce que $2^3$ et $3^2$ sont égaux ?
Exercice 2¶
La fonction racine carrée fait-elle partie de la StdLib ? Quel est son type ?
Ecrire une fonction dist:float->float->float
telle que dist x y
calcule la distace OM avec $O(0,0)$ et $M(x,y)$
Exercice 3¶
L'appel Random.float 1.0
renvoie un float
aléatoire compris entre 0 et 1 (probabilité uniforme)
Ecrire une fonction boolAlea:unit->bool
telle que boolAlea ()
renvoie true
avec une probabilité 0.5 et false
avec une probabilité 0.5