Reputation: 31
I am new to OCaml and I am trying to convert some Haskell code to OCaml.
I have the following code, where I explore the OCaml module system and functors.
module type F =
sig
type 'a f
val fmap : ('a -> 'b) -> 'a f -> 'b f
end
module type FOO =
functor (C : F) ->
sig
type f
end
module Foo : FOO =
functor (C : F) ->
struct
type f = { foo : 'x . 'x C.f -> 'x ;
bar : 'x . 'x C.f -> 'x C.f }
end
module List =
struct
include List
type 'a f = 'a list
let fmap = List.map
end
module Bar =
struct
include Foo (List)
let ea = { foo = List.hd ; bar = List.tl }
end
I keep having the error Unbound record field foo
.
The fact that Foo
is a functor complicates matters, I feel like I cannot provide an annotation as I want to.
What am I missing?
Upvotes: 2
Views: 3265
Reputation: 29116
The problem arises because you're saying that Foo
has the signature of FOO
, and in FOO
you're saying that f
is abstract, i.e. that its definition is hidden. From the compiler's perspective you've deliberately hidden the implementation details of Foo.f
.
There are several way you can fix this:
FOO
from Foo
, allowing it to be inferred instead.module Foo =
functor (C : F) ->
struct
type f = { foo : 'x . 'x C.f -> 'x ;
bar : 'x . 'x C.f -> 'x C.f }
end
will infer its type as
module Foo :
functor (C : F) ->
sig type f = { foo : 'x. 'x C.f -> 'x; bar : 'x. 'x C.f -> 'x C.f; } end
f
in FOO
.module type FOO =
functor (C : F) ->
sig
type f = { foo : 'x . 'x C.f -> 'x ;
bar : 'x . 'x C.f -> 'x C.f }
end
Foo
and FOO
that allow you to create values of type f
without needing to know the exact details of its implementation. In this case this is however made more difficult by OCaml's lack of higher rank polymorphism, meaning you can't pass the universally quantified foo
and bar
functions to the constructor function directly (see Section 5.3 in the OCaml manual). The workaround is to wrap these functions in a record (or object), which effectively just brings us back to solution 2.Upvotes: 4
Reputation: 18902
One thing to be cautious when approaching OCaml's module system is that one of the primary use case of module types is to remove information. Moreover, it can be quite easy to remove so much information than a constrained module (M:S
) becomes useless.
Your module type FOO
is an example of a module type that removes too much information. With,
module type FOO =
functor (C : F) ->
sig
type f
end
the type f
is an abstract type that appears alone in the resulting module type. Since the type is abstract, no functions outside of the module can create it. And there no visible way to create a value of this type from inside the functor either. Consequently, it is not possible to ever create a value of this type. In other words, this module type is functionally equivalent to
module type FOO = functor (C : F) -> sig end
and
module F: FOO = ...
implies that applying F
can only be useful for its side-effect.
As a general piece of advice, when starting to use functors in OCaml, it is generally better to let the compiler infers their type. This avoids this common issue of writing module types that removes too much information.
Upvotes: 4