ℹ️ Skipped - page is already crawled
| Filter | Status | Condition | Details |
|---|---|---|---|
| HTTP status | PASS | download_http_code = 200 | HTTP 200 |
| Age cutoff | PASS | download_stamp > now() - 6 MONTH | 0.8 months ago |
| History drop | PASS | isNull(history_drop_reason) | No drop reason |
| Spam/ban | PASS | fh_dont_index != 1 AND ml_spam_score = 0 | ml_spam_score=0 |
| Canonical | PASS | meta_canonical IS NULL OR = '' OR = src_unparsed | Not set |
| Property | Value |
|---|---|
| URL | https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html |
| Last Crawled | 2026-03-17 08:17:25 (23 days ago) |
| First Indexed | 2025-03-21 14:34:27 (1 year ago) |
| HTTP Status Code | 200 |
| Meta Title | Structure des programmes OCaml – OCaml |
| Meta Description | null |
| Meta Canonical | null |
| Boilerpipe Text | Maintenant nous allons passer un peu de temps Ă observer la structure
d'ensemble de quelque vrais programmes en OCaml. Je veux vous parler des
définitions locales et globales, quand utiliser
;;
ou
;
, les
modules, les fonctions imbriquées, les références. Pour cela nous allons
passer sur plein de concepts d'OCaml qui ne vous diront rien car nous ne
les avons pas encore évoqués. Ne vous embarrassez pas des détails pour
le moment. Occupez-vous uniquement de la forme générale des programmes,
et aux fonctionnalités mises en avant.
« Variables » locales (
plus exactement
expressions locales)
Prenons la fonction
average
écrite en C, et ajoutons lui une variable
locale. (à comparer avec la première version que nous avions)
double
average (double a, double b)
{
double sum = a + b;
return sum / 2;
}
Faisons la mĂŞme chose avec notre version OCaml :
#
let
average
a b
=
let
sum
= a +. b
in
sum /. 2.0
;;
val average : float -> float -> float = <fun>
La formule standard
let nom = expression in
sert à définir une
expression locale nommée, et
nom
peut ensuite être utilisé dans la
fonction Ă la place de
expression
jusqu'au
;;
qui marque la fin du
bloc de code. Remarquez que nous n'avons pas changé l'indentation après
in
. Considérez
let ... in
comme s'il s'agissait d'une instruction.
Maintenant, traduire les variables locales en C par ces expressions
locales est une supercherie. En fait ils sont un peu différents. La
variable C
sum
a un emplacement réservé dans la pile. Vous pouvez
changer la valeur associĂ©e Ă
sum
plus loin dans la fonction si vous
voulez, ou mĂŞme prendre l'adresse de
sum
. Ce n'est PAS possible dans
la version OCaml. Dans la version OCaml,
sum
est juste une abréviation
pour l'expression
a +. b
. Il est impossible d'assigner ou de changer
la valeur de
sum
. (Nous verrons comment avoir de vraies variables un
peu plus loin).
Voyons un autre exemple qui devrait clarifier les choses. Les deux bouts
de code suivants devraient retourner la mĂŞme valeur (Ă savoir (a+b) +
(a+b)
2
):
#
let
f
a b
=
(a +. b) +. (a +. b) ** 2.
;;
val f : float -> float -> float = <fun>
#
let
f
a b
=
let
x
= a +. b
in
x +. x ** 2.
;;
val f : float -> float -> float = <fun>
Il se peut que la seconde version soit plus rapide (bien que la plupart
des compilateurs devraient être capable de faire cette étape
d'"élimination de sous-expressions communes" à votre place), et en tout
cas elle est plus lisible. Dans le second exemple,
x
est une
abréviation pour l'expression
a +. b
.
« Variables » globales (
plus précisément
expressions globales)
Vous pouvez aussi donner des noms Ă des choses dans l'environnement
global, et comme pour les "variables" locales ci-dessus, ce ne sont pas
vraiment des variables, mais des abbréviations pour ces choses. Voici un
exemple pris dans un programme réel :
let
html
=
let
content
= read_whole_file file
in
GHtml
.html_from_string content
;;
let
menu_bold
()
=
match
bold_button#active
with
| true -> html#set_font_style ~enable:[`BOLD] ()
| false -> html#set_font_style ~disable:[`BOLD] ()
;;
let
main
()
=
(* code omitted *)
factory#add_item
"Cut"
~key:_X ~callback: html#cut
;;
Dans ce vrai morceau de code,
html
est un "widget" ("contrĂ´le")
d'édition de HTML (un objet provenant de la bibliothèque lablgtk), créé
une fois pour toute au début du programme par la première instruction
let html =
. Il y est ensuite fait référence dans plusieurs des
fonctions suivantes.
A noter que le nom
html
dans le bout de programme ci-dessus ne devrait
pas être comparé à une vraie variable globale comme en C ou dans
d'autres langages impératifs. Aucun emplacement n'est réservé pour
"stocker" le "pointeur sur
html
". Il n'est pas non plus possible
d'assigner quelque chose Ă
html
, par exemple pour lui faire désigner
un autre widget. Dans la section suivante nous parlerons des références,
qui sont de vraies variables.
Let-binding (?)
Les utilisations de
let ...
aussi bien au niveau global que dans une
fonction, sont souvent appellées des
let-binding
.
Références : les vraies variables
Comment faire si vous voulez une vraie variable que vous pouvez assigner
puis changer à votre gré dans votre programme ? Il vous faut une
référence
. Les références sont similaires aux pointeurs en C/C++. En
Java, toutes les variables qui stockent des objets sont en fait des
références (pointeurs) sur ces objets. En Perl, les références sont des
références - comme en OCaml.
Voici comment créer une référence sur un entier (
int
) en OCaml :
#
ref
0
;;
- : int ref = {contents = 0}
En fait cette instruction n'est pas très utile. Nous avons créé une
référence et puis, comme elle n'a pas de nom, le glaneur de cellules a
fait son travail et l'a immédiatement libérée! (Il est même probable que
le compileur ait éliminé cette instruction à la compilation). Donnons un
nom à cette référence :
#
let
my_ref
=
ref
0
;;
val my_ref : int ref = {contents = 0}
Cette référence contient maintenant l'entier 0. Mettons une autre valeur
Ă la place (assignement) :
#
my_ref := 100
;;
- : unit = ()
Voyons ce que la référence contient à présent :
#
!my_ref
;;
- : int = 100
Donc l'opérateur
:=
est utilisé pour assigner les références, et
!
pour les déréférencer et accéder au contenu. Voici une comparaison
approximative avec C/C++ :
OCaml C/C++
let my_ref = ref 0;; int a = 0; int *my_ptr = &a;
my_ref := 100;; *my_ptr = 100;
!my_ref *my_ptr
Les références ont leurs usages, mais vous vous apercevrez que vous ne
les utiliserez pas si souvent. La plupart du temps, vous utiliserez
let nom = expression in
pour nommer des expressions locales dans vos
définitions de fonctions.
Fonctions imbriquées
Le langage C n'a pas réellement de notion de fonctions imbriquées. GCC
les supporte, mais je ne connais pas de programme qui en tire parti. En
tout cas, voici ce que la page info de gcc dit sur les fonctions
imbriquées :
Une « fonction imbriquée » est une fonction définie à l'intérieur d'une
autre fonction (les fonctions imbriquées ne sont pas supportées par GNU
C++.) Le nom de la fonction imbriquée est local au bloc dans lequel il
est défini. Par exemple, définissons une fonction imbriquée nommée
square
, et appelons-lĂ deux fois :
foo (double a, double b)
{
double square (double z) { return z * z; }
return square (a) + square (b);
}
La fonction imbriquée a accès à toutes les variables de la fonction
englobante qui sont visibles à l'emplacement de sa définition. Cela
s'appelle la « portée lexicale ». Par exemple, voici une fonction
imbriquée qui utilise une variable héritée nommée
offset
:
bar (int *array, int offset, int size)
{
int access (int *array, int index)
{ return array[index + offset]; }
int i;
/* ... */
for (i = 0; i < size; i++)
/* ... */ access (array, i) /* ... */
}
Vous comprenez l'idée. Les fonctions imbriquées sont, cependant, très
utiles et largement employées en OCaml. Voici un exemple d'utilisation
réel de fonction imbriquées :
#
let
read_whole_channel
chan
=
let
buf
=
Buffer
.create 4096
in
let rec
loop
()
=
let
newline
= input_line chan
in
Buffer
.add_string buf newline;
Buffer
.add_char buf '\n';
loop ()
in
try
loop ()
with
End_of_file ->
Buffer
.contents buf
;;
val read_whole_channel : in_channel -> string = <fun>
Ne vous inquiétez pas de ce que ce code fait — il utilise beaucoup de
concepts que nous n'avons pas encore vu dans ce tutorial.
Concentrez-vous plutôt sur la fonction imbriquée principale nommée
loop
qui ne prend qu'un argument unit. On peut appeler
loop ()
depuis l'intérieur de la fonction
read_whole_channel
, mais elle n'est
pas définie en dehors de cette fonction. La fonction imbriquée peut
accéder aux variables définies dans la fonction principale (ici
loop
accède au nom local
buf
.)
La syntaxe des fonctions imbriquées est la même que pour nommer les
expressions locales :
let nom arguments = définition de fonction in
.
Normalement, vous indenterez la définition de fonction, après être passé
à la ligne comme dans l'exemple précédent, et n'oubliez pas d'utiliser
let rec
Ă la place de
let
si votre fonction est récursive (comme
dans cet exemple).
Modules et
open
OCaml est accompagné d'une quantité de modules amusants et intéressants,
de bibliothèques de code utile. Par exemple on y trouve une bibliothèque
pour dessiner des graphismes, interagir avec la collection de contrĂ´les
de l'interface-utilisateur graphique, pour manipuler des grands nombres,
des structures de données, ou pour faire des appels systèmes POSIX. Ces
bibliothèques se trouvent dans
/usr/lib/ocaml/
(sous Unix en
tout cas). Pour ces exemples, nous allons nous concentrer sur un module
relativement simple appelé
Graphics
.
Le module
Graphics
est constitué de sept fichiers (sur mon système) :
/usr/lib/ocaml/graphics.a
/usr/lib/ocaml/graphics.cma
/usr/lib/ocaml/graphics.cmi
/usr/lib/ocaml/graphics.cmx
/usr/lib/ocaml/graphics.cmxa
/usr/lib/ocaml/graphics.cmxs
/usr/lib/ocaml/graphics.mli
Pour l'instant occupons-nous de
graphics.mli
. C'est un fichier texte,
que vous pouvez donc lire dès à présent. Notez que son nom est
graphics.mli
et non
Graphics.mli
. OCaml met automatiquement une
capitale au nom du fichier pour obtenir le nom du module. C'est
déroutant quand on ne le sait pas !
Il y a deux moyens pour utiliser les fonctions de
Graphics
. Ou bien on
commence son programme par la déclaration
open Graphics;;
, ou bien on
préfixe tous les appels de fonctions comme ceci :
Graphics.open_graph
.
open
ressemble un peu Ă l'instruction
import
en Java, ou plus encore
comme l'instruction
use
en Perl.
Pour utiliser les module
Graphics
dans la boucle interactive, il
faut au préalable charger la librairie avec
#load
"graphics.cma"
;;
Une paire d'exemples devrait éclaircir ce point. (Les deux exemples
dessinent des choses différentes — essayez-les). Remarquez que le
premier exemple appelle
open_graph
et le second
Graphics.open_graph
.
(* To compile this example: ocamlc graphics.cma grtest1.ml -o grtest1 *)
open
Graphics
;;
open_graph
" 640x480"
;;
for
i = 12
downto
1
do
let
radius
= i * 20
in
set_color (
if
(i mod 2) = 0
then
red
else
yellow);
fill_circle 320 240 radius
done
;;
read_line ()
;;
(* To compile this example: ocamlc graphics.cma grtest2.ml -o grtest2 *)
Random
.self_init ()
;;
Graphics
.open_graph
" 640x480"
;;
let rec
iterate
r x_init i
=
if
i = 1
then
x_init
else
let
x
= iterate r x_init (i-1)
in
r *. x *. (1.0 -. x)
;;
for
x = 0
to
639
do
let
r
= 4.0 *. (float_of_int x) /. 640.0
in
for
i = 0
to
39
do
let
x_init
=
Random
.float 1.0
in
let
x_final
= iterate r x_init 500
in
let
y
= int_of_float (x_final *. 480.)
in
Graphics
.plot x y
done
done
;;
read_line ();;
Ces deux exemples utilisent des fonctionnalités dont nous n'avons pas
encore parlé : boucles "for" dans le style impératif, if-then-else, et
récursion. Nous en parlerons plus tard. Examinez-les cependant, et
essayez de comprendre (1) comment ils fonctionnent, et (2) comment
l'inférence de type aide à éliminer des bugs.
Le module
Pervasives
Il y a un module que vous n'avez jamais besoin d'ouvrir avec
open
,
c'est le module
Pervasives
(allez jeter un oeil sur
/usr/lib/ocaml/VERSION/pervasives.mli
). Tous les symboles définis dans
le module
Pervasives
sont implicitement importés dans tous les
programmes OCaml.
Renommage de modules
Que faire si vous voulez utiliser des symboles en provenance du module
Graphics
, mais ne voulez pas tous les importer, et n'avez pas envie de
taper
Graphics.
Ă chaque fois ? Renommez le module avec cette astuce :
module
Gr
=
Graphics
;;
Gr
.open_graph
" 640x480"
;;
Gr
.fill_circle 320 240 240
;;
read_line ();;
Cela devient vraiment utile quand vous voulez utiliser les symboles d'un
module imbriqué (les modules peuvent être imbriqués les uns dans les
autres, eux-aussi), mais ne voulez pas avoir Ă taper Ă chaque fois le
chemin d'accès au module imbriqué.
L'opérateur de séquence
;
Le point-virgule
;
est un opérateur, comme
+
. Bon, pas tout Ă
fait comme
+
mais conceptuellement le même. L'opérateur
+
a pour
type
int -> int -> int
— il prend deux entiers et retourne un entier
(la somme des deux entrées). Le point-virgule
;
peut ĂŞtre vu comme
ayant le type
unit -> 'b -> 'b
— il prend deux valeurs et simplement
retourne la seconde, la première expression étant garantie d'être
évaluée avant la seconde. C'est donc comme l'opérateur
,
(virgule)
en C. Vous pouvez écrire
a; b; c; d
de la même manière que vous
pouvez écrire
a + b + c + d
.
C'est un de ces « sauts conceptuels » qui n'est pas toujours explicité
très bien : en OCaml, presque tout est une expression.
if/then/else
est une expression.
a; b
est une expression.
match foo with ...
est une expression. Le code qui suit est parfaitement légal (et
toutes les lignes font la mĂŞme chose);
#
let
f
x b y
=
if
b
then
x+y
else
x+0
let
f
x b y
= x + (
if
b
then
y
else
0)
let
f
x b y
= x + (
match
b
with
true -> y | false -> 0)
let
f
x b y
= x + (
let
g
z
=
function
true -> z | false -> 0
in
g y b)
let
f
=
fun
x ->
fun
b ->
fun
y ->
if
b
then
x+y
else
x+0
let
f
x b y
= x + (
let
_
= y + 3
in
();
if
b
then
y
else
0)
;;
val f : int -> bool -> int -> int = <fun>
Notez particulièrement la dernière ligne où j'utilise l'opérateur
;
pour « joindre » deux instructions. Toutes les fonctions en OCaml
peuvent être déclarées sous la forme :
let
name
[parameters]
= expression ;;
En OCaml, la définition de ce qu'est une expression est simplement un
peu plus large qu'en C. En fait, C a le concept d'« instructions » —
mais toutes les instructions en C sont simplement des expressions en
OCaml de type
unit
(combinées avec l'opérateur
;
).
La différence entre
;
et
+
est qu'on peut utiliser
+
comme une
fonction. Par exemple, on peut définir une fonction
sum_list
, qui
somme une liste d'entier, par
#
let
sum_list
=
List
.fold_left ( + ) 0
;;
val sum_list : int list -> int = <fun>
La disparition de
;;
Maintenant, nous allons nous pencher sur un point très important.
Tous les exemples ci-dessus finissaient par un double point-virgule
;;
. Cependant, si vous regardez Ă du code OCaml en dehors de ces
tutoriaux, vous trouverez de codes complets qui n'utilisent pas
;;
,
pas mĂŞme une seule fois.
La vérité est que
;;
est principalement utilisé dans la boucle
interactive (
toplevel
) et les tutoriaux pour marquer la fin d'une
phrase OCaml et l'envoyer au toplevel pour évaluation.
En dehors du toplevel, l'usage de
;;
est, au mieux, infréquent et
n'est
jamais requis
pour du code bien écrit.
En bref, le double point-virgule
;;
peut être utilisé pour trois
raisons :
compatibilité avec le toplevel ;
couper le code pour faciliter de débogage;
introduire une expression « au plus haut niveau ».
Insérer
;;
peut parfois se révéler utile pour les débutants lors du
débogage vu qu'il conclut la définition en cours. Dans l'exemple
suivant, la définition de
f
ne s'arrĂŞte pas Ă la ligne 1 Ă cause de
la virgule
,
. Par conséquent, le compilateur va générer une erreur
à la fin de la seconde ligne :
let
f
x
= x,
let
g
= x * x
Insérer un double point-virgule entre
f
et
g
sépare les
définitions de
f
de
g
 :
let
f
x
= x,
;;
let
g
= x * x
Un autre usage de
;;
est d'introduire une expression après des
définitions :
let
b
=
"This started
with
"
let
s
=
"a very nonsensical message."
;;
print_endline b; print_endline s
open
String
let
s
= concat
""
[
"Fortunately"
;
", "
;
"the"
;
"
end
"
;
"is"
;
"near"
;
"."
]
;;
print_endline s
;;
let
s
=
"
let
end
here"
in
print_endline s
En particulier, dans les exemples ci-dessus, toutes les lignes
démarrant après un
;;
produisent des effets de bord et les effacer
changerait uniquement l'effet du code, pas des définitions.
Cependant, cette utilisation de
;;
peut (devrait) toujours ĂŞtre
remplacée par soit
let
() = expression ()
si le résultat est de type
unit
, soit
let
_
= expression ()
sinon. Notez que la première forme est plus sure puisqu'elle requiert
que le type retourné par l'expression est
unit
, nous prémunissant,
par exemple, de l'oubli d'un argument comme dans
let
() =
print_newline
(* Ici, nous avons oublié () et le compilateur va se plaindre. *)
Avec cette convention, il n'y a plus d'expressions « au plus haut
niveau » : tout module peut être écrit comme une suite de
définitions. Par conséquent, des directives de style considèrent que
;;
ne devrait jamais être utilisé en dehors de la boucle interactive
(voir par exemple «Â
style guidelines
 »).
Toutes ces notions ensemble : un exemple de code réel
Dans cette section nous allons regarder quelques fragments de vrai code
pris dans la bibliothèque lablgtk 1.2. (Lablgtk est la bibliothèque
OCaml d'interfaçage avec la bibliothèque de widgets Gtk, native sous
Unix). Un avertissement : ces fragments utilisent beaucoup de concepts
que nous n'avons pas encore abordé. Ne vous arrêtez pas sur les détails,
regardez plutôt la forme générale du code, comment les auteurs ont
utilisé
;;
, où ils ont utilisé
;
et où ils ont utilisé
open
,
comment ils ont indenté le code, et utilisé des expressions nommées
locales ou globales.
... Je vais quand mĂŞme vous donner quelques indices pour que vous ne
soyez pas totalement perdus !
?foo
et
~foo
est la façon de passer des paramètres optionels et
nommés en OCaml. Il n'y a pas vraiment d'équivalent dans les
langages de la famille de C, mais Perl, Python et Smalltalk
permettent tous de nommer les arguments d'un appel de fonction, en
oublier certains, et fournir les autres dans n'importe quel ordre.
foo#bar
est un appel de méthode (appel de la méthode nommée
bar
de l'objet nommé
foo
). C'est l'équivalent de
foo->bar
, ou
foo.bar
ou
$foo->bar
en C++, Java et Perl, respectivement.
Premier fragment : Le programmeur ouvre une paire de modules standards
(en omettant le
;;
parce que les mots clés suivants sont
open
et
let
, respectivement). Il crée ensuite une fonction nommée
file_dialog
. Dans cette fonction il définit une expression nommée
appelée
sel
en utilisant une instruction
let sel = ... in
de deux
lignes. Puis il appelle plusieurs méthodes de
sel
.
(* First snippet *)
open
StdLabels
open
GMain
let
file_dialog
~title ~callback ?filename ()
=
let
sel
=
GWindow
.file_selection ~title ~modal:true ?filename ()
in
sel#cancel_button#connect#clicked ~callback:sel#destroy;
sel#ok_button#connect#clicked ~callback:do_ok;
sel#show ()
Deuxième fragment : Juste une longue liste de définitions au niveau
global. Remarquez que l'auteur a omis toutes les occurrences de
;;
grâce à la règle n°2.
(* Second snippet *)
let
window
=
GWindow
.window ~width:500 ~height:300 ~title:
"editor"
()
let
vbox
=
GPack
.vbox ~packing:window#add ()
let
menubar
=
GMenu
.menu_bar ~packing:vbox#pack ()
let
factory
=
new
GMenu
.factory menubar
let
accel_group
= factory#accel_group
let
file_menu
= factory#add_submenu
"File"
let
edit_menu
= factory#add_submenu
"Edit"
let
hbox
=
GPack
.hbox ~packing:vbox#add ()
let
editor
=
new
editor ~packing:hbox#add ()
let
scrollbar
=
GRange
.scrollbar `VERTICAL ~packing:hbox#pack ()
Troisième fragment : L'auteur importe tous les symboles du module
GdkKeysyms
. Puis nous avons un let-binding inhabituel.
let _ = expression
signifie "évalue la valeur de expression (avec tous
les effets de bords que cela comporte), puis jette le résultat". En
l'occurrence, "calcule la valeur de l'expression" signifie exécuter
Main.main ()
qui est la boucle principale de Gtk, qui a pour effet de
bord d'ouvrir la fenêtre de l'application à l'écran. Le "résultat" de
Main.main ()
est sans importance - probablement la valeur
unit
, mais
je n'ai pas vérifié - et elle n'est retournée que quand l'application
est en train de se terminer.
Remarquez que dans ce fragment nous avons de longues successions de
commandes procédurales. C'est un programme impératif classique.
(* Third snippet *)
open
GdkKeysyms
let
_
=
window#connect#destroy ~callback:
Main
.quit;
let
factory
=
new
GMenu
.factory file_menu ~accel_group
in
factory#add_item
"
Open
..."
~key:_O ~callback:editor#open_file;
factory#add_item
"Save"
~key:_S ~callback:editor#save_file;
factory#add_item
"Save
as
..."
~callback:editor#save_dialog;
factory#add_separator ();
factory#add_item
"Quit"
~key:_Q ~callback:window#destroy;
let
factory
=
new
GMenu
.factory edit_menu ~accel_group
in
factory#add_item
"Copy"
~key:_C ~callback:editor#text#copy_clipboard;
factory#add_item
"Cut"
~key:_X ~callback:editor#text#cut_clipboard;
factory#add_item
"Paste"
~key:_V ~callback:editor#text#paste_clipboard;
factory#add_separator ();
factory#add_check_item
"Word wrap"
~active:false
~callback:editor#text#set_word_wrap;
factory#add_check_item
"Read only"
~active:false
~callback:(
fun
b -> editor#text#set_editable (not b));
window#add_accel_group accel_group;
editor#text#event#connect#button_press
~callback:(
fun
ev ->
let
button
=
GdkEvent
.
Button
.button ev
in
if
button = 3
then
begin
file_menu#popup ~button ~time:(
GdkEvent
.
Button
.time ev); true
end
else
false);
editor#text#set_vadjustment scrollbar#adjustment;
window#show ();
Main
.main () |
| Markdown | [](https://coherentpdf.com/index.fr.html)
- [Apprendre](https://coherentpdf.com/learn/index.fr.html)
- [Documentation](https://coherentpdf.com/docs/index.fr.html)
- [Paquets](https://opam.ocaml.org/)
- [Communauté](https://coherentpdf.com/community/index.fr.html)
- [Actualités](https://coherentpdf.com/community/planet/)
[Éditer cette page](https://github.com/ocaml/ocaml.org/tree/master/site/learn/tutorials/structure_of_ocaml_programs.fr.md "Éditer cette page")
1. [Home](https://coherentpdf.com/)
2. [Apprendre](https://coherentpdf.com/learn/index.fr.html)
3. [Tutoriel OCaml](https://coherentpdf.com/learn/tutorials/index.fr.html)
4. Structure des programmes OCaml
- [en](https://coherentpdf.com/tmp/structure_of_ocaml_programs.html)
- fr
- [it](https://coherentpdf.com/tmp/structure_of_ocaml_programs.it.html)
- [日本語](https://coherentpdf.com/tmp/structure_of_ocaml_programs.ja.html)
- [한ęµě–´](https://coherentpdf.com/tmp/structure_of_ocaml_programs.ko.html)
- [ä¸ć–‡](https://coherentpdf.com/tmp/structure_of_ocaml_programs.zh.html)
- [Contenu](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html)
- [« Variables » locales (*plus exactement* expressions locales)](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#Variables-locales-plus-exactement-expressions-locales)
- [« Variables » globales (*plus précisément* expressions globales)](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#Variables-globales-plus-pr-cis-ment-expressions-globales)
- [Let-binding (?)](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#Let-binding)
- [Références : les vraies variables](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#R-f-rences-les-vraies-variables)
- [Fonctions imbriquées](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#Fonctions-imbriqu-es)
- [Modules et `open`](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#Modules-et-open)
- [Le module `Pervasives`](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#Le-module-Pervasives)
- [Renommage de modules](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#Renommage-de-modules)
- [L'opérateur de séquence `;`](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#L-39-op-rateur-de-s-quence)
- [La disparition de `;;`](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#La-disparition-de)
- [Toutes ces notions ensemble : un exemple de code réel](https://coherentpdf.com/tmp/structure_of_ocaml_programs.fr.html#Toutes-ces-notions-ensemble-un-exemple-de-code-r-el)
```
```
\#
# Structure des programmes OCaml
Maintenant nous allons passer un peu de temps à observer la structure d'ensemble de quelque vrais programmes en OCaml. Je veux vous parler des définitions locales et globales, quand utiliser `;;` ou `;`, les modules, les fonctions imbriquées, les références. Pour cela nous allons passer sur plein de concepts d'OCaml qui ne vous diront rien car nous ne les avons pas encore évoqués. Ne vous embarrassez pas des détails pour le moment. Occupez-vous uniquement de la forme générale des programmes, et aux fonctionnalités mises en avant.
## « Variables » locales (*plus exactement* expressions locales)
Prenons la fonction `average` écrite en C, et ajoutons lui une variable locale. (à comparer avec la première version que nous avions)
```
double
average (double a, double b)
{
double sum = a + b;
return sum / 2;
}
```
Faisons la mĂŞme chose avec notre version OCaml :
```
# let average a b =
let sum = a +. b in
sum /. 2.0;;
val average : float -> float -> float = <fun>
```
La formule standard `let nom = expression in` sert à définir une expression locale nommée, et `nom` peut ensuite être utilisé dans la fonction à la place de `expression` jusqu'au `;;` qui marque la fin du bloc de code. Remarquez que nous n'avons pas changé l'indentation après `in`. Considérez `let ... in` comme s'il s'agissait d'une instruction.
Maintenant, traduire les variables locales en C par ces expressions locales est une supercherie. En fait ils sont un peu différents. La variable C `sum` a un emplacement réservé dans la pile. Vous pouvez changer la valeur associée à `sum` plus loin dans la fonction si vous voulez, ou même prendre l'adresse de `sum`. Ce n'est PAS possible dans la version OCaml. Dans la version OCaml, `sum` est juste une abréviation pour l'expression `a +. b`. Il est impossible d'assigner ou de changer la valeur de `sum`. (Nous verrons comment avoir de vraies variables un peu plus loin).
Voyons un autre exemple qui devrait clarifier les choses. Les deux bouts de code suivants devraient retourner la mĂŞme valeur (Ă savoir (a+b) + (a+b)2):
```
# let f a b =
(a +. b) +. (a +. b) ** 2.;;
val f : float -> float -> float = <fun>
# let f a b =
let x = a +. b in
x +. x ** 2.;;
val f : float -> float -> float = <fun>
```
Il se peut que la seconde version soit plus rapide (bien que la plupart des compilateurs devraient être capable de faire cette étape d'"élimination de sous-expressions communes" à votre place), et en tout cas elle est plus lisible. Dans le second exemple, `x` est une abréviation pour l'expression `a +. b`.
## « Variables » globales (*plus précisément* expressions globales)
Vous pouvez aussi donner des noms à des choses dans l'environnement global, et comme pour les "variables" locales ci-dessus, ce ne sont pas vraiment des variables, mais des abbréviations pour ces choses. Voici un exemple pris dans un programme réel :
```
let html =
let content = read_whole_file file in
GHtml.html_from_string content
;;
let menu_bold () =
match bold_button#active with
| true -> html#set_font_style ~enable:[`BOLD] ()
| false -> html#set_font_style ~disable:[`BOLD] ()
;;
let main () =
(* code omitted *)
factory#add_item "Cut" ~key:_X ~callback: html#cut
;;
```
Dans ce vrai morceau de code, `html` est un "widget" ("contrôle") d'édition de HTML (un objet provenant de la bibliothèque lablgtk), créé une fois pour toute au début du programme par la première instruction `let html =`. Il y est ensuite fait référence dans plusieurs des fonctions suivantes.
A noter que le nom `html` dans le bout de programme ci-dessus ne devrait pas être comparé à une vraie variable globale comme en C ou dans d'autres langages impératifs. Aucun emplacement n'est réservé pour "stocker" le "pointeur sur `html`". Il n'est pas non plus possible d'assigner quelque chose à `html`, par exemple pour lui faire désigner un autre widget. Dans la section suivante nous parlerons des références, qui sont de vraies variables.
## Let-binding (?)
Les utilisations de `let ...` aussi bien au niveau global que dans une fonction, sont souvent appellées des **let-binding**.
## Références : les vraies variables
Comment faire si vous voulez une vraie variable que vous pouvez assigner puis changer à votre gré dans votre programme ? Il vous faut une **référence**. Les références sont similaires aux pointeurs en C/C++. En Java, toutes les variables qui stockent des objets sont en fait des références (pointeurs) sur ces objets. En Perl, les références sont des références - comme en OCaml.
Voici comment créer une référence sur un entier (`int`) en OCaml :
```
# ref 0;;
- : int ref = {contents = 0}
```
En fait cette instruction n'est pas très utile. Nous avons créé une référence et puis, comme elle n'a pas de nom, le glaneur de cellules a fait son travail et l'a immédiatement libérée! (Il est même probable que le compileur ait éliminé cette instruction à la compilation). Donnons un nom à cette référence :
```
# let my_ref = ref 0;;
val my_ref : int ref = {contents = 0}
```
Cette référence contient maintenant l'entier 0. Mettons une autre valeur à la place (assignement) :
```
# my_ref := 100;;
- : unit = ()
```
Voyons ce que la référence contient à présent :
```
# !my_ref;;
- : int = 100
```
Donc l'opérateur `:=` est utilisé pour assigner les références, et `!` pour les déréférencer et accéder au contenu. Voici une comparaison approximative avec C/C++ :
```
OCaml C/C++
let my_ref = ref 0;; int a = 0; int *my_ptr = &a;
my_ref := 100;; *my_ptr = 100;
!my_ref *my_ptr
```
Les références ont leurs usages, mais vous vous apercevrez que vous ne les utiliserez pas si souvent. La plupart du temps, vous utiliserez `let nom = expression in` pour nommer des expressions locales dans vos définitions de fonctions.
## Fonctions imbriquées
Le langage C n'a pas réellement de notion de fonctions imbriquées. GCC les supporte, mais je ne connais pas de programme qui en tire parti. En tout cas, voici ce que la page info de gcc dit sur les fonctions imbriquées :
Une « fonction imbriquée » est une fonction définie à l'intérieur d'une autre fonction (les fonctions imbriquées ne sont pas supportées par GNU C++.) Le nom de la fonction imbriquée est local au bloc dans lequel il est défini. Par exemple, définissons une fonction imbriquée nommée `square`, et appelons-là deux fois :
```
foo (double a, double b)
{
double square (double z) { return z * z; }
return square (a) + square (b);
}
```
La fonction imbriquée a accès à toutes les variables de la fonction englobante qui sont visibles à l'emplacement de sa définition. Cela s'appelle la « portée lexicale ». Par exemple, voici une fonction imbriquée qui utilise une variable héritée nommée `offset` :
```
bar (int *array, int offset, int size)
{
int access (int *array, int index)
{ return array[index + offset]; }
int i;
/* ... */
for (i = 0; i < size; i++)
/* ... */ access (array, i) /* ... */
}
```
Vous comprenez l'idée. Les fonctions imbriquées sont, cependant, très utiles et largement employées en OCaml. Voici un exemple d'utilisation réel de fonction imbriquées :
```
# let read_whole_channel chan =
let buf = Buffer.create 4096 in
let rec loop () =
let newline = input_line chan in
Buffer.add_string buf newline;
Buffer.add_char buf '\n';
loop ()
in
try
loop ()
with
End_of_file -> Buffer.contents buf;;
val read_whole_channel : in_channel -> string = <fun>
```
Ne vous inquiétez pas de ce que ce code fait — il utilise beaucoup de concepts que nous n'avons pas encore vu dans ce tutorial. Concentrez-vous plutôt sur la fonction imbriquée principale nommée `loop` qui ne prend qu'un argument unit. On peut appeler `loop ()` depuis l'intérieur de la fonction `read_whole_channel`, mais elle n'est pas définie en dehors de cette fonction. La fonction imbriquée peut accéder aux variables définies dans la fonction principale (ici `loop` accède au nom local `buf`.)
La syntaxe des fonctions imbriquées est la même que pour nommer les expressions locales : `let nom arguments = définition de fonction in`.
Normalement, vous indenterez la définition de fonction, après être passé à la ligne comme dans l'exemple précédent, et n'oubliez pas d'utiliser `let rec` à la place de `let` si votre fonction est récursive (comme dans cet exemple).
## Modules et `open`
OCaml est accompagné d'une quantité de modules amusants et intéressants, de bibliothèques de code utile. Par exemple on y trouve une bibliothèque pour dessiner des graphismes, interagir avec la collection de contrôles de l'interface-utilisateur graphique, pour manipuler des grands nombres, des structures de données, ou pour faire des appels systèmes POSIX. Ces bibliothèques se trouvent dans `/usr/lib/ocaml/` (sous Unix en tout cas). Pour ces exemples, nous allons nous concentrer sur un module relativement simple appelé `Graphics`.
Le module `Graphics` est constitué de sept fichiers (sur mon système) :
```
/usr/lib/ocaml/graphics.a
/usr/lib/ocaml/graphics.cma
/usr/lib/ocaml/graphics.cmi
/usr/lib/ocaml/graphics.cmx
/usr/lib/ocaml/graphics.cmxa
/usr/lib/ocaml/graphics.cmxs
/usr/lib/ocaml/graphics.mli
```
Pour l'instant occupons-nous de `graphics.mli`. C'est un fichier texte, que vous pouvez donc lire dès à présent. Notez que son nom est `graphics.mli` et non `Graphics.mli`. OCaml met automatiquement une capitale au nom du fichier pour obtenir le nom du module. C'est déroutant quand on ne le sait pas \!
Il y a deux moyens pour utiliser les fonctions de `Graphics`. Ou bien on commence son programme par la déclaration `open Graphics;;`, ou bien on préfixe tous les appels de fonctions comme ceci : `Graphics.open_graph`. `open` ressemble un peu à l'instruction `import` en Java, ou plus encore comme l'instruction `use` en Perl.
Pour utiliser les module `Graphics` dans la boucle interactive, il faut au préalable charger la librairie avec
```
#load "graphics.cma";;
```
Une paire d'exemples devrait éclaircir ce point. (Les deux exemples dessinent des choses différentes — essayez-les). Remarquez que le premier exemple appelle `open_graph` et le second `Graphics.open_graph`.
```
(* To compile this example: ocamlc graphics.cma grtest1.ml -o grtest1 *)
open Graphics;;
open_graph " 640x480";;
for i = 12 downto 1 do
let radius = i * 20 in
set_color (if (i mod 2) = 0 then red else yellow);
fill_circle 320 240 radius
done;;
read_line ();;
(* To compile this example: ocamlc graphics.cma grtest2.ml -o grtest2 *)
Random.self_init ();;
Graphics.open_graph " 640x480";;
let rec iterate r x_init i =
if i = 1 then x_init
else
let x = iterate r x_init (i-1) in
r *. x *. (1.0 -. x);;
for x = 0 to 639 do
let r = 4.0 *. (float_of_int x) /. 640.0 in
for i = 0 to 39 do
let x_init = Random.float 1.0 in
let x_final = iterate r x_init 500 in
let y = int_of_float (x_final *. 480.) in
Graphics.plot x y
done
done;;
read_line ();;
```
Ces deux exemples utilisent des fonctionnalités dont nous n'avons pas encore parlé : boucles "for" dans le style impératif, if-then-else, et récursion. Nous en parlerons plus tard. Examinez-les cependant, et essayez de comprendre (1) comment ils fonctionnent, et (2) comment l'inférence de type aide à éliminer des bugs.
## Le module `Pervasives`
Il y a un module que vous n'avez jamais besoin d'ouvrir avec `open`, c'est le module `Pervasives` (allez jeter un oeil sur `/usr/lib/ocaml/VERSION/pervasives.mli`). Tous les symboles définis dans le module `Pervasives` sont implicitement importés dans tous les programmes OCaml.
## Renommage de modules
Que faire si vous voulez utiliser des symboles en provenance du module `Graphics`, mais ne voulez pas tous les importer, et n'avez pas envie de taper `Graphics.` Ă chaque fois ? Renommez le module avec cette astuce :
```
module Gr = Graphics;;
Gr.open_graph " 640x480";;
Gr.fill_circle 320 240 240;;
read_line ();;
```
Cela devient vraiment utile quand vous voulez utiliser les symboles d'un module imbriqué (les modules peuvent être imbriqués les uns dans les autres, eux-aussi), mais ne voulez pas avoir à taper à chaque fois le chemin d'accès au module imbriqué.
## L'opérateur de séquence `;`
Le point-virgule `;` est un opérateur, comme `+`. Bon, pas tout à fait comme `+` mais conceptuellement le même. L'opérateur `+` a pour type `int -> int -> int` — il prend deux entiers et retourne un entier (la somme des deux entrées). Le point-virgule `;` peut être vu comme ayant le type `unit -> 'b -> 'b` — il prend deux valeurs et simplement retourne la seconde, la première expression étant garantie d'être évaluée avant la seconde. C'est donc comme l'opérateur `,` (virgule) en C. Vous pouvez écrire `a; b; c; d` de la même manière que vous pouvez écrire `a + b + c + d`.
C'est un de ces « sauts conceptuels » qui n'est pas toujours explicité très bien : en OCaml, presque tout est une expression. `if/then/else` est une expression. `a; b` est une expression. `match foo with ...` est une expression. Le code qui suit est parfaitement légal (et toutes les lignes font la même chose);
```
# let f x b y = if b then x+y else x+0
let f x b y = x + (if b then y else 0)
let f x b y = x + (match b with true -> y | false -> 0)
let f x b y = x + (let g z = function true -> z | false -> 0 in g y b)
let f = fun x -> fun b -> fun y -> if b then x+y else x+0
let f x b y = x + (let _ = y + 3 in (); if b then y else 0);;
val f : int -> bool -> int -> int = <fun>
```
Notez particulièrement la dernière ligne où j'utilise l'opérateur `;` pour « joindre » deux instructions. Toutes les fonctions en OCaml peuvent être déclarées sous la forme :
```
let name [parameters] = expression ;;
```
En OCaml, la définition de ce qu'est une expression est simplement un peu plus large qu'en C. En fait, C a le concept d'« instructions » — mais toutes les instructions en C sont simplement des expressions en OCaml de type `unit` (combinées avec l'opérateur `;`).
La différence entre `;` et `+` est qu'on peut utiliser `+` comme une fonction. Par exemple, on peut définir une fonction `sum_list`, qui somme une liste d'entier, par
```
# let sum_list = List.fold_left ( + ) 0;;
val sum_list : int list -> int = <fun>
```
## La disparition de `;;`
Maintenant, nous allons nous pencher sur un point très important. Tous les exemples ci-dessus finissaient par un double point-virgule `;;`. Cependant, si vous regardez à du code OCaml en dehors de ces tutoriaux, vous trouverez de codes complets qui n'utilisent pas `;;`, pas même une seule fois.
La vérité est que `;;` est principalement utilisé dans la boucle interactive (*toplevel*) et les tutoriaux pour marquer la fin d'une phrase OCaml et l'envoyer au toplevel pour évaluation.
En dehors du toplevel, l'usage de `;;` est, au mieux, infréquent et n'est *jamais requis* pour du code bien écrit. En bref, le double point-virgule `;;` peut être utilisé pour trois raisons :
- compatibilité avec le toplevel ;
- couper le code pour faciliter de débogage;
- introduire une expression « au plus haut niveau ».
Insérer `;;` peut parfois se révéler utile pour les débutants lors du débogage vu qu'il conclut la définition en cours. Dans l'exemple suivant, la définition de `f` ne s'arrête pas à la ligne 1 à cause de la virgule `,`. Par conséquent, le compilateur va générer une erreur à la fin de la seconde ligne :
```
let f x = x,
let g = x * x
```
Insérer un double point-virgule entre `f` et `g` sépare les définitions de `f` de `g` :
```
let f x = x,
;;
let g = x * x
```
Un autre usage de `;;` est d'introduire une expression après des définitions :
```
let b = "This started with"
let s = "a very nonsensical message.";;
print_endline b; print_endline s
open String
let s = concat "" ["Fortunately"; ", "; "the"; "end"; "is"; "near"; "."];;
print_endline s;;
let s = "let end here" in print_endline s
```
En particulier, dans les exemples ci-dessus, toutes les lignes démarrant après un `;;` produisent des effets de bord et les effacer changerait uniquement l'effet du code, pas des définitions.
Cependant, cette utilisation de `;;` peut (devrait) toujours être remplacée par soit
```
let () = expression ()
```
si le résultat est de type `unit`, soit
```
let _ = expression ()
```
sinon. Notez que la première forme est plus sure puisqu'elle requiert que le type retourné par l'expression est `unit`, nous prémunissant, par exemple, de l'oubli d'un argument comme dans
```
let () =
print_newline
(* Ici, nous avons oublié () et le compilateur va se plaindre. *)
```
Avec cette convention, il n'y a plus d'expressions « au plus haut niveau » : tout module peut être écrit comme une suite de définitions. Par conséquent, des directives de style considèrent que `;;` ne devrait jamais être utilisé en dehors de la boucle interactive (voir par exemple « [style guidelines](https://coherentpdf.com/tmp/guidelines.html) »).
## Toutes ces notions ensemble : un exemple de code réel
Dans cette section nous allons regarder quelques fragments de vrai code pris dans la bibliothèque lablgtk 1.2. (Lablgtk est la bibliothèque OCaml d'interfaçage avec la bibliothèque de widgets Gtk, native sous Unix). Un avertissement : ces fragments utilisent beaucoup de concepts que nous n'avons pas encore abordé. Ne vous arrêtez pas sur les détails, regardez plutôt la forme générale du code, comment les auteurs ont utilisé `;;`, où ils ont utilisé `;` et où ils ont utilisé `open`, comment ils ont indenté le code, et utilisé des expressions nommées locales ou globales.
... Je vais quand mĂŞme vous donner quelques indices pour que vous ne soyez pas totalement perdus \!
- `?foo` et `~foo` est la façon de passer des paramètres optionels et nommés en OCaml. Il n'y a pas vraiment d'équivalent dans les langages de la famille de C, mais Perl, Python et Smalltalk permettent tous de nommer les arguments d'un appel de fonction, en oublier certains, et fournir les autres dans n'importe quel ordre.
- `foo#bar` est un appel de méthode (appel de la méthode nommée `bar` de l'objet nommé `foo`). C'est l'équivalent de `foo->bar`, ou `foo.bar` ou `$foo->bar` en C++, Java et Perl, respectivement.
Premier fragment : Le programmeur ouvre une paire de modules standards (en omettant le `;;` parce que les mots clés suivants sont `open` et `let`, respectivement). Il crée ensuite une fonction nommée `file_dialog`. Dans cette fonction il définit une expression nommée appelée `sel` en utilisant une instruction `let sel = ... in` de deux lignes. Puis il appelle plusieurs méthodes de `sel`.
```
(* First snippet *)
open StdLabels
open GMain
let file_dialog ~title ~callback ?filename () =
let sel =
GWindow.file_selection ~title ~modal:true ?filename () in
sel#cancel_button#connect#clicked ~callback:sel#destroy;
sel#ok_button#connect#clicked ~callback:do_ok;
sel#show ()
```
Deuxième fragment : Juste une longue liste de définitions au niveau global. Remarquez que l'auteur a omis toutes les occurrences de `;;` grâce à la règle n°2.
```
(* Second snippet *)
let window = GWindow.window ~width:500 ~height:300 ~title:"editor" ()
let vbox = GPack.vbox ~packing:window#add ()
let menubar = GMenu.menu_bar ~packing:vbox#pack ()
let factory = new GMenu.factory menubar
let accel_group = factory#accel_group
let file_menu = factory#add_submenu "File"
let edit_menu = factory#add_submenu "Edit"
let hbox = GPack.hbox ~packing:vbox#add ()
let editor = new editor ~packing:hbox#add ()
let scrollbar = GRange.scrollbar `VERTICAL ~packing:hbox#pack ()
```
Troisième fragment : L'auteur importe tous les symboles du module `GdkKeysyms`. Puis nous avons un let-binding inhabituel. `let _ = expression` signifie "évalue la valeur de expression (avec tous les effets de bords que cela comporte), puis jette le résultat". En l'occurrence, "calcule la valeur de l'expression" signifie exécuter `Main.main ()` qui est la boucle principale de Gtk, qui a pour effet de bord d'ouvrir la fenêtre de l'application à l'écran. Le "résultat" de `Main.main ()` est sans importance - probablement la valeur `unit`, mais je n'ai pas vérifié - et elle n'est retournée que quand l'application est en train de se terminer.
Remarquez que dans ce fragment nous avons de longues successions de commandes procédurales. C'est un programme impératif classique.
```
(* Third snippet *)
open GdkKeysyms
let _ =
window#connect#destroy ~callback:Main.quit;
let factory = new GMenu.factory file_menu ~accel_group in
factory#add_item "Open..." ~key:_O ~callback:editor#open_file;
factory#add_item "Save" ~key:_S ~callback:editor#save_file;
factory#add_item "Save as..." ~callback:editor#save_dialog;
factory#add_separator ();
factory#add_item "Quit" ~key:_Q ~callback:window#destroy;
let factory = new GMenu.factory edit_menu ~accel_group in
factory#add_item "Copy" ~key:_C ~callback:editor#text#copy_clipboard;
factory#add_item "Cut" ~key:_X ~callback:editor#text#cut_clipboard;
factory#add_item "Paste" ~key:_V ~callback:editor#text#paste_clipboard;
factory#add_separator ();
factory#add_check_item "Word wrap" ~active:false
~callback:editor#text#set_word_wrap;
factory#add_check_item "Read only" ~active:false
~callback:(fun b -> editor#text#set_editable (not b));
window#add_accel_group accel_group;
editor#text#event#connect#button_press
~callback:(fun ev ->
let button = GdkEvent.Button.button ev in
if button = 3 then begin
file_menu#popup ~button ~time:(GdkEvent.Button.time ev); true
end else false);
editor#text#set_vadjustment scrollbar#adjustment;
window#show ();
Main.main ()
```
# [Apprendre](https://coherentpdf.com/learn/index.fr.html)
- [Exemples de code](https://coherentpdf.com/learn/taste.fr.html)
- [Tutoriels](https://coherentpdf.com/learn/tutorials/index.fr.html)
- [Livres](https://coherentpdf.com/learn/books.html)
- [Cas d'usage](https://coherentpdf.com/learn/success.fr.html)
# [Documentation](https://coherentpdf.com/docs/index.fr.html)
- [Installer OCaml](https://coherentpdf.com/docs/install.fr.html)
- [Manuel](http://caml.inria.fr/pub/docs/manual-ocaml/)
- [Paquets](https://opam.ocaml.org/packages/)
- [Compiler Releases](https://coherentpdf.com/releases/index.fr.html)
- [Logos](https://coherentpdf.com/docs/logos.html)
# [Communauté](https://coherentpdf.com/community/index.fr.html)
- [Lieux de discussion](https://coherentpdf.com/community/mailing_lists.fr.html)
- [Rencontres](https://coherentpdf.com/meetings/index.fr.html)
- [Actualités](https://coherentpdf.com/community/planet/)
- [Support](https://coherentpdf.com/community/support.fr.html)
- [Signaler un bug d'OCaml](http://caml.inria.fr/mantis/my_view_page.php)
# Site Web
- [Éditer cette page](https://github.com/ocaml/ocaml.org/tree/master/site/learn/tutorials/structure_of_ocaml_programs.fr.md)
- [Problèmes du site Web](https://github.com/ocaml/ocaml.org/issues)
- [Ă€ propos de ce site](https://coherentpdf.com/about.fr.html)
- [Dépôt GitHub](https://github.com/ocaml/ocaml.org/)
- [Crédits](https://coherentpdf.com/contributors.fr.html) |
| Readable Markdown | null |
| Shard | 35 (laksa) |
| Root Hash | 11981928348964667835 |
| Unparsed URL | com,coherentpdf!/tmp/structure_of_ocaml_programs.fr.html s443 |