# Functors
Today we will use the standard library's Map module, practice writing our own
functors, and take a closer look at compilation units.
## The Map module
The [Map module][map] in the OCaml standard library is an implementation
of a dictionary data structure. Recall that dictionaries map *keys* to
*values*. If a key \\(k\\) maps to a value \\(v\\), we say that
\\(v\\) is *bound* to \\(k\\).
[map]: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Map.html
##### Exercise: make char map [✭]
To create a map, we first have to use the
`Map.Make` functor to produce a module that is specialized for the type
of keys we want. Type the following in utop:
```
# module CharMap = Map.Make(Char);;
```
The output tells you that a new module named `CharMap` has been
defined, and it gives you a signature for it. Find the values
`empty`, `add`, and `remove` in that signature. Explain
their types in your own words.
□
##### Exercise: char ordered [✭]
The `Map.Make` functor requires its input module to match the
`Map.OrderedType` signature. Look at [that signature][ord] as well
as the [signature for the `Char` module][char]. Explain in your own
words why we are allowed to pass `Char` as an argument to `Map.Make`.
[ord]: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Map.OrderedType.html
[char]: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Char.html
□
##### Exercise: use char map [✭✭]
Using the `CharMap` you just made, create a map that contains
the following bindings:
* `'A'` maps to `"Alpha"`
* `'E'` maps to `"Echo"`
* `'S'` maps to `"Sierra"`
* `'V'` maps to `"Victor"`
Use `CharMap.find` to find the binding for `'E'`.
Now remove the binding for `'A'`. Use `CharMap.mem` to find whether
`'A'` is still bound.
Use the function `CharMap.bindings` to convert your map
into an association list. Are the correct three bindings active in it?
□
##### Exercise: bindings [✭✭]
Investigate the [documentation of the `Map.S`][map.s] signature to find
the specification of `bindings`. Which of these expressions will
return the same association list?
1. `CharMap.(empty |> add 'x' 0 |> add 'y' 1 |> bindings)`
2. `CharMap.(empty |> add 'y' 1 |> add 'x' 0 |> bindings)`
3. `CharMap.(empty |> add 'x' 2 |> add 'y' 1 |> remove 'x' |> add 'x' 0 |> bindings)`
Check your answer in utop.
*Note: make sure you understand the syntax being used in the expressions
above. If not, ask your TA to review it.*
□
[map.s]: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Map.S.html
Here is a type for dates:
```
type date = { month:int; day:int }
```
For example, March 31st would be represented as `{month=3; day=31}`.
Our goal in the next few exercises is to implement a map whose keys
have type `date`.
Obviously it's possible to represent invalid
dates with type `date`—for example, `{ month=6; day=50 }`
would be June 50th, which is [not a real date][parksandrec].
The behavior of your code in the exercises below is unspecified
for invalid dates.
[parksandrec]: http://nbcparksandrec.tumblr.com/post/46760908046/march-31st-is-a-day
##### Exercise: date order [✭✭]
To create a map over dates, we need a module that we can pass
as input to `Map.Make`. That module will need to match
the `Map.OrderedType` signature. Create such a module.
Here is some code to get you started:
```
module Date = struct
type t = date
let compare ...
end
```
Recall the [specification of `compare`][ord] in `Map.OrderedType` as you write
your `Date.compare` function.
##### Exercise: calendar [✭✭]
Use the `Map.Make` functor with your `Date` module to create a
`DateMap` module. Then define a `calendar` type as follows:
```
type calendar = string DateMap.t
```
The idea is that `calendar` maps a `date` to the name of an
event occurring on that date.
Using the functions in the `DateMap` module, create a calendar
with a few entries in it. If you need inspiration, here are
some good ideas for entries:
* 10/12: CS 3110 Fall 2017 Prelim
* 12/06: CS 3110 Fall 2017 Final Exam
* 12/07: CS 3110 Fall 2017 Final Project Due
□
##### Exercise: print calendar [✭✭]
Write a function `print_calendar : calendar -> unit`
that prints each entry in a calendar in a format similar
the inspiring examples in the previous exercise.
*Hint: use `DateMap.iter`, which is documented
in the [`Map.S` signature][map.s].*
□
## Writing functors
Our goal in the next series of exercises is to write a functor
that, given a module supporting a `to_string` function, returns
a module supporting a `print` function that prints that string.
##### Exercise: ToString [✭✭]
Write a module type `ToString` that specifies a signature with
an abstract type `t` and a function `to_string : t -> string`.
□
##### Exercise: Print [✭✭]
Write a functor `Print` that takes as input a module named `M` of type `ToString`.
The structure returned by your functor should have exactly one value
in it, `print`, which is a function that takes a value of type
`M.t` and prints a string representation of that value.
□
##### Exercise: Print Int [✭✭]
Create a module named `PrintInt` that is the result of applying
the functor `Print` to a new module `Int`.
You will need to write `Int` yourself. The type `Int.t` should be `int`.
*Hint: do not seal `Int`.*
Experiment with `PrintInt` in utop. Use it to print the value of
an integer.
□
##### Exercise: Print String [✭✭]
Create a module named `PrintString` that is the result of applying
the functor `Print` to a new module `MyString`.
You will need to write `MyString` yourself. *Hint: do not seal `MyString`.*
Experiment with `PrintString` in utop. Use it to print the value of
a string.
□
##### Exercise: Print reuse [✭]
Explain in your own words how `Print` has achieved code reuse, albeit
a very small amount.
□
## Compilation units
*This section more properly belongs in the previous lab, but we moved
it forward to here. The notes on compilation units are with the
notes on modules from the previous lecture.*
As the lecture notes on modules discuss, if you have a pair of files
named `foo.ml` and `foo.mli` they together form a *compilation unit*.
The `.ml` file is called the *implementation*, and the `.mli` file
is called the *interface*.
If, for example, `foo.mli` contains exactly the following:
```
val x : int
val f : int -> int -> int
```
and `foo.ml` contains exactly the following:
```
let x = 0
let y = 12
let f x y = x + y
```
Then compiling `foo.ml` will have the same effect as defining the module
`Foo` as follows:
```
module Foo : sig
val x : int
val f : int -> int -> int
end = struct
let x = 0
let y = 12
let f x y = x + y
end
```
##### Exercise: implementation without interface [✭]
Create a file named `date.ml`. In it put exactly the following code:
```
type date = { month:int; day:int }
let make_date month day = {month; day}
let get_month d = d.month
let get_day d = d.day
let to_string d = (string_of_int d.month) ^ "/" ^ (string_of_int d.day)
```
Compile that file to bytecode:
```
$ ocamlbuild date.cmo
```
Now start utop and type the following to use the module you've just created:
```
# #directory "_build";;
# #load "date.cmo";;
# let j1 = Date.make_date 1 1;;
val j1 : Date.date = {Date.month = 1; day = 1}
# j1.day;;
- : int = 1
# Date.to_string j1;;
- : string = "1/1"
```
□
##### Exercise: implementation with interface [✭]
After doing the previous exercise, also create a file named `date.mli`.
In it put exactly the following code:
```
type date = { month:int; day:int; }
val make_date : int -> int -> date
val get_month : date -> int
val get_day : date -> int
val to_string : date -> string
```
Recompile `date.ml` to bytecode:
```
$ ocamlbuild date.cmo
```
Restart utop and re-issue the same phrases as before:
```
# #directory "_build";;
# #load "date.cmo";;
# let j1 = Date.make_date 1 1;;
val j1 : Date.date = {Date.month = 1; day = 1}
# j1.day;;
- : int = 1
# Date.to_string j1;;
- : string = "1/1"
```
□
##### Exercise: implementation with abstracted interface [✭]
After doing the previous two exercises, edit `date.mli` and change
the first declaration in it to be exactly the following:
```
type date
```
The type `date` is now abstract. Recompile `date.ml` to bytecode:
```
$ ocamlbuild date.cmo
```
Restart utop and re-issue the same phrases as before. The responses
to two of them will change. Explain in your own words those changes.
```
# #directory "_build";;
# #load "date.cmo";;
# let j1 = Date.make_date 1 1;;
# j1.day;;
# Date.to_string j1;;
```
□
## Additional exercises
##### Exercise: is for [✭✭✭]
Write a function `is_for : string CharMap.t -> string CharMap.t`
that given an input map with bindings from \\(k_1\\) to \\(v_1\\),
..., \\(k_n\\) to \\(v_n\\), produces an output map with the same
keys, but where each key \\(k_i\\) is now bound to the string
"\\(k_i\\) is for \\(v_i\\)". For example, if `m` maps
`'a'` to `"apple"`, then `is_for m` would map `'a'` to `"a is for apple"`.
*Hint: there is a one-line solution that uses a function from
the `Map.S` signature. To convert a character to a string,
you could use `String.make`. An even fancier way would be
to use `Printf.sprintf`.*
□
##### Exercise: first after [✭✭✭]
Write a function `first_after : calendar -> Date.t -> string` that
returns the name of the first event that occurs strictly
after the given date. If there is no such event, the function
should raise `Not_found`, which is an exception already defined
in the standard library.
*Hint: there is a one-line solution that uses two functions
from the `Map.S` signature.*
□
##### Exercise: sets [✭✭✭]
The standard library `Set` module is quite similar to the `Map` module.
Use it to create a module that represents sets of *case-insensitive strings*.
Strings that differ only in their case should be considered equal by the set.
For example, the sets {"grr", "argh"} and {"aRgh", "GRR"} should be
considered the same, and adding "gRr" to either set should not change
the set. Assuming your module is named `CisSet`, here is some test code:
```
# CisSet.(equal (of_list ["grr"; "argh"]) (of_list ["GRR"; "aRgh"]))
- : bool = true
```
□
##### Exercise: Print String reuse revisited [✭✭]
The `PrintString` module you created above supports just
one operation: `print`. It would be great to have a module
that supports all the `String` module functions in addition
to that `print` operation, and it would be super great to derive
such a module without having to copy any code.
Define a module `StringWithPrint`. It should have all the values
of the built-in `String` module. It should also have the `print`
operation, which should be derived from the `Print` functor rather
than being copied code.
*Hint: use two `include` statements.*
□
##### Exercise: printer for date [✭✭✭, recommended]
After finishing **implementation with abstracted interface**,
and after reading the notes in the previous lecture about
`#install_printer`, add a declaration to `date.mli`:
```
val format : Format.formatter -> date -> unit
```
And add a definition of `format` to `date.ml`.
*Hint: use `Format.fprintf` and `Date.to_string`.*
Now recompile, load utop, and install the printer by issuing the directive
```
#install_printer Date.format;;
```
after loading `date.cmo`.
Reissue the other phrases to utop as you did
in the exercises above. The response from one phrase
will change in a helpful way. Explain why.
□
## Challenge exercise: Algebra
Download this file: [algebra.ml](algebra.ml). It contains two signatures and four structures:
* `Ring` is signature that describes the algebraic structure called a *[ring]*, which is
an abstraction of the addition and multiplication operators.
* `Field` is a signature that describes the algebraic structure called a *[field]*, which
is like a ring but also has an abstraction of the division operation.
* `IntRing` and `FloatRing` are structures that implement rings in terms of `int` and `float`.
* `IntField` and `FloatField` are structures that implement fields in terms of
`int` and `float`.
* `IntRational` and `FloatRational` are structures that implement fields in
terms of ratios (aka fractions)—that is, pairs of `int` and pairs of `float`.
*(For afficionados of abstract algebra: of course these representations
don't necessarily obey all the axioms of rings and fields because of the
limitations of machine arithmetic. Also, the division operation in
`IntField` is ill-defined on zero. Try not to worry about that.)*
Using this code, you can write expressions like the following:
```
# FloatField.(of_int 9 + of_int 3 / of_int 4 |> to_string);;
- : string = "9.75"
# IntRational.(
let half = one / (one+one) in
let quarter = half*half in
let three = one+one+one in
let nine = three*three in
to_string (nine + (three*quarter))
);;
- : string = "39/4"
```
[ring]: https://en.wikipedia.org/wiki/Ring_(mathematics)
[field]: https://en.wikipedia.org/wiki/Field_(mathematics)
##### Exercise: refactor arith [✭✭✭✭]
The file [algebra.ml](algebra.ml) contains a great deal of duplicated code.
Refactor the code to improve the amount of code reuse it exhibits.
To do that, use `include`, functors, and introduce additional structures
and signatures as needed.
There isn't necessarily a right answer here, but it is possible to eliminate
all the duplicated code. Here's some advice to guide you toward a good solution:
* No name should be *directly declared* in more than one signature. For example,
`(+)` should not be directly declared in `Field`; it should be reused from an
earlier signature. By "directly declared" we mean a declaration of the form
`val name : ...`. An indirect declaration would be one that results from
an `include`.
* You need only three *direct definitions* of the algebraic operations
and numbers (plus, minus, times, divide, zero, one): once for `int`,
once for `float`, and once for ratios. For example, `IntField.(+)`
should not be directly defined as `Pervasives.(+)`; rather, it should be
reused from elsewhere. By "directly defined" we mean a definition of the
form `let name = ...`. An indirect definition would be one that results
from an `include` or a functor application.
* The rational structures can both be produced by a single functor that is applied
once to `IntField` and once to `FloatField`.
* It's possible to eliminate all duplication of `of_int`, such that it
is directly defined exactly once, and all structures reuse that
definition; and such that it is directly declared in only one signature.
This will require the use of functors.
It will also require inventing an algorithm that can convert an integer
to an arbitrary `Ring` representation, regardless of what the representation
type of that `Ring` is.
[dsub]: http://caml.inria.fr/pub/docs/manual-ocaml/extn.html#sec234
When you're done, the types of all the modules should remain unchanged. You
can easily see those types by running `ocamlc -i algebra.ml`, which will originally
output the following:
```
module type Ring =
sig
type t
val zero : t
val one : t
val ( + ) : t -> t -> t
val ( ~- ) : t -> t
val ( * ) : t -> t -> t
val to_string : t -> string
val of_int : int -> t
end
module type Field =
sig
type t
val zero : t
val one : t
val ( + ) : t -> t -> t
val ( ~- ) : t -> t
val ( * ) : t -> t -> t
val ( / ) : t -> t -> t
val to_string : t -> string
val of_int : int -> t
end
module IntRing : Ring
module IntField : Field
module FloatRing : Ring
module FloatField : Field
module IntRational : Field
module FloatRational : Field
```
The final output of that command on your solution might define additional types, but
the ones above should remain literally identical.
□