Better JavaScript API support? + Is it a good idea to create alternative DOM bindings?

I’m currently using Js_of_ocaml to create an SVG-based web interface. I think that OCaml as a language is very enjoyable due to its type system, and I wouldn’t use JavaScript instead. I’ve also found Js_of_ocaml to work really well with Dune without me needing to get my hands dirty. However, I have been frustrated by the Js_of_ocaml API.

One major issue is the separation between the Dom_html and Dom_svg modules. They each define their own different types, such as (Dom_html|Dom_svg).element and (Dom_html|Dom_svg).document. In the case of element, I was able to work around the incompatibility by coercing to a common supertype. However, I just hit a roadblock because Dom_svg.document is not a supertype of Dom_html.document. Dom_html.window's document attribute is of type Dom_html.document, and I cannot use it as a Dom_svg.document.

I feel the need to create my own DOM bindings from the ground up because I do not feel that I can work with Js_of_ocaml’s bindings. However, I do not want to further OCaml ecosystem fragmentation (repeating the “multiple standard library” issue). I also feel concerned about existing fragmentation in the OCaml-JavaScript sphere due to BuckleScript, which has yet another standard library extension and DOM API, and supports interop with NPM packages but not OCaml packages.

  • Is it a good idea for me to create my own bindings and share them as a library?
  • If so, may I solicit advice from other people to see how I may design the library to be useful for them?
  • Would it be too ambitious to attempt to support both Js_of_ocaml and BuckleScript, to prevent fragmentation between compilers? The two compilers have different FFIs.

What are you trying to do exactly, and why is js_of_ocaml’s API insufficient ? If you are looking mostly into building new elements (and making them change over time), then tyxml provides an alternative take that might be sufficient.

Note that unless you are writing an svg document with embedded javascript (and not an html document with pieces of svg in it), Dom_svg.document is probably not what you want.

1 Like

Thank you, I didn’t realize that tyxml existed,

The functions that create new SVG elements (createG, createRect, etc.) take Dom_svg.document as an argument. I cannot pass the document of an arbitrary window, as the document attribute of Dom_html.window is a Dom_html.document ( Is there a reason why there are two different types?

EDIT: I looked into tyxml and it seems that it is only for building XML documents? My use case involves heavy DOM manipulation of existing elements as well.

Okay, I managed to use cppo to implement conditional compilation, so that different implementations of the same interface can be used with either Js_of_ocaml and BuckleScript. Is this a good idea? I have to think about it…

I tried making something that could work with both BuckleScript and Js_of_ocaml, but I found it infeasible because the two compilers handle things like objects differently in a way that I couldn’t abstract away. Js_of_ocaml’s bindings don’t always reflect the actual DOM API, but I think that I’d rather work around any rough edges than try to build an alternate bindings. I’d close this topic if I had the privileges.

EDIT: Although I couldn’t find it on MDN, it seems that document, when used with SVGs, does have a rootElement attribute, so having separate types is correct. The functions for creating SVG elements should then just take a Dom.document instead of Dom_svg.document so one can use it with window##.document. For the most part, it seems that Js_of_ocaml covers the DOM API, with a few rough edges with the types that I’m unfortunately hitting. I take back what I said before about wanting to create new bindings.

1 Like

Out of curiosity–could you provide a small example of the difference in handling objects?

Sorry, I simply meant that the two compilers use different ways to map between JS objects and OCaml. Js_of_ocaml has readonly_prop, writeonly_prop, prop, and meth. Attribute and methods are accessed differently (##. vs ##) and attributes are set with :=. BuckleScript separates const attributes, mutable attributes, and methods with [@bs.set] and [@bs.meth]. Both attributes and methods are accessed with ##, and attributes are set with #=. I know that Js_of_ocaml uses a PPX, and I was unable to find out how BuckleScript handles its syntax. I simply decided that trying to be compatible across both compilers was infeasible.

Ah, thank you for the explanation. Yeah I agree, you would have had to jump through a few hoops to abstract away all those differences.