mercredi 21 avril 2010

Caml

Programmation fonctionnelle :

La programmation fonctionnelle est un paradigme de programmation qui considère le calcul en tant qu'évaluation de fonctions mathématiques et rejette le changement d'état et la mutation des données. Elle souligne l'application des fonctions, contrairement au modèle de programmation impérative qui met en avant les changements d'état[1].
Un langage fonctionnel est donc un langage de programmation dont la syntaxe et les caractéristiques encouragent la programmation fonctionnelle. Alors que l'origine de la programmation fonctionnelle peut être trouvée dans le lambda-calcul, le langage fonctionnel le plus ancien est Lisp, créé en 1958 par McCarthy. Lisp a donné naissance à des variantes telles que Scheme (1975) et Common Lisp (1984)[2], qui comme Lisp ne sont pas ou peu typés. Des langages fonctionnels plus récents tels ML (1973), Haskell (1987), OCaml, Erlang, Clean et Oz, CDuce (2003) ou F# sont fortement typés.

Programmation fonctionnelle

La programmation fonctionnelle s'affranchit de façon radicale des effets secondaires en interdisant toute opération d'affectation.
Le paradigme fonctionnel n'utilise pas de machine d'états pour décrire un programme, mais un emboîtement de fonctions que l'on peut voir comme des « boîtes noires » que l'on peut imbriquer les unes dans les autres. Chaque boîte possédant plusieurs paramètres en entrée mais une seule sortie, elle ne peut sortir qu'une seule valeur possible pour chaque n-uplet de valeurs présentées en entrée. Ainsi, les fonctions n'introduisent pas d'effets de bord. Un programme est donc une application, au sens mathématique, qui ne donne qu'un seul résultat pour chaque ensemble de valeurs en entrée. Cette façon de penser, qui est très différente de la pensée habituelle en programmation impérative est l'une des causes principales de la difficulté qu'ont les programmeurs formés aux langages impératifs pour aborder la programmation fonctionnelle. Cependant, elle ne pose généralement pas de difficultés particulières aux débutants qui n'ont jamais été exposés à des langages impératifs. Un avantage important des fonctions sans effet de bord est la facilité que l'on a à les tester unitairement. Par ailleurs, l'usage généralisé d'une gestion de mémoire automatique par l'intermédiaire d'un ramasse-miettes (en anglais garbage collector) simplifie la tâche du programmeur.
En pratique, pour des raisons d'efficacité, et du fait que certains algorithmes s'expriment aisément avec une machine d'états, certains langages fonctionnels autorisent la programmation impérative en permettant de spécifier que certaines variables sont assignables (ou mutables selon la dénomination habituelle), et donc la possibilité d'introduire localement des effets de bord. Ces langages sont regroupés sous le nom de langages fonctionnels impurs.
Les langages dits purement fonctionnels n'autorisent pas la programmation impérative. De fait, ils sont dénués d'effets de bord et protégés contre les problèmes que pose l'exécution concurrente. On peut voir par exemple ce qui a été fait dans le cadre du langage Erlang.
L'implémentation des langages fonctionnels fait un usage sophistiqué de la pile car afin de s'affranchir de la nécessité de stocker des données temporaires dans des tableaux ils font largement appel à la récursivité (fait d'inclure l'appel d'une fonction dans sa propre définition). La récursivité peut être rendue plus efficace à l'aide d'une technique dénommée récursion terminale (en anglais tail-recursion), qui consiste à accumuler les résultats intermédiaires dans une case mémoire de la pile et à la passer en paramètre dans l'appel récursif. Ceci permet d'éviter d'empiler les appels récursifs dans la pile en les remplaçant par une simple succession de sauts. Le code généré par le compilateur est alors similaire à celui généré par une boucle en impératif. Certains langages comme Scheme, OCaml et Anubis optimisent automatiquement les appels récursifs de cette manière.

le langage Caml:

Caml (originalement acronyme de Categorical Abstract Machine Language) , (un jeu de mots sur "La machine abstraite catégorique" et "ML" la famille des langages a laquelle il appartient .

Caml a été conçu et mis en œuvre dans le projet Formel à l'INRIA Dirigé par "Gerrard Huet" jusqu'au 1994 et maintenant son developpement se poursuit dans le projet "Cristal".

Comme tous les descendants de ML, Caml est typé statiquement, rigoureusement évalués, et utilise la gestion automatique de la mémoire.
la première implémentation de Caml en Lisp a été surnommée :"Heavy CAML" en raison de ses exigences Mémoire et CPU , par Rapport a son successeur "CAML Light" qui a été implémente en C par "Xavier Leroy" et " Damien Doligez" . en plus de sa réécriture complète "Caml Lihgt" a ajouter un puissant système de modules pour le noyeau du langage .

Actuellement l'implémentation principale de "Caml" est "Objective Caml", qui ajoute de nombreuses nouvelles fonctionnalités pour les langues, y compris une couche objet.

Caractéristiques et fonctionnalités du langage

Sûreté:

Le langage Caml est très sûr. Le compilateur fait de nombreuses vérifications avant la compilation des programmes. De nombreuses erreurs de programmation deviennent ainsi impossibles en Caml: confusions de types de données, accès erronés à l'intérieur des données par exemple. En effet, tous ces points sont vérifiés et gérés automatiquement par le compilateur, ce qui garantit l'intégrité parfaite des données manipulées par les programmes.
Caml est typé statiquement, mais il est inutile d'ajouter des informations de type dans les programmes (comme en Ada, en Pascal ou en C): les annotations de typage sont automatiquement calculées par le compilateur.
Types de données:

Il existe en Caml de nombreux types de données prédéfinis:
  • types de base: entiers, flottants, booléens, caractères, chaînes de caractères.
  • types de données plus complexes: n-uplets, tableaux, listes, ensembles, tables de hachage, files, piles, flux de données.
Au-delà de ces types prédéfinis, Caml propose de puissants moyens de définir de nouveaux types: types enregistrements, types énumérés, et types sommes généraux. Les types sommes sont une généralisation des types unions, à la fois simple, sûre et facile à maîtriser. Ils permettent la définition de types de données qui présentent des valeurs hétérogènes repérées par des constructeurs de valeurs.
Au gré du programmeur, tous ces types sont définissables concrètement (les constructeurs sont disponibles à l'extérieur du module) ou abstraitement (l'implémentation est restreinte au module de définition et les constructeurs sont invisibles à l'extérieur).
Ce mécanisme autorise un contrôle fin du degré d'encapsulation des données manipulées par les programmes, ce qui est indispensable pour la programmation à grande échelle.
Fonctions:
Caml est un langage de programmation fonctionnel: il n'y a pas de restriction à la définition et à l'usage des fonctions, qu'on peut librement passer en argument ou retourner en résultat dans les programmes.

Gestion mémoire automatisée et incrémentale:

Caml offre une gestion automatique de la mémoire: l'allocation et la libération des structures de données est implicite (il n'y a pas de primitives de manipulation explicite de la mémoire comme ``new'' ou ``free'' ou ``dispose''), et laissée à la charge du compilateur. On obtient ainsi une programmation bien plus sûre, puisqu'il n'y a jamais de corruption inattendue des structures de données manipulées.
De plus le gestionnaire mémoire opère en parallèle avec l'application, sans jamais l'arrêter de façon notable (récupération mémoire incrémentale).

Traits impératifs:

Caml offre la panoplie complète des traits de la programmation impérative, en particulier les tableaux modifiables en place, les boucles et les variables affectables, les enregistrements avec champs physiquement modifiables.

Compilateur rapide, code exécutable rapide:

Caml propose un compilateur de fichiers, et la compilation séparée est assurée par un système de modules. De surcroît, le compilateur Caml comporte une option qui maximise la vitesse de compilation, la portabilité des programmes obtenus, et minimise la taille des exécutables (compilation en code-octets).
Le compilateur d'Objective Caml comporte en plus une option ``optimisante'' qui privilégie la vitesse d'exécution (compilation en code natif): le compilateur optimisant d'Objective Caml produit des programmes dont la vitesse d'exécution est digne des meilleurs compilateurs disponibles actuellement.

Interactivité

Caml offre également un système interactif (une boucle de lecture-évaluation-impression des résultats), qui est très pratique pour apprendre le langage ou essayer et corriger ses programmes: il n'y a pas besoin d'utiliser forcément des fichiers, ni d'ajouter des ordres d'impression dans les programmes puisque les résultats sont imprimés automatiquement par le système interactif.

Forte capacité de traitement symbolique

Caml vous propose la ``programmation orientée par filtrage'': cette puissante méthode de programmation est une généralisation de l'analyse de cas traditionnelle qui est maintenant disponible pour tous les types de données du langage. Le mécanisme de filtrage est un moyen concis et élégant de ``tester et de nommer'' les données en une seule opération. Le compilateur Caml tire avantage de ce trait unique pour faire de nombreuses vérifications sémantiques sur le code qui lui est soumis: le vérificateur de filtrage du compilateur est capable de détecter les branches inutiles des analyses de cas (``ce cas ne se présentera jamais à l'exécution'') et, plus étonnant encore, de détecter les cas oubliés (``ce cas n'est pas envisagé par votre programme''). Ainsi, le vérificateur de filtrage met souvent le doigt sur de subtiles erreurs qui se glissent dans les programmes. De surcroît, le vérificateur de filtrage est capable d'apporter la preuve de couverture exhaustive des cas des programmes qui utilisent le filtrage.
Le filtrage apporte un confort inégalé dans le traitement symbolique des données.
Le vérificateur de filtrage procure un niveau de sécurité dans la programmation et un degré de qualité inégalé des programmes qui manipulent des données symboliques.

Traitement des erreurs:

Caml possède un mécanisme général d'exceptions, pour traiter ou corriger les erreurs ou les situations exceptionnelles.
Mise au point des programmes
Plusieurs méthodes de mise au point des programmes s'offrent à vous en Caml:
  • le système interactif offre une méthode élémentaire mais très simple et rapide pour tester des (petites) fonctions: on vérifie simplement les résultats obtenus sur quelques exemples tapés directement dans la boucle d'interaction.
  • dans les cas plus complexes, le système interactif permet également à très peu de frais de suivre la progression des calculs avec le mécanisme de trace des appels de fonctions.
  • enfin le debogueur symbolique avec retour arrière permet de suivre très finement le déroulement de l'exécution, de l'arrêter à tout moment pour examiner l'état courant des variables et des fonctions en attente, et même de revenir en arrière dans les calculs pour reprendre l'exécution au moment où un evênement intéressant se produit.

Polymorphisme

Caml est doté d'un puissant typage ``polymorphe'': certains types peuvent rester indéterminés, représentant alors ``n'importe quel type''.
Ainsi, les fonctions et procédures qui sont d'usage général s'appliquent à n'importe quel type de données, sans exception (par exemple les routines de tri s'appliquent à tout type de tableaux).

Méthode d'évaluation

Caml est un langage ``strict'', par opposition aux langages paresseux. Cependant la pleine fonctionnalité permet de créer des suspensions et donc de coder l'évaluation paresseuse de données potentiellement infinies.

Programmation en vraie grandeur

Les programmes Caml sont formés d'unités de compilation que le compilateur compile séparément. Ces organisation est parfaitement compatible avec l'utilisation d'outils traditionnels de gestion de projets (comme l'utilitaire make d'Unix). Le système de module du langage est puissant et sûr (toutes les interactions entre modules sont statiquement vérifiées par le contrôleur de types). Les modules d'Objective Caml peuvent comporter des sous-modules (à un degré d'emboîtement quelconque) et les fonctions des modules dans les modules sont autorisées (ce qui permet de définir des modules paramétrés par d'autres modules).

Programmation orientée objets

Objective Caml propose des objets qui permettent d'utiliser le style orienté objets dans les programmes Caml. Fidèle à la philosohie du langage, cette extension orientée objets obéit au paradigme du ``typage fort'': en conséquence, aucune méthode ne peut être appliquée à un objet qui ne pourrait y «répondre» («les méthodes sont toujours bien comprises»). Encore une fois, cette vérification systématique du compilateur évite de nombreuses erreurs. Ceci offre au programmeur Caml, outre un confort insoupçonné dans l'écriture de ses programmes orientés objets, un niveau inégalé de qualité des programmes qu'il produit.

Puissantes bibliothèques

De nombreuses bibliothèques et contributions sont disponibles en Caml, en particulier des primitives de dessin indépendantes de la machine (libgraph), une arithmétique rationnelle exacte en multi-précision (camlnum), et de nombreuses interfaces avec des technologies bien connues: générateurs d'analyseurs lexicaux et syntaxiques avec camllex et camlyacc. Sous Unix, on dispose aussi d'un débogueur avec retour arrière, d'un navigateur dans les fichiers sources (camlbrowser), d'une interface graphique à l'aide de Tk/Tcl (camltk) et d'une interface poussée avec le système (libunix).

Examples de code:

1-Fonction factorielle (récursivité et la programmation purement fonctionnel):

 let rec fact n = if n=0 then 1 else n * fact(n-1);;

2-Dérivés numeriques (fonctions d'ordre supérieur):
en tant que langage de programmation fonctionnelle, il est facile de créer et passer autour
des fonctions dans les programmes OCaml.
Cette capacité a un très grand nombre d'applications. Calcul de la dérivée numérique
d'une fonction est une telle demande. La fonction Caml suivant:
"d" calcule la dérivée numérique d'une fonction donnée "f" à un moment donné "X":

 let d delta f x =
     (f (x +. delta) -. f (x -. delta)) /. (2. *. delta);;