A few small questions

I like piping more. I’m going to add a eslint rule preventing async/await usage in my team when the feature released.

1 Like

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 ==.

I think that is fine. For simple cases it should always compile to === anyways. Just thoroughly test what it does for complex cases, though.

Actually I am not sure anymore, why I still avoid ==. There were some weird edge cases but I guess they are fixed now.

Thanks.

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.

1 Like

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.

What is the difference between Js_dict and Js.Dict and so forth, and which one should I be using?

They’re the same, however, use the latter one.

How can I “nest” types?

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?

Would need to see an example of using it in plain JS to figure out how to target the usage in ReScript.

Ok, pseudocode:

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?

What I’m doing right now:

module Document = {
  type t<'a> = 'a
}

I think this would be sort of what I’m trying to do:

module Document = {
  type t<'a> = {
    id: string,
    ...'a
  }
}

Maybe I’m missing the complexity here but this looks pretty simple to me:

type user = {
  id: string // note, I'm assuming this is a string, you will know better
  email: string,
}

@val
external findUser: string => user = "findUser"

I think the complexity is separation-of-concerns. I’m composing multiple different types of documents, but they are all mongo documents.

This means that id should be in document, but email should be in user (just as an example).

How should I think about {…} - is it type safe, and how can I convert a JSON object to a {…} (assuming it isn’t type-safe!?}

How can I deal with polymorphic interop?

E.g., React sends an array of React.element or a React.element depending on whether someone is using my component with one or many children. I need to ensure it is always an array. Can I do this with ReScript or should I used %raw?

You can think of it as equivalent to TypeScript’s object type, it allows treating the value as an object and getting any field from it. There’s no guarantee that the object actually has those fields though, so it’s not particularly typesafe. You can convert a JSON object to various types but the goal should usually be to convert it into a record type which offers maximum safety and convenience.

React sends an array of React.element or a React.element depending on whether someone is using my component with one or many children. I need to ensure it is always an array.

Can you show some example code of how this is done in JavaScript?

Thank you.

JS solution: children = Array.isArray(children) ? children : [children]

I think the complexity is separation-of-concerns. I’m composing multiple different types of documents, but they are all mongo documents.

Gotta ask yourself, is the complexity from separaing these concerns actually gaining you any value in the long run? Are there often times where you want a user that doesn’t have an ID?

A way around it is to treat Document.t as a box with an ID that contains a value, and then use an identy casting function to “extract” the data

module Document = {
   type t<'a> = {id: string}
   external data: t<'a> => 'a = "%identity"
}

let userDocument: Document.t<user> = {/**/}

let id = userDocument.id
let user = Document.data(userDocument)

children = Array.isArray(children) ? children : [children]

hmm I can’t think of a use case where modifying the react children would be useful except for intentionally calling Array.map on them to do something, which that I would consider an anti pattern. You can probably implement something similar using context