Combine/extend records

i’m aware objects are a thing, but what if my types have a fixed shape but that shape is the same as some other type, with one or two extra fields? i could just copy-paste, but it’d be quite a big pain having to copy-paste everything every time the definitions change

(tl;dr it’d be nice if there was type c = {...a, ...b} for records)

There are two kinds of records in rescript.
One is fixed shape, it is called regular record, where you can not do it, you can do it with embedding:

type t0 = { x : int}
type t1 = { y : int}
type t2 = { t0 : t0, t1 : t1} 

The other is structural record, where you can do it:

type t0 = { "x" : int} 
type t1 = { "y" : int}

type t2 = { ... t0, ... t1}
3 Likes

yeah, well… i was just wondering why it’s not possible to do it with regular records - i feel like it would be useful for e.g. functors

thanks for the awesome information.

Records are not structural types. Use structural records.

if you read my original post, you’ll find that i am, in fact, aware that structural records (aka objects) exist…
so my question is not “can i do this with objects”
it’s “is there a reason we can’t spread records”
especially given that e.g. include Module is a thing, which is basically spreading except for modules

Module are structurally typed though

3 Likes

Modules exist on a different layer but you’ll find them more akin to structural records rathen records. You’re effectively asking why a certain language primitive with a specific purpose is not something that you want it to be. I doubt that making records structural instead of nominal makes sense for the language. It’s like asking why ints don’t support string operations. I think that’s where mine confusion with your question comes from

3 Likes

i don’t want structural records though? but i’m assuming regardless whether it’s a record or an object, the typechecker would have the fields right? so combining the fields is a thing that can be done?

again, the answer i’m looking for isn’t “why do you want to do this, records aren’t structural”, i know this, the docs say that
the answer i’m looking for would be something more like “we can’t do this because we resolve spreads before typecheck” or “the architecture just doesn’t support it right now” or “it’s just not implemented because it isn’t very useful”

Then I guess the answer would be “Because nominal typing that the records have is a useful property of the records. Because of nominal typing “spreading” one record type into another goes against the type system works fundamentally”. But that’s just my take.
I won’t lie I did come across some situations where I had record types that differ in a couple of properties. I used objects for them. And I used type spread. Normally it led to a disaster (again for me) because I’d fail to recognise when the types that seemed to only have a couple of fields different diverge to a point where they are radically different. And the compiler wouldn’t be there for me to help

re: modules - note that i do believe it’s possible to achieve something very similar by using functors, and having getters/setters instead of a concrete type - so it stands to reason that this is possible (maybe it’s a bad idea, maybe not) for records (even though they are nominally typed)

Perhaps technically it could be done. However every new feature provided by the compiler is a complexity and maintenance burden. Look at TypeScript–it may well have a Turing-complete type system now. In my view that’s not the ReScript philosophy. The goal is not to provide a kitchen sink language but a small, curated feature set.

When some use case becomes a pain point, like having to copy-paste many fields across many record types, that usually indicates a design smell. One suggestion is to examine if structural types (objects) could be used instead.

i don’t want structural records though? but i’m assuming regardless whether it’s a record or an object, the typechecker would have the fields right? so combining the fields is a thing that can be done?

It could be done for simple types. The complexity comes from polymorphic and recursive types. For structural types, it is by definition expandable.

We could add a special case for non-recursive/non-polymorphic types, but you will end up lots of ad-hoc rules

5 Likes