Idiomatic method chaining with options?

Hello! When I was writing a dom-manipulating code in ReScript, I occasionally wrote the following code and got a type error (using rescript-webapi):

open Webapi.Dom
open Document
open Element
open Belt.Option

let elt = document->getElementById("foo")->map(setInnerHTML)("<p>Hello</p>")

(at the "<p>Hello</p>" part).
Apparently the reason is that the preceding expression is of type option<element => string>.

Is there a nicer way to write such a method chaining, as I feel such a method chaining would be usual in JavaScript and ReScript?

Thanks!

It would be great if you could provide the actual type error. Otherwise really hard to guess what’s wrong :smiley:

Also, those 4 open in the upper scope are just asking for unintentionally shadowed functions.

Not at my computer right now but the last part looks problematic. You’ve got an option that could be None and are trying to apply it to the hello text. Maybe just getElement → option.forEach( elem => sethtml to hello on the element )

Thank you for a very quick reply! The actual error is at setInnerHTML part (sorry the wrong place) saying This has type: (Webapi.Dom.Element.t, string) => unit, Somewhere wanted: option<'a>.

And I know that it could be rewritten like

switch 
  document
    -> Document.getElementById("foo")
    -> Option.map(Element.setInnerHTML) {
  | Some(f) => f("<p>Hello!</p>")
  | None => ()
}

However, I am wondering if it could be a little shorter, hopefully without pattern matching. (For me as a OCaml programmer, this is usual, but I’d like to know if there is a nicer way in the current ReScript ecosystem.)

Thanks!

I guess this is how I’d do it, if I’d be forced to use Belt.Option.map:

@set
external setInnerHTML: (Dom.element, string) => unit = "innerHTML"
@val external document: Dom.element = "document"
@send
external getElementById: (Dom.element, string) => option<Dom.element> =
  "getElementById"

let elt =
  document
  ->getElementById("foo")
  ->Belt.Option.map(el => el->setInnerHTML("<p>Hello</p>"))

JS Output:

var Belt_Option = require("./stdlib/belt_Option.js");

var elt = Belt_Option.map(document.getElementById("foo"), (function (el) {
        el.innerHTML = "<p>Hello</p>";
        
      }));

exports.elt = elt;

Playground Link

Thank you so much! I realised that something like optional chaining ?. in JavaScript, binding operator let* in OCaml, or point-free style, … is what I anticipated. For future reference, the following link would also be useful:

Again, thanks a lot!

Have you seen Belt.Option.flatMap?
I have it aliased to bind in our codebase for haskell familiarity.
I wonder if an operator would make it more familiar to js coders

2 Likes