RFC: More general type checking for structural typings

Maybe ReScript needs both field: int=? and field: option<int>.

1 Like

Or a compiler flag / config?

Maybe a notation {name: "test", age: 7, _} for automatically filling the optionals with None

4 Likes

Yeah this would be nice I guess.
I can then use this feature only for particular cases like ReactNative styles and would not use this for most of the other part of the codebase.

2 Likes

I think flags would only complicate things, and anyway, I think a lot of codebases would need both behaviors.

3 Likes

Maybe we should have this behavior only for @@obj annotated types, as Hongbo suggested, and keep the current behavior for the other types.

Then we could have both behaviors depending on the use case.

It would totally make sense not to be guided when adding new fields to a type with many optional fields.

3 Likes

Hi, I polished the implementation a little bit, the feature is close to be finalized.
So this new feature would be opt-in by using an attribute called @obj. For types like this:

@obj
type r = {
  x : int , 
  y : option <int>
}

let v0 = { x : 3 }
let v1 = { x : 3 , y : None}

generated JS would be:

var v0 = { x : 3 }
var v1 = { x : 3 }

The idea is that for objects annotated with obj, you don’t care about its performance in general, it is used in config patterns with lots of optionals.

Let me know if I miss anything or if any sematnics is unclear, thanks!

22 Likes

@Hongbo Thank you for this feature!!!

Thank you. Looks fantastic!

Really nice @Hongbo !

Amazing, thank you @Hongbo!

exciting! Regarding the name, I wonder if it makes sense to disambiguate from the @obj on externals, and provide something a little more descriptive, maybe like @stripUndefined ?

1 Like

Perhaps it still generates the constructor function as well? In that case it won’t break the behavior I think.

oh i see. @obj makes sense to me from that perspective :+1:

Btw I have some use cases where I don’t want the factory function to be generated. So it would be great if we can have a way not to be generated!

Wow this is such a great quality of life improvement, great job Hongbo!

@jfrolich I don’t really see the point of generating a function now to be honest.

I was thinking the function need to be created to make @obj backwards compatible, but I just realized the previous @obj is an annotation to a function instead of a record. So agree!

2 Likes

Amazing work @Hongbo!

This is great, thank you! It would make interop so much more comfortable.

I’m likely overcomplicating, but can’t it lead to a religious split in how people design their constructors in the ReScript land?

// Make a thing like this:
let myThing = Superlib.Thing.make("Bowser", {
  setting: "Mario",
  level: "Castle",
})

// versus
let myThing = Superlib.Thing.make("Bowser", ~setting="Mario", ~level="Castle", ())

An explicit and clear note about idiomatic way in the docs would help a lot.

UPD: You did the strong move for the fast-pipe vs pipe-last and the trailing unit arg saving hours of discussion. I see here the similar situation which requires a strong opinion from the BDFL.

There is no one size fits all solution in my opinion.
For function with around 3~4 optional arguments, the labeled ones seems to be more efficient.
But there are cases with 10 or even more optional arguments, you don’t want a function to have that many arguments, the object one makes more sense

4 Likes