I think this really depends on what you are trying to accomplish. There are a lot of awkward APIs out there that attach semantic meaning to a field being missing (rather than existing, but undefined or null). My first suggestion would be to write your FFI binding such that weird stuff like this isn’t leaking into your PureScript code.
If you want the convenience of having required/optional fields, and all optional fields have a default value (as they really really should), then I’d define two functions:
- One that takes the full set of options.
- One that has Row constraints for
Record.mergeing with defaults.
The reason being that the former has better type inference and suggestions since no constraints are involved.
I personally try to avoid other solutions as best I can, and it’s primarily because I think a lot of the motivation comes from wanting to attach PureScript semantics to JS directly, which is always going to be a bear. I do think
option is the cleanest API with actual PureScript semantics.
options predates all the Row constraint magic, AFAIK. I don’t really like the
Union approach, because you can’t actually write PureScript with those constraints to achieve those results! To me that sounds awfully fishy, though I’ll admit it’s certainly ergonomic. I haven’t used the other ones.
I’ll note, that you can do pretty much anything with enough constraints, but it’s not going to be a great experience. As an example, https://github.com/purescript-web/purescript-web-fetch/blob/master/src/Web/Fetch/Request.purs is a well-typed version of the Fetch API. It’s pretty neat that you can do all the auto-lifting and conversions with typeclass magic, but I wouldn’t want to actually write and maintain a real API like this.