let - Linux

Transcription

let - Linux
OCaml
caml.inria.fr
« Pourquoi c'est (presque ;-) le meilleur
langage du monde »
Jeudi 7 avril 2005 – David Mentré
OCaml en quelques mots
●
●
OCaml est un langage de programmation :
–
fortement typé, à typage polymorphe
–
à inférence de type
–
types de données algébriques et filtrage
–
fonctionnel supérieur, impératif et objet
–
avec exceptions
–
avec un ramasse miettes
–
efficace
–
compilateur libre (QPL compatible DFSG & LGPL)
Bref : que des bonnes raisons de l'utiliser ! ;-)
Petit historique
●
1985 : langages de la famille ML (dont Caml)
–
développé à l'INRIA
–
comme support pour des assistants de preuve
●
●
1990 : système Caml Light
–
●
par ex. : Coq de l'INRIA
byte code efficace
1996 : OCaml (Objective Caml)
–
objet
–
compilateur natif
–
... et tout le reste
Démarrage : les types
●
Utiliser l'interpréteur pour découvrir OCaml
●
Toutes les expressions sont typées
$ ocaml
Objective Caml version 3.08.2
# 1;;
- : int = 1
# "toto";;
- : string = "toto"
# [|1; 2|];;
- : int array = [|1; 2|]
# [true; false];;
- : bool list = [true; false]
Types de base, fonctions
●
Types classiques
–
booléens, entiers, flottants, chaînes, tuples,
tableaux, listes, ...
–
structures de données plus complexes
●
ensembles, table de hachage, ...
Variables
# let pi = 4.0 *. atan 1.0;;
val pi : float = 3.14159265358979312
●Fonctions
# let carre x = x * x;;
val carre : int -> int = <fun>
●
Avantages du typage d'OCaml
●
Avantage du typage fort ?
–
●
détection des erreurs dès la compilation
Oui mais rentrer les types c'est lourd !
–
c'est pourquoi on a une inférence de type
●
le compilateur trouve les types, comme un grand
# let carre_flottant x = x *. x;;
val carre_flottant : float -> float = <fun>
# let carre x = x * x;;
val carre : int -> int = <fun>
# carre 4.0;;
This expression has type float but is here used with type int
Type Somme et filtrage
●
Définir les différentes alternatives
# type chocolat = Noir | Lait;;
type chocolat = Noir | Lait
# let j_aime = Noir;;
val j_aime : chocolat = Noir
●
Traiter chaque cas dans une fonction
# let bon_chocolat choco =
match choco with
| Noir -> true
| Lait -> false;;
val bon_chocolat : chocolat -> bool = <fun>
# bon_chocolat Noir;;
- : bool = true
Avantage du filtrage
●
On n'oublie pas un cas !
–
si on l'utilise de manière systématique, ces bugs
sont évités
●
le compilateur le dit
# let bon_chocolat choco =
match choco with
| Noir -> true;;
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
Lait
val bon_chocolat : chocolat -> bool = <fun>
–
structures de données correctes par
construction
Polymorphisme
●
Une fonction est générique
–
elle marche sur plusieurs types ('a ≡ )
# List.rev;;
- : 'a list -> 'a list = <fun>
# List.rev [1; 2; 3; 4];;
- : int list = [4; 3; 2; 1]
# List.rev ["langage"; "super"; "un"];;
- : string list = ["un"; "super"; "langage"]
# List.rev [Noir; Lait];;
- : chocolat list = [Lait; Noir]
# List.map;;
- : ('a -> 'b) -> 'a list -> 'b list = <fun>
Avantage du polymorphisme
●
Les programmes sont génériques
–
fonctionnalités similaires dans d'autres langages
●
–
●
ex. : Ada, Eiffel, template C++
en OCaml, le polymorphisme arrive tout seul
avec l'inférence de type
Combinable avec d'autres constructions
–
comme les types Somme
–
programmation claire et efficace
Filtrage et type somme polymorphe
# type 'a résultat = Trouvé of 'a | Pas_trouvé;;
type 'a résultat = Trouvé of 'a | Pas_trouvé
# let rec cherche elt liste =
match liste with
| tete :: reste when tete = elt -> Trouvé tete
| tete :: reste when tete <> elt -> cherche elt reste
| _ -> Pas_trouvé;;
val cherche : 'a -> 'a list -> 'a résultat = <fun>
# cherche Noir [Noir; Lait];;
- : chocolat résultat = Trouvé Noir
# cherche 0 [1; 2; 3; 4];;
- : int résultat = Pas_trouvé
Fonctions d'ordre supérieur
Des fonctions qui prennent en argument
d'autres fonctions
●
–
utile pour être générique
# List.filter;;
- : ('a -> bool) -> 'a list -> 'a list = <fun>
# List.filter (fun x -> x = 2) [1; 2; 3; 4];;
- : int list = [2]
# List.filter (fun x -> x mod 2 = 0) [1; 2; 3; 4];;
- : int list = [2; 4]
# List.filter (fun x -> x >= 2 && x < 4) [1; 2; 3; 4];;
- : int list = [2; 3]
Style fonctionnel
●
Utiliser des fonctions récursives
–
variables à assignation unique
●
–
on leur donne une seul valeur : let x = ... in ....
factorielle : n! = 1 * 2 * ... * n-1 * n
# let rec factorielle x =
if x <= 1 then 1 else x * factorielle (x - 1);;
val factorielle : int -> int = <fun>
# factorielle 5;;
- : int = 120
Style impératif
●
Comme la programmation « classique »
–
variables peuvent être modifiées plusieurs fois
# let factorielle n =
let result = ref 1 in
for i = 2 to n do
result := i * !result
done;
!result;;
val factorielle : int -> int = <fun>
# factorielle 5;;
- : int = 120
Remarques sur le style fonctionnel
●
C'est efficace ?
–
Oui car :
●
une fonction récursive terminale est équivalente à
une boucle
–
●
●
et le compilateur OCaml fait la transformation (comme gcc)
en sous-main, on a un jeu de pointeur, pas de copie
réelle de données
Avantages : fonctionnel ou impératif ?
–
cela dépend :-)
–
le style fonctionnel permet souvent une
programmation plus propre et plus lisible
Programme récursif terminal
Fonctionnel # let rec factorielle x =
●
if x <= 1 then 1 else x * factorielle (x - 1);;
val factorielle : int -> int = <fun>
# let factorielle x =
●Fonctionnel
let rec f x acc =
if x <= 1 then acc else f (x - 1) (x * acc) in
terminal
f x 1;;
val factorielle : int -> int = <fun>
Impératif
●
# let factorielle n =
let result = ref 1 in
for i = 2 to n do
result := i * !result
done;
!result;;
val factorielle : int -> int = <fun>
Exemple de programme fonctionnel
# let rec tri liste =
match liste with
| [] -> []
| x :: l -> insère x (tri l)
and insère elem liste =
match liste with
| [] -> [elem]
| x :: l ->
if elem < x then elem :: x :: l else x :: insère elem l;;
val tri : 'a list -> 'a list = <fun>
val insère : 'a -> 'a list -> 'a list = <fun>
# tri [2; 1; 0];;
- : int list = [0; 1; 2]
Style objet
●
●
Comme en C++, Java, ...
–
méthodes privées/publiques, virtuelles, ...
–
héritage multiple
–
liaison tardive
–
...
Avec en plus
–
objets immédiats (objet sans classe)
–
méthodes polymorphes
–
objets fonctionnels
Exemple de programme objet
# class point =
object
val mutable x = 0
method get_x = x
method move d = x <- x + d
end;;
class point :
object val mutable x : int method get_x : int method
move : int -> unit end
# let p = new point;;
val p : point = <obj>
# p#get_x;;
- : int = 0
# p#move 3;;
- : unit = ()
# p#get_x;;
- : int = 3
Exceptions
●
Comme en C++, Java, ...
–
mais efficace
–
donc utilisable comme structure de contrôle, pas
seulement sur les cas d'erreur
# exception Trouvé_à_l'index of int;;
exception Trouvé_à_l'index of int
# let cherche x tableau =
for i = 0 to Array.length tableau - 1 do
if tableau.(i) = x then raise (Trouvé_à_l'index i)
done;;
val cherche : 'a -> 'a array -> unit = <fun>
# cherche 3 [|1; 4; 3; 7; 8|];;
Exception: Trouvé_à_l'index 2.
Ramasse miettes
●
Récupération automatique de la mémoire
–
●
ramasse miettes, Garbage Collector (GC)
On alloue des variables, fonctions, ...
–
... la libération est automatique
–
pas de segfault
●
●
toute une classe de bugs en moins !
Ramasse miettes efficace
–
contrairement à Java (du moins au début)
–
techniquement : GC générationnel incrémental
●
générationnel : vieux objets et petits nouveaux
●
incrémental : un petit coup de GC de temps en temps
Compilation
●
Deux compilateurs
–
–
byte code sur une machine virtuelle (ocamlc)
●
marche sur toutes les plate-formes
●
efficace
●
utilisé dans l'environnement interactif
code natif (ocamlopt)
●
●
●
pour x86, IA64, PowerPC, AMD64, Alpha, SPARC,
MIPS, PA-RISC, StrongARM
très efficace
Plate-formes supportées
–
Unix (Linux, *BSD, propriétaires), Windows,
MacOS X
Performances
●
OCaml est très efficace
–
« au pire, 2 fois plus lent que le C »
–
en pratique, quasiment aussi rapide que le C
●
voire plus (gain sur
l'algorithmique)
shootout.alioth.debian.org
CPU : x1
Mem : x1
Lignes : x0
1
2
3
4
...
8
...
13
...
18
19
20
21
22
...
24
langage
C Intel
C gcc
OCaml
Ada 95 GNAT
...
C++ Intel
...
C# Mono
...
Python
Perl
Tcl
Java
Ruby
...
PHP
score
92,26
85,99
76,52
63,49
...
44,67
...
34,25
...
29,59
27,92
26,47
22,07
18,15
...
15,15
Calcul scientifique
Sieve primes up to 10^8 (bit-twiddling/array limited):
32-bit OCaml: 7.102s
32-bit C++: 19.145s
64-bit OCaml: 5.697s
64-bit C++: 13.433s
100th-nearest neighbours from a 10k-atom model of amorphous silicon
(de/allocation limited):
32-bit OCaml: 28.407s
32-bit C++: 14.035s
64-bit OCaml: 35.538s
64-bit C++: 12.392s
Generate, bubble sort and accumulate an array of 10^4 double-precision random floatingpoint numbers:
32-bit OCaml: 1.185s
32-bit C++: 1.471s
64-bit OCaml: 0.785s
64-bit C++: 0.957s
without bounds checking:
32-bit OCaml: 0.992s
32-bit C++: 1.249s
64-bit OCaml: 0.591s
64-bit C++: 0.705s
Jon Harrop / 2005-03-30 /
caml-list
2048^2 mandelbrot (float-arithmetic limited):
32-bit OCaml: 2.946s
32-bit C++: 1.479s
64-bit OCaml: 1.704s
64-bit C++: 1.161s
1024 FFTs and iFFTs (float-arithmetic limited):
32-bit OCaml: 31.491s
32-bit C++: 8.441s
64-bit OCaml: 9.260s
64-bit C++: 8.562s
Accumulate a Lorentzian over the number of integer triples (i, j, k) which
lie in i^2 + j^2 + k^2 < 400 (float-arithmetic limited):
32-bit OCaml: 16.329s
32-bit C++: 8.002s
64-bit OCaml: 9.459s
64-bit C++: 5.933s
Utilisation d'OCaml
●
A éviter en OCaml
–
programmation système et temps réel dur
–
boucles bien tassées
●
–
petits scripts, traitement du texte, bidouille
●
–
Perl, Python, shell script
programmation objet « pure » (à mon avis)
●
●
C, C++, Ada
C++, SmallTalk, Eiffel, Python
Pour tout le reste, OCaml est utilisable
–
soit 90 % de la programmation courante
Environnement
●
Environnement de programmation complet
–
debugger, compilateurs, lex et yacc, éditeurs
(mode Emacs), threads, ...
–
pleins de bibliothèques, d'exemples, ...
●
●
The Hump : http://caml.inria.fr/cgi-bin/hump.en.cgi
Communauté à croissance exponentielle
–
liste de diffusion (très) active
●
–
voire trollesque
mais quand même plus petite que C, C++, Java
Ce dont je ne parlerai pas
●
Modules fonctionnels (functors)
–
encapsulation et abstraction des données
●
Liaison avec le langage C
●
Labels
# let f ~grand ~petit = grand - petit;;
val f : grand:int -> petit:int -> int = <fun>
# f ~grand:3 ~petit:2;;
- : int = 1
# f ~petit:2 ~grand:3;;
- : int = 1
●
Debugger avec retour arrière temporel
●
Évaluation paresseuse
Et les inconvénients !?
●
Langage de recherche
–
plus difficile de trouver un programmeur
–
le langage évolue (mais en bien :-)
●
Pas autant de bibliothèques que Java ou C
●
Pas de debugger sur le code natif
●
Messages d'erreur parfois cryptiques
●
Quelques contraintes liées au typage
–
pas de récursivité entre modules, ordre des
fichiers au link
un exemple : demexp
●
Programme OCaml complet
–
réseau, serveur, client graphique (GTK2), XML,
threads
–
~10.000 lignes de source
–
~6.000 lignes de code OCaml
●
●
½ serveur, ½ client
–
versions Linux et Windows
–
2 ans de développement
Bibliothèques externes
–
ocamlrpc, CDuce
Conclusion
●
OCaml a de réelles qualités
–
efficace, sûr, agréable, bien outillé, portable
–
on évite des bugs par construction
–
c'est un vrai plaisir de programmer avec
●
●
aussi rapide que le C, aussi sûr que l'Ada, aussi
souple que le Lisp (voire plus que les trois :-)
Utilisez le pour l'apprécier !
Pour démarrer
●
●
●
Site officiel
–
http://caml.inria.fr/
–
listes de discussions, sources, pointeurs
Un livre
–
http://www.pps.jussieu.fr/Livres/ora/DA-OCAML/index.html
–
apprendre le langage
La bosse du chameau
–
http://caml.inria.fr//cgi-bin/hump.fr.cgi
–
trouver la roue
Questions ?
?