You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: data/tutorials/language/1ms_00_modules.md
+70-71Lines changed: 70 additions & 71 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,29 +21,27 @@ Modules are collections of definitions grouped together. This is the basic means
21
21
### File-Based Modules
22
22
23
23
In OCaml, every piece of code is wrapped into a module. Optionally, a module
24
-
itself can be a submodule of another module, pretty much like directories in a
25
-
file system.
24
+
itself can be a [submodule](#submodules) of another module, pretty much like
25
+
directories in a file system.
26
26
27
-
When you write a program using two files named `athens.ml` and `berlin.ml`,
28
-
each automatically defines a module named `Athens` and `Berlin`, which provides
29
-
whatever you put into the files.
27
+
Here is a program using two files: `athens.ml` and `berlin.ml`. Each file
28
+
defines a module named `Athens` and `Berlin`, respectively.
30
29
31
-
Here is the code in the file `athens.ml`:
30
+
Here is the file `athens.ml`:
32
31
<!-- $MDX file=examples/athens.ml -->
33
32
```ocaml
34
33
let hello () = print_endline "Hello from Athens"
35
34
```
36
35
37
-
This is what is in`berlin.ml`:
36
+
Here is the file`berlin.ml`:
38
37
<!-- $MDX file=examples/berlin.ml -->
39
38
```ocaml
40
39
let () = Athens.hello ()
41
40
```
42
41
43
42
To compile them using [Dune](https://dune.build/), at least two
44
43
configuration files are required:
45
-
* The `dune-project` file contains project-wide configuration data.
46
-
Here's a very minimal one:
44
+
* The `dune-project` file contains project-wide configuration.
47
45
```lisp
48
46
(lang dune 3.7)
49
47
```
@@ -54,27 +52,24 @@ configuration files are required:
54
52
(executable (name berlin))
55
53
```
56
54
57
-
Here is a possible way to create those files, build the source, and run the
58
-
executable:
55
+
After you create those files, build and run them:
59
56
<!-- $MDX dir=examples -->
60
57
```bash
61
-
$ echo"(lang dune 3.7)"> dune-project
62
-
63
-
$ echo"(executable (name berlin))"> dune
64
-
65
58
$ dune build
66
59
67
60
$ dune exec ./berlin.exe
68
-
Hello
61
+
Hello from Athens
69
62
```
70
63
71
-
Actually, `dune build` is optional. Running `dune exec` would have triggered the
72
-
compilation. Note that in the `dune exec` command, the parameter `./berlin.exe`
73
-
is not a file path. This command means “execute the content of the file
74
-
`./berlin.ml`.” However, the executable file is stored and named differently.
64
+
Actually, `dune build` is optional. Running `dune exec ./berlin.exe` would have
65
+
triggered the compilation. Note that in the `dune exec` command, the parameter
66
+
`./berlin.exe` is not a file path. This command means “execute the content of
67
+
the file `./berlin.ml`.” However, the executable file is stored and named
68
+
differently.
75
69
76
70
In a project, it is preferable to create the `dune` configuration files and
77
-
directory structure using the `dune init project` command.
71
+
directory structure using the `dune init project` command. Refer to the Dune
72
+
documentation for more on this matter.
78
73
79
74
### Naming and Scoping
80
75
@@ -84,38 +79,40 @@ always starts with a capital letter: `Athens`) followed by a dot and the
84
79
thing you want to use (`hello`). It may be a value, a type constructor, or
85
80
anything the module provides.
86
81
87
-
If you are using a module heavily, you can directly access its contents. To do
88
-
this, use the `open` directive. In our example, `berlin.ml` could have been
82
+
If you are using a module heavily, you might want to `open` it. This brings the
83
+
module's definitions into scope. In our example, `berlin.ml` could have been
89
84
written:
90
85
<!-- $MDX skip -->
91
86
```ocaml
92
87
open Athens
93
88
let () = hello ()
94
89
```
95
90
96
-
Using `open` is optional. Usually, we don't open the module `List` because it
97
-
provides names other modules also provide, such as `Array` or `Option`. Other
98
-
modules like `Printf` provide names that aren't subject to conflicts, such as
99
-
`printf`. Placing `open Printf` at the beginning of the file avoids writing
100
-
`Printf.printf` all over the place.
91
+
Using `open` is optional. Usually, we don't open a module like `List` because it
92
+
provides names other modules also provide, such as `Array` or `Option`. Modules
93
+
like `Printf` provide names that aren't subject to conflicts, such as `printf`.
94
+
Placing `open Printf` at the top of a file avoids writing `Printf.printf` repeatedly.
101
95
```ocaml
102
96
open Printf
103
97
let data = ["a"; "beautiful"; "day"]
104
98
let () = List.iter (printf "%s\n") data
105
99
```
106
100
107
-
The standard library is a module called `Stdlib` where modules `List`,
108
-
`Option`, `Either`, and others are [submodules](#submodules). Implicitly, all
109
-
OCaml begins with `open Stdlib`. That avoids writing `Stdlib.List.map`,
110
-
`Stdlib.Array`, or using `Stdlib.` anywhere.
101
+
The standard library is a module called `Stdlib`. It contains
102
+
[submodules](#submodules)`List`, `Option`, `Either`, and more. By default, the
103
+
OCaml compiler opens the standard library, as if you had written `open Stdlib`
104
+
at the top of every file. Refer to Dune documentation if you need to opt-out.
111
105
112
-
There are also two means to open modules locally:
106
+
You can open a module inside a definition, using the `let open ... in` construct:
113
107
```ocaml
114
108
# let list_sum_sq m =
115
109
let open List in
116
110
init m Fun.id |> map (fun i -> i * i) |> fold_left ( + ) 0;;
117
111
val list_sum_sq : int -> int = <fun>
112
+
```
118
113
114
+
The module access notation can be applied to an entire expression:
115
+
```ocaml
119
116
# let array_sum_sq m =
120
117
Array.(init m Fun.id |> map (fun i -> i * i) |> fold_left ( + ) 0);;
121
118
val array_sum_sq : int -> int = <fun>
@@ -125,19 +122,17 @@ val array_sum_sq : int -> int = <fun>
125
122
126
123
By default, anything defined in a module is accessible from other modules.
127
124
Values, functions, types, or submodules, everything is public. This can be
128
-
restricted. That allows distinguishing content provided to other modules from
129
-
internal use content. What is internal is kept private and not available from
130
-
other modules.
125
+
restricted to avoid exposing definitions that are not relevant from the outside.
131
126
132
127
For this, we must distinguish:
133
-
-Implementation, which is a module's actual content.
134
-
-Interface, which is a module's public content list.
128
+
-The definitions inside a module (the module implementation)
129
+
-The public declarations of a module (the module interface)
135
130
136
-
An `.ml` file contains a module implementation. By default, without an explicitly
137
-
defined interface, an implementation has a default interface where everything is
138
-
public.
131
+
An `.ml` file contains a module implementation; an `.mli` file contains a module
132
+
interface. By default, when no corresponding `.mli` file is provided, an
133
+
implementation has a default interface where everything is public.
139
134
140
-
Copy the `athens.ml` file into `cairo.ml` and change it with this contents:
135
+
Copy the `athens.ml` file into `cairo.ml` and change its contents:
141
136
<!-- $MDX file=examples/cairo.ml -->
142
137
```ocaml
143
138
let message = "Hello from Cairo"
@@ -177,13 +172,13 @@ let () = Cairo.hello ()
177
172
178
173
Update the `dune` file to allow the compilation of this example aside from the
179
174
previous one.
180
-
181
175
<!-- $MDX dir=examples -->
182
-
```bash
183
-
$ echo"(executables (names berlin delhi))"> dune
184
-
185
-
$ dune build
176
+
```lisp
177
+
(executables (names berlin delhi))
178
+
```
186
179
180
+
Compile and execute both programs:
181
+
```shell
187
182
$ dune exec ./berlin.exe
188
183
Hello from Athens
189
184
@@ -228,7 +223,6 @@ type aleph = Ada | Alan | Alonzo
228
223
type bet = bool
229
224
230
225
type gimel = Christos | Christine
231
-
232
226
let gimel_of_bool b = if (b : bet) then Christos else Christine
233
227
let gimel_flip = function Christos -> Christine | Christine -> Christos
234
228
let gimel_to_string x = "Christ" ^ match x with Christos -> "os" | _ -> "ine"
@@ -240,7 +234,6 @@ let dalet_of = function
240
234
| Some (Either.Right x) -> Donald x
241
235
```
242
236
243
-
244
237
Update file `dune`:
245
238
```lisp
246
239
(executables (names berlin delhi) (modules berlin delhi))
@@ -250,6 +243,9 @@ Update file `dune`:
250
243
Run the `dune utop` command, it triggers `Exeter`'s compilation, launches `utop` and loads `Exeter`.
251
244
```ocaml
252
245
# open Exeter;;
246
+
247
+
# #show aleph;;
248
+
type aleph = Ada | Alan | Alonzo
253
249
```
254
250
255
251
Type `aleph` is public. Values can be created or accessed.
@@ -276,25 +272,25 @@ val gimel_of_bool : bool -> gimel
276
272
- : string = "Christine"
277
273
```
278
274
279
-
Type `gimel` is _abstract_. Values are available, but only as function result or argument. Only the provided functions `gimel_of_bool`, `gimel_flip`, and `gimel_to_string`and polymorphic functions can receive or return `gimel` values.
275
+
Type `gimel` is _abstract_. Values can be created or manipulated, but only as function results or arguments. Only the provided functions `gimel_of_bool`, `gimel_flip`, and `gimel_to_string`or polymorphic functions can receive or return `gimel` values.
280
276
```ocaml
281
277
# #show dalet;;
282
278
type dalet = private Dennis of int | Donald of string | Dorothy
283
279
284
-
# Dennis 42;;
280
+
# Donald 42;;
285
281
Error: Cannot create values of the private type Exeter.dalet
The type `dalet` is _read-only_. Pattern matching is possible, but values can only be constructed by the provided functions, here `dalet_of`.
293
+
The type `dalet` is _read-only_. Pattern matching is possible, but values can only be constructed by the provided functions, here `dalet_of`.
298
294
299
295
Abstract and read-only types can be either variants, as shown in this section, records, or aliases. It is possible to access a read-only record field's value, but creating such a record requires using a provided function.
300
296
@@ -322,11 +318,13 @@ let () =
322
318
Florence.print_goodbye ()
323
319
```
324
320
325
-
The module `Hello` is a submodule of module `Florence`.
321
+
Definitions from a submodule are access by chaining module names, here
322
+
`Florence.Hello.print`.
326
323
327
-
### Submodule Interface
324
+
### Submodule with Signatures
328
325
329
-
We can also restrict the interface of a submodule. Here is a second version of the `florence.ml` file:
326
+
To define an interface to a submodule we can provide a _module signature_. This
327
+
is done in this second version of the `florence.ml` file:
330
328
```ocaml
331
329
module Hello : sig
332
330
val print : unit -> unit
@@ -340,9 +338,9 @@ let print_goodbye () = print_endline "Goodbye"
340
338
341
339
The first version made `Florence.Hello.message` public. In this version it can't be accessed from `glasgow.ml`.
342
340
343
-
### Interfaces are Types
341
+
### Module Signatures are Types
344
342
345
-
The role played by interfaces to implementations is akin to the role played by types to values. Here is third possible way to write file `florence.ml`:
343
+
The role played by module signatures to implementations is akin to the role played by types to values. Here is a third possible way to write file `florence.ml`:
346
344
```ocaml
347
345
module type HelloType = sig
348
346
val hello : unit -> unit
@@ -356,9 +354,9 @@ end
356
354
let print_goodbye () = print_endline "Goodbye"
357
355
```
358
356
359
-
The interface used previously for `Florence.Hello` is turned into a `module type` called `HelloType`. Later, when defining `Florence.Hello`, it is annotated with `HelloType` as a value could be. The `HelloType` acts as a type alias.
357
+
First, we define a `module type` called `HelloType` which defines the same module interface as previously. Instead of providing the signature when defining the `Hello` module, we use the `HelloType` module type.
360
358
361
-
This allows writing once interfaces shared by several modules. An implementation satisfies any module type listing some of its contents. This implies a module may have several types and there are subtyping relationship between module types.
359
+
This allows writing interfaces shared by several modules. An implementation satisfies any module type listing some of its contents. This implies a module may have several types and there is a subtyping relationship between module types.
362
360
363
361
## Module Manipulation
364
362
@@ -378,9 +376,7 @@ module Unit :
378
376
end
379
377
```
380
378
381
-
There is online documentation for each library, for instance, [`Unit`](/api/Unit.html).
382
-
383
-
The OCaml compiler tool chain can be used to dump a `.ml` file default interface.
379
+
The OCaml compiler tool chain can be used to dump an `.ml` file's default interface.
384
380
```shell
385
381
$ ocamlc -c -i cairo.ml
386
382
val message : string
@@ -389,7 +385,7 @@ val hello : unit -> unit
389
385
390
386
### Module Inclusion
391
387
392
-
Let's say we feel that a function is missing from the standard `List` module,
388
+
Let's say we feel that a function is missing from the `List` module,
393
389
but we really want it as if it were part of it. In an `extlib.ml` file, we
394
390
can achieve this effect by using the `include` directive:
395
391
@@ -402,12 +398,13 @@ module List = struct
402
398
end
403
399
```
404
400
405
-
It creates a module `Extlib.List` that has everything the standard `List`
406
-
module has, plus a new `uncons` function. In order to override the default `List` module from another `.ml` file, we merely need to add `open Extlib` at the beginning.
401
+
It creates a module `Extlib.List` that has everything the standard `List` module
402
+
has, plus a new `uncons` function. In order to override the default `List`
403
+
module from another `.ml` file, we need to add `open Extlib` at the beginning.
407
404
408
405
## Stateful Modules
409
406
410
-
A module may have an internal state. This is the case standard library`Random` module. The functions `Random.get_state` and `Random.set_state` provide read and write access to the internal state, which is nameless and has an abstract type.
407
+
A module may have an internal state. This is the case for the`Random` module from the standard library. The functions `Random.get_state` and `Random.set_state` provide read and write access to the internal state, which is nameless and has an abstract type.
411
408
```ocaml
412
409
# let s = Random.get_state ();;
413
410
val s : Random.State.t = <abstr>
@@ -425,7 +422,9 @@ val s : Random.State.t = <abstr>
425
422
- : int = 89809344
426
423
```
427
424
428
-
Values returned by `Random.bits` will differ in your setup, but the first and third calls return the same results, showing that the internal state was reset.
425
+
Values returned by `Random.bits` will differ when you run this code. The first
426
+
and third calls return the same results, showing that the internal state was
0 commit comments