Functors in Js_of_ocaml, type mismatch

Hi, this is probably my misunderstanding of functors, but I could not solve it.

I have a module that defines a type for the dom canvas element, but passing it a canvas leads to type mismatch. Alternatively, I guess it could be due to it being a Js.t object.

A bit simplified:

open Js_of_ocaml

module type Painter = sig
  type ctx

  type surface
end

module CanvasPainter : Painter = struct
  type ctx = Dom_html.canvasRenderingContext2D Js.t

  type surface = Dom_html.canvasElement Js.t
end

module MyViewer : GraphViewer = functor
  (G: GraphModel)
  (P: Painter)
  (GP: GraphPainter)
  -> (struct
      module MyPainter = (GP (G) (P))

      type state = {
        graph: G.t;
        surface: P.surface;
      }

      let make_graph ns es = (ns, es)

      let redraw st = MyPainter.draw st.graph st.layout st.ctx

      let make_state g s = {
        graph=g;
        surface=s;
      }
    end)

module Viewer = MyViewer (Graph) (CanvasPainter) (GRAPH_PAINTER)

let main =
  let graph = Viewer.make_graph [] [] in
  let canvas = (Js.Opt.get
       (Js.Opt.bind
          (Dom_html.document##getElementById (Js.string canvas_id))
          Dom_html.CoerceTo.canvas)
       (fun _ -> failwith "Could not find canvas"))
  in
  let viewer_state = (Viewer.make_state
                        graph
                        canvas)
  in
  Viewer.redraw viewer_state

Gives an error (which is in the call to make_state):

File "bin/main.ml", line 54, characters 38-44:
54 |                                       canvas
                                           ^^^^^^
Error: The value canvas has type Dom_html.canvasElement Js.t
       but an expression was expected of type CanvasPainter.surface

The types seem to align?

I am at a point where functors seem attractive for many things, so also welcoming comments on my use of them.

In this line, the whole module is reduce only to the signature of Painter. And looking in the signature, the type surface is abstract.

You probably wanted to write something like:

module CanvasPainter : Painter with type painter = Dom_html.canvasElement Js.t

Thank you.

This is interesting… not quite how I imagined module signatures to work. So isn’t there a way to define all members of Painter without a with?

In the repo examples they use a different pattern, but seems similar enough.

So, just removing : Painter does compile. The signature is already limited at the call site, so this should be fine.

Edit: so my updated understanding is that giving a module definition a signature makes available only the abstract definitions, unless defined with with. Passing a signature to a functor or to a value acts as an “interface” definition.

The compiler is already able to infer the type for all the members, and using a signature explicitaly is usefull when you want to:

  • restrict the type of the elements seed outside of the module (see the examples for the private type)
  • ensure the module stick with a given interface (for example for first class modules)

Most of the time, you do not need to add the module signature manually.