That looks like an exception thrown at runtime, as expected when you try to get a value from None.
What is Js.nullable and Js.Nullable.t - are they the same?
Yes they are, you can see from the type equivalences in the API docs.
What is Obj (undocumented!?).
There are some undocumented modules inherited from OCaml which you wouldn’t normally use in ReScript.
What is the proper way of handling undefined/null returns from JS interop? Doing Js.Nullable.toOption?
If the return type could be a value or undefined, then it can be handle by using the option type, else you can use the Js.Nullable.t type if it could be null.
Re. Belt.Option.getExn - yes indeed, but it looks like the exception throwing itself is broken to me (like it’s trying to retrieve a missing error message, for example. Otherwise it should have a sensible error message!?)
Re. option for undefined - so if I am unsure if the method returns null or not sometimes (JS API docs do not always differentiate between null/undefined properly because it doesn’t mean anything of significance to many JS users), I should be playing safe with Js.nullable and then Js.Nullable.toOption?
So I’m picking up ReScript quickly. Bindings for JS interop was initially the biggest problem for me to wrap my head around. I think that kind of settled - I understand how to do bindings easily now and they really aren’t much of a problem. I feel decorators and whatnot need a bit of a cleanup, however. And also I think the compiler can ignore them in the wrong context and do nothing, rather than tell me I’m using them incorrectly.
For currying, I figured out the issue. I was trying to use type definitions for bindings (e.g.,
type t = {
someMethod: (string, string) => unit
}
I did this thinking I was being clever and idiomatic. Turns out I was just being stupid. Replacing these with @send externals instead fixed the auto-currying problem for me. Which makes sense - ReScript was trying to curry them since it thought they were ReScript functions.
My biggest pet peeve right now is async/await. The promise pipes are becoming ridiculously messy. Sometimes I need to pass on two values, then back to one, change it over, sometimes I have to do two promises together and sometimes they must be in sequence.
Async/await is coming in the release after V10. Not unlikely that there will be an alpha or similar in the not too distant future where it can be tried. Read more here: [ANN] Async/await is coming to ReScript!
As for == vs ===, I noticed the generated output is different. That makes sense to me knowing the difference it means in JS (== generates some caml object equality function call).
Should you generally prefer === everywhere in ReScript? I was hoping to get away from that since that is one thing I dislike about JS.
@ryyppy wrote an issue about this, should land in the docs at some point:
Personally, I almost always use ===, (mainly for Primitives).
I almost never use deep equal (==), since for complex data structures I use the equality method they provide. E.g. Map.String.eq, Set.Int.eq, etc. For records that have some methods associated to them, mostly we do not compare the whole thing, but only certain fields (e.g. dates for sorting).
If I ever need to compare a full record or (polymorphic) variant, then I use ==.
JavaScript itself does not even provide a deep equal operator. Even React compares via shallowEqual and not deep for performance reasons.
At least the compiler can tell you when you introduced a polymorphic comparison (warning no. 102).
Maybe you can help me and give suggestions on how to structure it more neatly? To me, it looks weird going back and forth between then and thenResolve with long functions.
Thanks. That doesn’t really clear it up though - someone pointed out earlier that “=== is almost what you want” and the link you posted also points out a very specific problem with poly variants where it just falls apart. I think I will stick to ==.
It seems it compiles to some caml equals function for abstract types when doing simple integer equality - but maybe that is expected. Do you need an example?
How can I parse a JSON object to a given type in a type safe manner?
I guess you found an edge case then. I don’t think it currently can optimize that away.
How can I parse a JSON object to a given type in a type safe manner?
This is another rabbit hole. There are multiple ways, all with their benefits and drawbacks. Have a look at this recent thread:
There is no blessed path (yet). Personally, I prefer to be able to generate decoders and encoders from types with atdgen. ppx_decco or ppx_spice help with that as well. Most other libraries require you to write the decoders yourself.
There is also the integrated Js.Json.t, but it is not as convenient.
This is the current expected behavior. Structural equality (==) is a bit “magical.” The compiler will compile it to === if it knows that the type is primitive. If it doesn’t (such as with abstract types) it uses the caml equal function to try to compare the structure of each value.
The caml equal function isn’t very efficient or 100% safe, which is why it’s a good practice for abstract types to provide their own equality function. Also, If a polymorphic function needs to test equality, then it’s good for it to accept a equality function as a parameter too. (Which is what most Belt functions do.)
FWIW, this is a quirk that ReScript inherited in OCaml. It’s not really ideal, but it’s how the compiler works for now. IMO it’s best to just avoid using == when possible.
Right now, I am working with MongoDB. This is how I’m thinking about structuring the type:
I have Document.t<'a> which is what is returned from the DB on query (option<Document.t…>). A document, however could be of different types depending on the collection I’m querying. For example, it might be Document.t<User.t>.
I need to be able to call some sort of save() method using the Document.t type, but I also need to be able to access the fields of User.t.
What would be a sane way to structure the type, or what would be a sane way to work with the object?
function mongoDbFind<T>(query: any): Document<T> | null ...
function findUser(email:string): Document<User> | null ...
const user = findUser("user@somedomain.com")
const id = user.id // from Document<T>
const email = user.email // from User
In essence, Document is a wrapper around User. A User is guaranteed to have the field email, and a Document is guaranteed to have the field id
How can I compose a type in ReScript so I can access both id and email on the object?