Pass module to class

Not really much of an expert, but inspired by the community enthusiasm for modules, I am trying to use a module for abstract view of data to be displayed in a UI. The UI displays a list of things, and provides various actions to invoke on the things, while the knowledge about the concrete thing and the actions supported are implemented in the module.

The UI inherits from platform functionality, so it’s naturally a class object.

The module needs to have values formed at run time, and the natural (?) way to accomplish that was what evidently is a “first class” module:

module type Contents = sig
        type t
        val items: t list
        val print_an_item: int -> unit
        end

(* create module with supplied list of strings. *)
let make_strings_contents strings = (module  struct
        type t = string
        let items = strings
        let print_an_item i = print_endline (List.nth items i)
        end: Contents)

Now I need to provide this module instance to my UI class:

class list_window contents = object (self)
        method get_print_an_item (module T: Contents) = T.print_an_item
        method print_an_item i = (self#get_print_an_item contents) 1
        end

OK! that works, but it seems cumbersome. Is there a way to declare the module so that class methods will have direct access to the contents?

How about access to the items value? So far I haven’t found any way to smuggle a 'a list out of the instantiated module – i.e., the item list with the opaque type as represented by the Contents signature. “The type constructor T.t would escape its scope.”

If I were using a class for this …

class virtual ['a] contentclass = object
        method virtual items: 'a list
        method virtual print_an_item: int -> unit
        end
class string_content strings = object
        inherit ['a] contentclass
        val items = strings
        method items = items
        method print_an_item i = print_endline (List.nth items i)
        end
class list_window (contents: 'a contentclass) = object (self)
        method print_an_item i = contents#print_an_item i
        method count_contents = List.length contents#items
        end

Thanks for any insights! I know this must be pretty basic, but …

The class language is restricted in a few ways, and one of them is that it can’t manipulate modules directly, so you have to unpack the module either before the class definition or inside the methods.
Here here examples of both approaches:

(* Before the class: using a functor *)
module MkListWindow (T : Contents) = struct
  class list_window = object
    method print_an_item i = T.print_an_item i
  end
end
(* Inside the methods *)
class list_window contents = object
  method print_an_item i =
    let module T = (val contents : Contents) in
    T.print_an_item i
end
(* Alternative: unpack fields individually *)
class list_window contents =
  let print_an_item =
    let module T = (val contents : Contents) in
    T.print_an_item
  in
  object
    method print_an_item i = print_an_item i
  end

It’s a relatively complex feature, but you can kind of transform abstract types into type variables in first-class module types:

type 'a content_mod = (module Contents with type t = 'a)

You can then annotate contents with the type 'a content_mod, define your class with a type parameter ['a] (if necessary), and that you should put you back into familiar territory (hopefully).

1 Like