Define own data structures in OCaml using Make syntax - ocaml

In attempting to implement the exercises from Purely Functional Data Structures in OCaml I'm not sure how I can create instances of my solutions.
Say I have the following code:
module type Stack =
sig
type 'a t
val empty : 'a t
val isEmpty : 'a t -> bool
val cons : 'a -> 'a t -> 'a t
val head : 'a t -> 'a
val tail : 'a t -> 'a t
end
(* Implementation using OCaml lists *)
module MyStack : Stack = struct
type 'a t = 'a list
exception Empty
let empty = []
let isEmpty l =
match l with
| [] -> true
| _ -> false
let cons x l = x :: l
let head l =
match l with
| h :: _ -> h
| [] -> raise Empty
let tail l =
match l with
| _ :: r -> r
| [] -> raise Empty
end
I want to provide a Make function similar to Set.Make(String) for creating a specialised instance.
But I'm not sure how to do that.

Seems to me it's natural to parameterize a set by a notion of order (or you could get away with just equality). But a stack doesn't need to be parameterized in that way; i.e., it doesn't depend on a notion of order or equality. It just depends on algebraic properties of its structure.
You already have a parametrically polymorphic module that can be used to make a stack of any type of object.
I'm looking at the code for the Set module. If you want to make a functor like Set.Make, you need a module type for the elements. Since you can use any type at all (unlike Set, which needs an ordered type), you could use something like this:
module type AnyType = struct type t end
Then your functor might look like this (again, I'm just copying code from the Set module):
module Make(Any: AnyType) =
struct
type elt = Any.t
type t = elt list
...
end
Update
If you just want to try out your stack code as is, you can just start using it:
$ ocaml
OCaml version 4.01.0
# #use "mystack.ml";;
module type Stack =
sig
type 'a t
val empty : 'a t
val isEmpty : 'a t -> bool
val cons : 'a -> 'a t -> 'a t
val head : 'a t -> 'a
val tail : 'a t -> 'a t
end
module MyStack : Stack
# let x = MyStack.cons 3 MyStack.empty;;
val x : int MyStack.t = <abstr>
# MyStack.head x;;
- : int = 3
#

Related

Parametrically polymorphic modules

Here is a simple OCaml module type for a monad:
module type Monad = sig
type 'a t
val return : 'a -> 'a t
val bind : 'a t -> ('a -> 'b t) -> 'b t
end
I can instantiate this with any particular monad, such as the reader monad for some type r:
module Reader_monad : Monad = struct
type 'a t = r -> 'a
let return a = fun _ -> a
let bind o f = fun x -> f (o x) x
end
And I can parametrize it over the type r by using a functor:
module type Readable = sig type r end
module Reader (R : Readable) : Monad = struct
type 'a t = R.r -> 'a
let return a = fun _ -> a
let bind o f = fun x -> f (o x) x
end
However, the latter approach requires that I instantiate different instances of the functor for different types r. Is there any way to define a "parametrically polymorphic" module of type Monad that would give parametrically polymorphic functions like return : 'a -> ('r -> 'a)?
I think can get more or less what I want with a separate module type for "families of monads":
module type Monad_family = sig
type ('c, 'a) t
val return : 'a -> ('c, 'a) t
val bind : ('c, 'a) t -> ('a -> ('c, 'b) t) -> ('c, 'b) t
end
module Reader_family : Monad_family = struct
type ('c, 'a) t = 'c -> 'a
let return a = fun _ -> a
let bind o f = fun x -> f (o x) x
end
But if I have a substantial library of general facts about monads, this would require modifying it everywhere manually to use families. And then some monads are parametrized by a pair of types (although I suppose that could be encoded by a product type), etc. So I would rather avoid having to do it this way.
If this isn't directly possible, is there at least a way to instantiate the module Reader locally inside a parametrically polymorphic function? I thought I might be able to do this with first-class modules, but my naive attempt
let module M = Reader(val (module struct type r = int end) : Readable) in M.return "hello";;
produces the error message
Error: This expression has type string M.t
but an expression was expected of type 'a
The type constructor M.t would escape its scope
which I don't understand. Isn't the type M.t equal to int -> string?
I think this is the same issue as The type constructor "..." would escape its scope when using first class modules, where the module M doesn't live long enough. If you instead wrote
# module M = Reader(struct type r = int end);;
# M.return "hello";;
- : string M.t = <fun>
then this would work fine.
Separately, the Reader functor loses some type equalities that you might want. You can restore them by defining it as such:
module Reader (R : Readable) : Monad with type 'a t = R.r -> 'a = struct
type 'a t = R.r -> 'a
let return a = fun _ -> a
let bind o f = fun x -> f (o x) x
end

What does this type error on heterogeneous lists means?

I have a heterogeneous list and a function on them
type ('ls,'tl) hlist =
| Nil : ('a,'a) hlist
| Cons: 'a * ('l, 't) hlist -> ('a -> 'l, 't) hlist
let rec headlist l =
match l with
| Cons (h, t) -> Cons (head h, headlist t)
| Nil -> Nil
and would like to traverse a hlist of lists of different types, and build a list of the heads of each list. The idea is something like this:
headlist Cons( [1,2,3], Cons( ['a','b'], Cons( [true,false], Nil )))
= Cons( 1, Cons( 'a', Cons( true, Nil)))
However, I encounter a type error.
Error: This expression has type ('a, 'b list -> 'a) hlist
but an expression was expected of type
('b list -> 'a, 'b list -> 'a) hlist
The type variable 'a occurs inside 'b list -> 'a
I don't understand the type error. What is it saying? Am I trying to do something impossible?
Your problem start with the fact that it is not possible to write a type for the headlist function that you have in mind. Since it is in general necessary to write explicitly the type of functions manipulating GATDs, it is good practice to start writing this type, to check that one can write the type; and only remove it afterward in the rare cases where it is possible to elide the explicit type annotations.
The root of the issue here is that heterogeneous lists are much more rigid than normal lists. In particular, depending on the operations needed on such lists, it is frequent to have to tailor specialized heterogeneous list types. For instance, with the classical heterogeneous list:
type void = |
module Hlist = struct
type 'a t =
| []: void t
| (::): 'a * 'l t -> ('a -> 'l) t
let head(x::_) = x
let rec length: type a. a t -> int = function
| [] -> 0
| a :: q -> 1 + length q
end
it is impossible to express the condition: all elements of the heterogeneous list are heterogeneous lists with at least one element themselves. However, it is possible to define another list type that does enforce this condition:
module Hlist_of_nonempty_hlist_0 = struct
type 'a t =
| []: void t
| (::): (('h -> 'more) as 'a) Hlist.t * 'l t -> ('a -> 'l) t
end
With this new list type, I can compute the length of all nested lists:
let rec map_length: type a. a Hlist_of_nonempty_hlist_0 t -> int list = function
| [] -> []
| a :: q -> Hlist.length a :: map_length q
However, I can still not apply head to all elements, because the types of the head are not easily accessible. One option is to store those types directly in the type of Hlist_of_nonempty_hlist:
module Hlist_of_nonempty_hlist = struct
type ('a,'b) t =
| []: (void,void) t
| (::):
(('h -> 'more) as 'a) Hlist.t * ('l,'hl) t
-> ('a -> 'l, 'h -> 'hl) t
end
and with this specialized heterogeneous list type, writing the type of map_head becomes straightforward:
let rec map_head:
type l hl. (l, hl) Hlist_of_nonempty_hlist.t -> hl Hlist.t
= function
| [] -> []
| (a::_) :: q -> a :: map_head q
But this is a lot of design work on the type for one function. And going further and trying to write any generic functions over heterogeneous lists generally require a lot of polymorphic records and functors.
I don't think there's a type that describes the function you want. You want to say that the input is an hlist all of whose heterogeneous types are lists. I don't see a way to say that, which suggests to me that you can't have a function like this.
However, I have been wrong many times, and GADTs are something I'm particularly unsteady about.
If I understand correctly, your function headlist is supposed to have type ('a list -> 'b list -> ... -> 'z, 'z) hlist -> ('a -> 'b -> ... > 'z, 'z) hlist. I do not think there is a single OCaml type that covers all the possible arities. So, the compiler looks for a much simpler type, hence the strange error message.

How to achieve the type-class of sml?

I want to write a comparable set as below.
signature COMPARABLE_SET=
sig
type 'a set
val empty: 'a set
val insert: 'a * 'a set -> 'a set
val member: 'a * 'a set -> bool
end
I need to limit the element in 'a set type to be comparable:(there is a function with type:'a * 'a -> order).
How to achieve it?
If you want to do it in OCaml, this is simply a functor case :
First, you need to define the type of your elements :
module type OrderedType = sig
type t
val compare : t -> t -> int
end
And then you'll define a functor on this type :
module MakeComparableSet (Ord : OrderedType) :
sig
type elt = Ord.t
type t
val empty : t
val insert : elt -> t -> t
val member : elt -> t -> bool
end = struct
type elt = Ord.t
type t
let empty = failwith "TODO"
let insert = failwith "TODO"
let member = failwith "TODO"
end
Which is exactly what is made here.
You can see a functor as a function on module that will create new modules. Here, the functor ComparableSet takes a module of signature OrderedType and returns a module that is a set.

Dynamic binding using a ref cell

I understand that you can't do this, but want to understand precisely why.
module M : sig
type 'a t
val call : 'a t -> 'a option
end = struct
type 'a t
let state : ('a t -> 'a option) ref = ref (fun _ -> None)
let call : ('a t -> 'a option) = fun x -> !state x
end
Results in:
Error: Signature mismatch:
Modules do not match:
sig
type 'a t
val state : ('_a t -> '_a option) ref
val call : '_a t -> '_a option
end
is not included in
sig
type 'a t
val call : 'a t -> 'a option
end
Values do not match:
val call : '_a t -> '_a option
is not included in
val call : 'a t -> 'a option
Why are the abstract types not compatible here?
My gut tells me it has everything to do with early vs late binding, but I'm looking for an exact description of what the type system is doing here.
One way to look at it is that your field state can't have the polymorphic value you ascribe to it, because mutable values can't be polymorphic. References are at most monomorphic (as indicated by the '_a notation for the type variable).
If you just try to declare a similar reference in the toplevel, you'll see the same effect:
# let lfr: ('a list -> 'a option) ref = ref (fun x -> None);;
val lfr : ('_a list -> '_a option) ref = {contents = <fun>}
The type variable '_a indicates some single type that hasn't yet been determined.
The reason that references can't be polymorphic is that it's unsound. If you allow references to be generalized (polymorphic) it's easy to produce programs that go horribly wrong. (In practice this usually means a crash and core dump.)
The issue of soundness is discussed near the beginning of this paper: Jacques Garrigue, Relaxing the Value Restriction (which I refer to periodically when I forget how things work).
Update
What I think you want is "rank 2 polymorphism". I.e., you want a field whose type is polymorphic. You can actually get this in OCaml as long as you declare the type. The usual method is to use a record type:
# type lfrec = { mutable f: 'a. 'a list -> 'a option };;
type lfrec = { mutable f : 'a. 'a list -> 'a option; }
# let x = { f = fun x -> None };;
val x : lfrec = {f = <fun>}
# x.f ;;
- : 'a list -> 'a option = <fun>
The following code compiles for me using lfrec instead of a reference:
module M : sig
type 'a t
val call : 'a t -> 'a option
end = struct
type 'a t
type lfrec = { mutable f: 'a. 'a t -> 'a option }
let state: lfrec = { f = fun _ -> None }
let call : ('a t -> 'a option) = fun x -> state.f x
end

overgeneralized curried fns

module MapHelpers (Ord : Map.OrderedType) = struct
include Map.Make (Ord)
let add_all a b = fold add a b
end
works but the seemingly equivalent
module MapHelpers (Ord : Map.OrderedType) = struct
include Map.Make (Ord)
let add_all = fold add
end
fails to compile with
File "Foo.ml", line 2, characters 18-104:
Error: The type of this module,
functor (Ord : Map.OrderedType) ->
sig
...
val add_all : '_a t -> '_a t -> '_a t
end,
contains type variables that cannot be generalized
Command exited with code 2.
and adding an explicit type annotation
: 'a . 'a t -> 'a t -> 'a t
causes compilation to fail earlier with
Error: This definition has type 'a t -> 'a t -> 'a t
which is less general than 'a0. 'a0 t -> 'a0 t -> 'a0 t
Why does adding the explicit formals a b change the way these two modules are typed?
This is a consequence of the value restriction, as described in the following FAQ item:
A function obtained through partial application is not polymorphic enough
The more common case to get a ``not polymorphic enough'' definition is when defining a function via partial application of a general polymorphic function. In Caml polymorphism is introduced only through the “let” construct, and results from application are weakly polymorph; hence the function resulting from the application is not polymorph. In this case, you recover a fully polymorphic definition by clearly exhibiting the functionality to the type-checker : define the function with an explicit functional abstraction, that is, add a function construct or an extra parameter (this rewriting is known as eta-expansion):
# let map_id = List.map (function x -> x) (* Result is weakly polymorphic *)
val map_id : '_a list -> '_a list = <fun>
# map_id [1;2]
- : int list = [1;2]
# map_id (* No longer polymorphic *)
- : int list -> int list = <fun>
# let map_id' l = List.map (function x -> x) l
val map_id' : 'a list -> 'a list = <fun>
# map_id' [1;2]
- : int list = [1;2]
# map_id' (* Still fully polymorphic *)
- : 'a list -> 'a list = <fun>
The two definitions are semantically equivalent, and the new one can be assigned a polymorphic type scheme, since it is no more a function application.
See also this discussion about what the _ in '_a indicates -- weak, non-polymorphic type variables.