Skip to content

Commit 6801541

Browse files
author
Cuihtlauac ALVARADO
committed
Review edits
1 parent b960348 commit 6801541

File tree

1 file changed

+70
-71
lines changed

1 file changed

+70
-71
lines changed

data/tutorials/language/1ms_00_modules.md

Lines changed: 70 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,27 @@ Modules are collections of definitions grouped together. This is the basic means
2121
### File-Based Modules
2222

2323
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.
2626

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.
3029

31-
Here is the code in the file `athens.ml`:
30+
Here is the file `athens.ml`:
3231
<!-- $MDX file=examples/athens.ml -->
3332
```ocaml
3433
let hello () = print_endline "Hello from Athens"
3534
```
3635

37-
This is what is in `berlin.ml`:
36+
Here is the file `berlin.ml`:
3837
<!-- $MDX file=examples/berlin.ml -->
3938
```ocaml
4039
let () = Athens.hello ()
4140
```
4241

4342
To compile them using [Dune](https://dune.build/), at least two
4443
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.
4745
```lisp
4846
(lang dune 3.7)
4947
```
@@ -54,27 +52,24 @@ configuration files are required:
5452
(executable (name berlin))
5553
```
5654

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:
5956
<!-- $MDX dir=examples -->
6057
```bash
61-
$ echo "(lang dune 3.7)" > dune-project
62-
63-
$ echo "(executable (name berlin))" > dune
64-
6558
$ dune build
6659

6760
$ dune exec ./berlin.exe
68-
Hello
61+
Hello from Athens
6962
```
7063

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.
7569

7670
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.
7873

7974
### Naming and Scoping
8075

@@ -84,38 +79,40 @@ always starts with a capital letter: `Athens`) followed by a dot and the
8479
thing you want to use (`hello`). It may be a value, a type constructor, or
8580
anything the module provides.
8681

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
8984
written:
9085
<!-- $MDX skip -->
9186
```ocaml
9287
open Athens
9388
let () = hello ()
9489
```
9590

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.
10195
```ocaml
10296
open Printf
10397
let data = ["a"; "beautiful"; "day"]
10498
let () = List.iter (printf "%s\n") data
10599
```
106100

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.
111105

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:
113107
```ocaml
114108
# let list_sum_sq m =
115109
let open List in
116110
init m Fun.id |> map (fun i -> i * i) |> fold_left ( + ) 0;;
117111
val list_sum_sq : int -> int = <fun>
112+
```
118113

114+
The module access notation can be applied to an entire expression:
115+
```ocaml
119116
# let array_sum_sq m =
120117
Array.(init m Fun.id |> map (fun i -> i * i) |> fold_left ( + ) 0);;
121118
val array_sum_sq : int -> int = <fun>
@@ -125,19 +122,17 @@ val array_sum_sq : int -> int = <fun>
125122

126123
By default, anything defined in a module is accessible from other modules.
127124
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.
131126

132127
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)
135130

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.
139134

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:
141136
<!-- $MDX file=examples/cairo.ml -->
142137
```ocaml
143138
let message = "Hello from Cairo"
@@ -177,13 +172,13 @@ let () = Cairo.hello ()
177172

178173
Update the `dune` file to allow the compilation of this example aside from the
179174
previous one.
180-
181175
<!-- $MDX dir=examples -->
182-
```bash
183-
$ echo "(executables (names berlin delhi))" > dune
184-
185-
$ dune build
176+
```lisp
177+
(executables (names berlin delhi))
178+
```
186179

180+
Compile and execute both programs:
181+
```shell
187182
$ dune exec ./berlin.exe
188183
Hello from Athens
189184

@@ -228,7 +223,6 @@ type aleph = Ada | Alan | Alonzo
228223
type bet = bool
229224
230225
type gimel = Christos | Christine
231-
232226
let gimel_of_bool b = if (b : bet) then Christos else Christine
233227
let gimel_flip = function Christos -> Christine | Christine -> Christos
234228
let gimel_to_string x = "Christ" ^ match x with Christos -> "os" | _ -> "ine"
@@ -240,7 +234,6 @@ let dalet_of = function
240234
| Some (Either.Right x) -> Donald x
241235
```
242236

243-
244237
Update file `dune`:
245238
```lisp
246239
(executables (names berlin delhi) (modules berlin delhi))
@@ -250,6 +243,9 @@ Update file `dune`:
250243
Run the `dune utop` command, it triggers `Exeter`'s compilation, launches `utop` and loads `Exeter`.
251244
```ocaml
252245
# open Exeter;;
246+
247+
# #show aleph;;
248+
type aleph = Ada | Alan | Alonzo
253249
```
254250

255251
Type `aleph` is public. Values can be created or accessed.
@@ -276,25 +272,25 @@ val gimel_of_bool : bool -> gimel
276272
- : string = "Christine"
277273
```
278274

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.
280276
```ocaml
281277
# #show dalet;;
282278
type dalet = private Dennis of int | Donald of string | Dorothy
283279
284-
# Dennis 42;;
280+
# Donald 42;;
285281
Error: Cannot create values of the private type Exeter.dalet
286282
287283
# dalet_of (Some (Either.Left 10));;
288284
- : dalet = Dennis 10
289285
290286
# let dalet_to_string = function
291-
| None -> "Dorothy"
292-
| Some (Either.Left _) -> "Dennis"
293-
| Some (Either.Right _) -> "Donald";;
294-
val dalet_to_string : ('a, 'b) Either.t option -> string = <fun>
287+
| Dorothy -> "Dorothy"
288+
| Dennis _ -> "Dennis"
289+
| Donald _ -> "Donald";;
290+
val dalet_to_string : dalet -> string = <fun>
295291
```
296292

297-
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`.
298294

299295
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.
300296

@@ -322,11 +318,13 @@ let () =
322318
Florence.print_goodbye ()
323319
```
324320

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`.
326323

327-
### Submodule Interface
324+
### Submodule with Signatures
328325

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:
330328
```ocaml
331329
module Hello : sig
332330
val print : unit -> unit
@@ -340,9 +338,9 @@ let print_goodbye () = print_endline "Goodbye"
340338

341339
The first version made `Florence.Hello.message` public. In this version it can't be accessed from `glasgow.ml`.
342340

343-
### Interfaces are Types
341+
### Module Signatures are Types
344342

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`:
346344
```ocaml
347345
module type HelloType = sig
348346
val hello : unit -> unit
@@ -356,9 +354,9 @@ end
356354
let print_goodbye () = print_endline "Goodbye"
357355
```
358356

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.
360358

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.
362360

363361
## Module Manipulation
364362

@@ -378,9 +376,7 @@ module Unit :
378376
end
379377
```
380378

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.
384380
```shell
385381
$ ocamlc -c -i cairo.ml
386382
val message : string
@@ -389,7 +385,7 @@ val hello : unit -> unit
389385

390386
### Module Inclusion
391387

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,
393389
but we really want it as if it were part of it. In an `extlib.ml` file, we
394390
can achieve this effect by using the `include` directive:
395391

@@ -402,12 +398,13 @@ module List = struct
402398
end
403399
```
404400

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.
407404

408405
## Stateful Modules
409406

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.
411408
```ocaml
412409
# let s = Random.get_state ();;
413410
val s : Random.State.t = <abstr>
@@ -425,7 +422,9 @@ val s : Random.State.t = <abstr>
425422
- : int = 89809344
426423
```
427424

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
427+
reset.
429428

430429
## Conclusion
431430

0 commit comments

Comments
 (0)