Expand records returned by functions

Hello,
I just tagged this Learning because I’m actually learning Ocaml/Reasonml so I’m not sure if what I’m going to say makes sense or not.
Also sorry but I’m going to use Reason syntax because it’s more familiar to me, but I admit that I like Ocaml more.
Being a javascript developer I use the expansion operator a lot, and I was quite happy to see that Ocaml/Reason supports it as well. However, it seems to be very limited to the application scope. For example, let’s say I have a function that returns a record, like this =

let shift = (session: exercisesList) => {
    session: List.tl(session),
    current: List.hd(session),
  }

That function clearly returns a record, but I can’t do this:

let newRec = { ...shift (session) }

Neither this:

let info = shift (session)
let newRec = { ...info }

Seems that the only thing I can do is use records manually created.
Is there an alternative ? Am I doing something wrong ? Is this intentionally not supported ?

The syntax for record update ({ ...record, field:value, field2:value2} or { record with field1 = value1; field2 = value2 }) requires to update at least one field:

let remove = (session) => { ...shift(session), session: [] }

If you don’t need to update a field, you can just return shift(session).

1 Like

Thanks for your prompt response @octachron
Are you sure that the problem is with the lack of field update ? I didn’t think about that, and since I refactored the code I have added one more field to the record I’m expanding at.
I’ll check what you are saying and report back.

Thanks!

I tried to make some changes, and I’m getting type errors. I have this type:

type state = {
  session: exercisesList,
  current: Trainer.exercise_run,
  finished: bool,
};

The shift function returns a subset of that type, so I had to define an intermediary type that I called nextSlice

type nextSlice = {
  session: exercisesList,
  current: Trainer.exercise_run,
};

When I try to do this inside a react component:

...

  initialState: () => 
  { ...shift(session.exercises),
    finished: false,
  },
... 

I get a type error saying that the finished field is not part of the nextSlice type, and if I type the function to return state like this:

  initialState: ():state => 
  { ...shift(session.exercises),
    finished: false,
  },

Then the error is about next slice not being compatible with state.
The better solution I came to was to use destructuring to extract the fields and then re-build the state:

let {session, current} = shift(state.session);
    {session, current, finished: false};

Maybe I’m missing something

Record types are nominal in OCaml: from the point of view of the type system, record types are defined by their name. Consequently, the type system does not acknowledge any subtyping relationship between record types. From its point of view, the type state

type state = {
  session: exercisesList,
  current: Trainer.exercise_run,
  finished: bool,
};

and nextSlice

type nextSlice = {
  session: exercisesList,
  current: Trainer.exercise_run,
};

are just completely unrelated. Thus it is not possible to use a record update to expand a nextSlice to
a session. What you can do indeed, it is write a function that extends a nextSlice to a session

let extendSlice: (~finished:bool, nextSlice) => session

Ok,
How would you write such function ? making use of destructuring seems the better option:

let extendSlice = (finished, {session, current})=> {finished,session,current}

This seems fine indeed.

1 Like