Compose complex struct signature

Hi there, I am wondering if I can pass a type in signature declaration, so I can declare a return signature of a functor more clearly.

For example

module type Flour = sig
  type t
end

module type Bread = sig
  type t
end

module Cook (F : Flour) : Bread = struct
  type t = {material : F.t; sauce : 'a}
  let cook (material: F.t) sauce = {material; sauce}
end

For some reasons, I need to declare the return signature of functor Cook explicitly. However, I have no idea how to declare cook function in signature Bread. Can I declare/reference a type from another signature when declaring a new signature? Something like

module type Bread = sig
  type t
  type r = Flour.t
  val cook : r -> 'a -> t
end

This should work:

module type Bread = sig
  type flour
  type 'a t = { material : flour; sauce : 'a }

  val cook : flour -> 'a -> 'a t
end

You can see this technique (a helper type) being used here: OCaml library : Set and OCaml library : Set.S

1 Like

Thank you for your reply! It works!

1 Like

I have complete my code, it looks like this. Notice that instead of using 'a, I replaced it with a explicit type.

module type Flour = sig
  type t
end

module type Bread = sig
  type i
  type t

  val cook : i -> int -> t
end

module Cook (F : Flour) : Bread = struct
  type i = F.t

  type t =
    { material : i
    ; cid : int
    }

  let cook (material : i) cid = { material; cid }
end

module F1 = struct
  type t = { fid : int }
end

module B1 = Cook (F1)

let f1 : F1.t = { fid = 3 }
let b1 = B1.cook f1 3

However, the last line does not work, it says

This expression has type F1.t but an expression was expected of type B1.i
type t = { fid : int; }

In fact, F1.t and B1.i are of the same type, because B1 is created through Cook functor. Is there anything i am missing? Or do I misunderstand something :slightly_frowning_face:

Yes, you are making the Cook functor’s output module too abstract, so the compiler ‘forgets’ the fact that the types are the same. The simplest way to solve it is to just not annotate the return type:

module Cook(F : Flour) = struct
  ...
end

In that case the compiler will infer the correct type and understand the type relationships automatically.

1 Like

When you have a signature with abstract type, you can make it less abstract adding equation with with type t = expression. For instance you can specialize your Bread signature with type i = float.

module type Bread_with_float = Bread with type i = float;;
module type Bread_with_float =
  sig type i = float type t val cook : i -> int -> t end

And so, you should write your functor signature:

module Cook (F : Flour) : Bread with type i = F.t = struct
  ...
end

This way you will have the equation F1.t = B1.i

1 Like

Thank you for your solution! This is a simplified version of my code. In my original code, it is way more complex. The type of Flour can have variant from Cook(Flour). In this case, when I need to generate the new module through functor, I need to explicit provide the return signature for it because they are mutually referred.

Thank you for your solution! It works! Many thanks! :smile:

1 Like