Valentyn Zakharenko
Valentyn Zakharenko

Reputation: 3088

Why is it impossible to include module type from functor parameter module into module type in resulting module of functor?

OCaml doesn't provide the ability to create a module type by functor directly. But, It allows some manipulations with the nested module type. So, I can use module type from parameter to bind module type in the result:

module CONTAINER = struct
  module type S = sig
    val a : int
  end
end

module COPY (CONTAINER : sig module type S end) = struct
  module type S = CONTAINER.S
end 

module type S_FROM_CONTAINER = COPY(CONTAINER).S
    

S_FROM_CONTAINER isn't abstract. It is necessary to provide a to apply S_FROM_CONTAINER.

(*
module Impl : S_FROM_CONTAINER = struct
end 

> Error: Signature mismatch:
> Modules do not match: sig end is not included in S_FROM_CONTAINER  
> The value `a' is required but not provided
*)

module Impl : S_FROM_CONTAINER = struct
  let a = 42
end 

Also, I can use S from CONTAINER as a type of module in module type in the resulting module:

module AS_TYPE_OF_IMPL (CONTAINER : sig module type S end) = struct
  module type S = sig
    module Impl : CONTAINER.S 
  end
end 

module type IMPL_CONTAINER_S = 
  AS_TYPE_OF_IMPL(CONTAINER).S 

Impl module in IMPL_CONTAINER_S isn't abstract module type too:

(* module ImplContainer : IMPL_CONTAINER_S = struct
     module Impl = struct end
   end

> Error: Signature mismatch:
> Modules do not match:
>   sig module Impl : sig end end
> is not included in
>   IMPL_CONTAINER_S
> In module Impl:
> Modules do not match: sig end is not included in CONTAINER.S
> In module Impl:
> The value `a' is required but not provided
*)

module ImplContainer : IMPL_CONTAINER_S = struct
  module Impl = struct let a = 42 end
end

That's fine. I think it is a powerful tool. But I found one limitation that makes me sad. It is impossible to include this module type into another one.

module EXTEND_S (CONTAINER : sig module type S end) = struct
  module type S = sig
    include CONTAINER.S
    val extention : int
  end
end

Or

module AS_PART_OF_IMPL (CONTAINER : sig module type S end) = struct
  module type S = sig
    module Impl : sig
      include CONTAINER.S
      val extention : int
    end 
  end 
end

include CONTAINER.S
^^^^^^^^^^^
Error: This module type is not a signature

Why I can't do this? For me, it looks like some gap, nor only in the compiler but in language conception too. What I miss to understand?

Upvotes: 1

Views: 939

Answers (1)

octachron
octachron

Reputation: 18892

In brief, include are not part of the type system of modules and they work on signatures by inlining the content of the signature. And adding an include expression in the module system is not that straightforward because one cannot include any module type in any context. Consider for instance:

module F(X:sig module type x module type y end) = struct
  module type result = sig 
    include x
    include y
  end
end

is not always valid. Consider for instance,

module type x = sig
  type t
  val x: t
end
module type y = sig
  type t
  val y: t
end

then

module type t = sig
  include x
  include y
end

yields an error

Error: Illegal shadowing of included type t/863 by t/865
      Line 2, characters 2-11:
        Type t/863 came from this include
      Line 3, characters 2-10:
        The value x has no valid type if t/863 is shadowed

Thus include s is not always valid and making it possible to include abstract type module would require to refine the module type system to capture this condition.

Moreover, having module types with a dynamic number of runtime components would require a different compilation scheme because querying a path inside a module can no longer be reduced to querying a statistically-known index in a block.

Upvotes: 3

Related Questions