Bird
Bird

Reputation: 31

OCaml - Unbound record field

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

Answers (2)

glennsl
glennsl

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:

  1. Remove the signature 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
  1. Provide the full type definition of 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
  1. Add a constructor function to 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

octachron
octachron

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

Related Questions