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 par ex. : Coq de l'inria 1990 : système Caml Light 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 classiques Types de base, fonctions 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
On n'oublie pas un cas! Avantage du filtrage 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
Une fonction est générique Polymorphisme 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]
Utiliser des fonctions récursives variables à assignation unique Style fonctionnel 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 Fonctionnel terminal Impératif # let rec factorielle x = if x <= 1 then 1 else x * factorielle (x - 1);; val factorielle : int -> int = <fun> # let factorielle x = let rec f x acc = if x <= 1 then acc else f (x - 1) (x * acc) in f x 1;; val factorielle : int -> int = <fun> # 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 langage score 1 C Intel 92,26 2 C gcc 85,99 3 OCaml 76,52 4 Ada 95 GNAT 63,49......... 8 C++ Intel 44,67......... 13 C# Mono 34,25......... 18 Python 29,59 19 Perl 27,92 20 Tcl 26,47 21 Java 22,07 22 Ruby 18,15......... 24 PHP 15,15
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 Calcul scientifique 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 C, C++, Ada petits scripts, traitement du texte, bidouille Perl, Python, shell script programmation objet «pure» (à mon avis) 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
Langage de recherche Et les inconvénients!? 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
Programme OCaml complet un exemple : demexp 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?