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
Improve some wording in the functors tutorial (#2023)
* Reorder a paragraph in functors tutorial to improve content flow
* Grammar: add the word "by" in functors tutorial
* Clarify note on `with type` constraint in functors tutorial
* Simplify note on `with type` constraint in functors tutorial
Co-authored-by: Cuihtlauac Alvarado <[email protected]>
* Grammar: add the word "of" in functors tutorial
* Add section on empty variants in basic data types tutorial
* Add missing definition to `Binary` functor in functors tutorial
* Grammar: use "facilitate" over "allows" in functors tutorial
* Add section on naming and scoping to functors tutorial
* Reword section header in functors tutorial
Co-authored-by: Cuihtlauac Alvarado <[email protected]>
* Use more self-explanatory constructor in functors tutorial
Co-authored-by: Cuihtlauac Alvarado <[email protected]>
* Improve compiler error explanation in functors tutorial
Co-authored-by: Cuihtlauac Alvarado <[email protected]>
* Remove unnecessary word in functors tutorial
Co-authored-by: Cuihtlauac Alvarado <[email protected]>
* Remove small digression from functors tutorial
* Simplify sentence in functors tutorial
Co-authored-by: Cuihtlauac Alvarado <[email protected]>
* Reword explanation on scoping in functors tutorial
* Remove extra space and add comma after "i.e." in functors tutorial
* Revert "Add section on empty variants in basic data types tutorial"
This reverts commit 03758f0.
* Remove empty variants as placeholders in `Binary` functor example
* Apply suggestions from code review
* Apply suggestions from code review
* Update data/tutorials/language/1ms_01_functors.md
* Clarify note on `with type` constraint in functors tutorial
Co-authored-by: Cuihtlauac Alvarado <[email protected]>
* Typo: change "In" to "If" in functors tutorial
* Apply suggestions from code review
* Update data/tutorials/language/1ms_01_functors.md
---------
Co-authored-by: Cuihtlauac Alvarado <[email protected]>
Copy file name to clipboardExpand all lines: data/tutorials/language/1ms_01_functors.md
+54-10Lines changed: 54 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -62,7 +62,7 @@ Here is how this reads (starting from the bottom, then going up):
62
62
63
63
**Note**: Most set operations need to compare elements to check if they are the same. To allow using a user-defined comparison algorithm, the `Set.Make` functor takes a module the specifies both the element type `t` and the `compare` function. Passing the comparison function as a higher-order parameter, as done in `Array.sort`, for example, would add a lot of boilerplate code. Providing set operations as a functor allows specifying the comparison function only once.
64
64
65
-
Here is an example how to use `Set.Make`:
65
+
Here is an example of how to use `Set.Make`:
66
66
67
67
**`funkt.ml`**
68
68
@@ -79,10 +79,6 @@ This defines a module `Funkt.StringSet`. What `Set.Make` needs are:
79
79
- Type `t`, here `string`
80
80
- Function allowing to compare two values of type `t`, here `String.compare`
81
81
82
-
However, since the module `String` defines
83
-
- Type name `t`, which is an alias for `string`
84
-
- Function `compare` of type `t -> t -> bool` compares two strings
85
-
86
82
This can be simplified using an _anonymous module_ expression:
87
83
```ocaml
88
84
module StringSet = Set.Make(struct
@@ -93,6 +89,10 @@ end)
93
89
94
90
The module expression `struct ... end` is inlined in the `Set.Make` call.
95
91
92
+
However, since the module `String` already defines
93
+
- Type name `t`, which is an alias for `string`
94
+
- Function `compare` of type `t -> t -> bool` compares two strings
95
+
96
96
This can be simplified even further into this:
97
97
```ocaml
98
98
module StringSet = Set.Make(String)
@@ -156,14 +156,14 @@ let _ =
156
156
157
157
This allows the user to seemingly extend the module `String` with a submodule `Set`. Check the behaviour using `opam exec -- dune exec funkt < dune`.
158
158
159
-
## Functors Allows Parametrising Modules
159
+
## Parametrising Modules with Functors
160
160
161
161
### Functors From the Standard Library
162
162
163
163
A functor is almost a module, except it needs to be applied to a module. This turns it into a module. In that sense, a functor allows module parametrisation.
164
164
165
165
That's the case for the sets, maps, and hash tables provided by the standard library. It works like a contract between the functor and the developer.
166
-
* If you provide a module that implements what is expected, as described the parameter interface
166
+
* If you provide a module that implements what is expected, as described by the parameter interface
167
167
* The functor returns a module that implements what is promised, as described by the result interface
168
168
169
169
Here is the module's signature that the functors `Set.Make` and `Map.Make` expect:
@@ -241,9 +241,10 @@ module type S = sig
241
241
end
242
242
243
243
module Binary(Elt: OrderedType) : S = struct
244
-
type elt = | (* Replace by your own *)
245
-
type t = | (* Replace by your own *)
244
+
type elt (* Add your own type definition *)
245
+
type t (* Add your own type definition *)
246
246
(* Add private functions here *)
247
+
let empty = failwith "Not yet implemented"
247
248
let is_empty h = failwith "Not yet implemented"
248
249
let insert h e = failwith "Not yet implemented"
249
250
let merge h1 h2 = failwith "Not yet implemented"
@@ -370,7 +371,50 @@ let _ =
370
371
371
372
Check the program's behaviour using `opam exec -- dune exec funkt < dune`.
372
373
373
-
**Note**: The functor `IterPrint.Make` returns a module that exposes the type from the injected dependency (here first `List.t` then `Array.t`). That's why a `with type` constraint is needed. When parametrising other something not exposed by the module (and _implementation detail_), the `with type` constraint is not needed.
374
+
**Note**: Modules received and returned by `IterPrint.Make` both have a type `t`. The `with type ... := ...` constraint exposes that the two types `t` are the same. This makes functions from the injected dependency and result module use the exact same type. When the parameter's contained type is not exposed by the result module (i.e., when it is an _implementation detail_), the `with type` constraint is not necessary.
375
+
376
+
### Naming and Scoping
377
+
378
+
The `with type` constraint unifies types within a functor's parameter and result modules. We've used that in the previous section. This section addresses the naming and scoping mechanics of this constraint.
379
+
380
+
Naively, we might have defined `Iter.Make` as follows:
381
+
382
+
```ocaml
383
+
module Make(Dep: Iterable) : S = struct
384
+
type 'a t = 'a Dep.t
385
+
let f = Dep.iter (fun s -> Out_channel.output_string stdout (s ^ "\n"))
386
+
end
387
+
```
388
+
389
+
If the function `f` isn't used, the project compiles without error.
390
+
391
+
However, since `Make` is invoked to create module `IterPrint` in `funkt.ml`, the project fails to compile with the following error message:
but an expression was expected of type string IterPrint.t
401
+
```
402
+
403
+
Outside the functor, it is not known that `type 'a t` is set to `Dep.t`. In `funkt.ml`, `IterPrint.t` appears as an abstract type exposed by the result of `Make`. This is why the `with type` constraint is needed. It propagates the knowledge that `IterPrint.t` is the same type as `Dep.t` (`List.t` in this case).
404
+
405
+
The type constrained using `with type` isn't shadowed by definitions within the functor body. In the example, the `Make` functor can be redefined as follows:
406
+
407
+
```ocaml
408
+
module Make(Dep: Iterable) : S with type 'a t := 'a Dep.t = struct
In the example above, `t` from `with type` takes precedence over the local `t`, which only has a local scope. Don't shadow names too often because it makes the code harder to understand.
0 commit comments