Jisuk Byun
Jisuk Byun

Reputation: 65

How to make a value of type which is defined with module in OCaml?

TL;DR - In OCaml, how to call the type looks like type 'elt set = (module BatSet.S with type elt = 'elt) and how to make a value of this type?

Currently, I'm reading code in the Tezos protocol[*], and I saw the following code.

module type Boxed_set = sig
  type elt
  val elt_ty : elt comparable_ty
  module OPS : S.SET with type elt = elt
  val boxed : OPS.t
  val size : int
end

type 'elt set = (module Boxed_set with type elt = 'elt)

I've never heard about the syntax like type a = (module B). So I open a top-level OCaml interpreter and reproduce similar usage using the module batteries. Following code is the log of what I reproduced.

$ ocaml
        OCaml version 4.07.1

# #use "topfind";;
- : unit = ()
Findlib has been successfully loaded. Additional directives:
  #require "package";;      to load a package
  #list;;                   to list the available packages
  #camlp4o;;                to load camlp4 (standard syntax)
  #camlp4r;;                to load camlp4 (revised syntax)
  #predicates "p,q,...";;   to set these predicates
  Topfind.reset();;         to force that packages will be reloaded
  #thread;;                 to enable threads

- : unit = ()

# #require "batteries";;
[...]: loaded

# type 'elt set = (module BatSet.S with type elt = 'elt);;
type 'elt set = (module BatSet.S with type elt = 'elt)

This weird type-definition really works, but I stuck here. How the module itself can be a type? How to make a value which has type 'elt set like let v : int set = (...)? Is there any kind of keyword to call the types like this?

[*] https://gitlab.com/tezos/tezos/blob/master/src/proto_alpha/lib_protocol/script_typed_ir.ml, Commit-Hash: 86b5227f7efd8aa78fcc427776920480c6c0e780

Upvotes: 1

Views: 589

Answers (2)

Jeffrey Scofield
Jeffrey Scofield

Reputation: 66823

A type like (module M) is the type of a first-class module.

You can make a value of such a type simply by defining a module with the right type.

Here is a simple example I used to test this out:

# module type B = sig type elt end;;
module type B = sig type elt end
# type 'elt b = (module B with type elt = 'elt);;
type 'elt b = (module B with type elt = 'elt)
# module X = struct type elt = int end;;
module X : sig type elt = int end
# (module X : B with type elt = int);;
- : (module B with type elt = int) = <module>

This last line shows module X treated as a value (a first-class module).

Now, indeed, this value has type int b:

# ((module X : B with type elt = int) : int b);;
- : (module B with type elt = int) = <module>

Upvotes: 3

octachron
octachron

Reputation: 18912

This is the type of a first class module with constraint: https://caml.inria.fr/pub/docs/manual-ocaml/manual028.html .

This type can be used to pack a module inside a value:

let int_set = ((module BatSet.Make(Int)): int set);;

Such values can be then unpacked to a module, for instance, I can use it to define an unique_sorted function:

let unique_sorted (type a) ((module MySet): a set) list =
  let set = List.fold_left (fun set x -> MySet.add x set) MySet.empty list in
  MySet.elements set
let test = assert( unique_sorted int_set [4;0;1;2;2] = [0;1;2;4] )

Upvotes: 5

Related Questions