Beginner: approaches for package API discovery (eg. react)

Hi, I’m new to both ReScript and React, and excited to finally try React in a pragmatic functional setting. (Coming from a background of extensive experience with both Vue and Elm.)

I’ve just started playing around with a counter app and thought I’d try introducing an input field, but got stuck with the onInput handler. I have a specific question about this, but also one about the recommended general approach for discovering/learning the API for ReScript packages.

So my specific problem is simply that onInput expects ReactEvent.Form.t => unit, and I don’t know how to get the input element’s value out of ReactEvent.Form.t for use with a useState update function. The example from React’s docs has e => updateFoo(e.target.value), so I tried this but evidently ReactEvent.Form.t is not a record.

My next step was to look for the ReScript React API docs but I can find nothing beyond the (very useful) guide on rescript-lang.org. I gather that there is no central mechanism for package documentation, equivalent to Haddock or package.elm-lang.org. So I had a look in the rescript-react repo and found this in ReactEvent.resi:

module Form: {
  @get external target: t => {..} = "target"
}

That at least got me as far as realising that I should access target via a function. As for its return type, I was stumped. Eventually I guessed that it might be an object, and finally arrived at ReactEvent.Form.target(e)["value"] which compiles.

So my specific question: what is this {..} syntax? Is it explained anywhere in the language docs?

And my more general question: what workflow could I have followed to arrive at the right answer here? Am I correct that there’s no standard documentation mechanism? For a package with no API docs, is my best bet to read .resi files?

Ideally I feel I should be able to find the answer to a problem like this within VS Code. Is there a way to do that? It might be my unfamiliarity with VS Code (I’m an IntelliJ user), but I couldn’t find a way to navigate to or bring up any more information about the definition of ReactEvent.Form.t.

Open object

{..} is the open object which makes it easier for binding authors, because they do not need to exhaustively list all the things that can be returned from the event. It’s a tradeoff for sure, but results in less verbose code, especially when dealing with the DOM and Events which are very polymorphic.

If you want to have more type safety, you can make the binding more specific to your use-case.

module Form = {
  type target = {value: string}
  @get external target: t => target = "target"
}

The open object {..} is unfortunately not yet documented in the Syntax Lookup yet, and it is also not explained well enough in the docs in my opinion.

Better docs

You’re right that you should not need to have to open the .resi file to know how a function is supposed to work. In the best case the function needs to show a usage example right when you autocomplete ReactEvent.Form.target. That’s what doc comments (/** */) are for, which only recently got support in version 10.

For now, you can achieve the same with the ugly @ocaml.doc decorator:

@ocaml.doc("
Example:
` ` `
let onClick = event => ReactEvent.Form.target(event)[\"value\"]
")
` ` `
@get external target: t => target = "target"

(Remove the spaces between the backticks for it to work)

and it would appear like that in VSCode on hover (but somehow not yet on autocomplete):

There is only a small number of ocaml.doc comments in the rescript-react repo yet, I hope it will get better with the dedicated syntax.

Conclusion

Learning and understanding type signatures (.resi files) is a helpful skill, regardless if there are already better docs or not. It is good to get reminded by beginners, that it should not be the only way. If the official docs seem not sufficient for you, check out awesome-rescript, especially the example apps.

Right, thanks for the explanation. I figured it must be something like that. What surprises me the most about not having learned the syntax in the tutorial (besides how quickly I stumbled into it!) is that it’s quite emphatic about there being only one escape hatch in the language, namely the %identity casting thing. But surely this is another feature that undermines the soundness of the type system?

Thanks also for mentioning the syntax lookup page, I hadn’t noticed that.

I realised afterwards that I could hover on ReactEvent.Form to see a preview of its resi declarations. So, especially when enhanced with doc comments, resi files are the canonical documentation source, ReScript’s Haddock equivalent.

What would be really nice is if the package explorer (rescript-lang.org/packages) was extended such that you could click through to a docs page for each package, with extracted and rendered resi declarations with their doc comments.

Can you point to where that is said? Because it doesn’t seem exactly correct, I mean, any incorrect usage of ReScript’s extensive JavaScript FFI features will result in unsoundness. And the open object feature definitely falls under this category.

ReScript’s type system is robust and does not allow dangerous, unsafe stuff like implicit type casting, randomly guessing a value’s type, etc. However, out of pragmatism, we expose a single escape hatch for you to “lie” to the type system

I’d say open object counts as “randomly guessing a value’s type”

Yeah, we should probably add a disclaimer there that this is aside from the FFI which could potentially also cause unsound types.

1 Like

I get that this page is partly spin, and it’s much nicer to be able to say “only one escape hatch”. Other more subtle ways of introducing unsoundness could be admitted later, but it seems to me that open object is explicitly an escape hatch and so the above quoted statement amounts to a lie.

Ok yeah, a disclaimer about FFI would cover it. :slightly_smiling_face: