Translating guard clauses to ReScript

In ReScript, how would you do what you would normally do with guard clauses in JS? For example:

function MyComponent() {
  let result = loadSomeData();

  if (!result) {
    return "No data loaded";
  }
  
  // complex render based on the data
}

In ReScript I’m using a switch but this causes a lot of nesting for the most important part of the function which reduces readability.

i.e.

@react.component
let make = () => {
  let result = loadSomeData();

  switch (result) {
    | None => React.string("No data loaded")
    | Some(data) => {
      // complex render based on the data
    }
  }
}

The above is a somewhat simplified example, in the actual component where I’m facing this the data loading takes a state value as argument and the component rendered with data will change that state to change what it’s displaying.

1 Like

There isn’t anything at the moment. Everything must be an expression which means early return doesn’t exist; whether it’s an if/else, or a switch, or Belt.Option.map with a function, you’re going to end up with nesting somewhere.

I usually put up with the nesting and refactor the complex code into another function. That isn’t always nice to maintain, though.

2 Likes

There is a PR implementing guard syntax:

2 Likes

Practically speaking, today you can do things like:

let make = () => switch (loadSomeData()) {
  | None => ...
  | Some(data) => ...
}

Or if you have multiple conditions to guard:

let make = () => switch (loadSomeData(), loadOtherData()) {
  | (Some(data), Some(otherData)) => ...
  | _ => ...
}

This pattern matching on multiple pieces of data at the same time reduces rightward drift, which is the main purpose of guard syntax.

3 Likes

The guard clause syntax looks interesting, although I’ll have to get used to parsing it since it’s [assignment, when condition, terminate] rather than [terminate condition, terminate, continue].

I know the multiple switch trick, haven’t necessarily thought of using it here. That’s something I’ll have to remember.

Hoisting the switch into the function declaration doesn’t work unfortunately since React components usually start with one or more hook invocations, where this doesn’t really work well.

An upside is that early return is not possible so you’re sure that is not a possibility when reading code (you don’t have to read the function in reverse order to see if it there are early returns). Combined with pattern matching the end result is clearer I think. To make it even more readable I really liked the fun syntax in reason where you basically pattern match guards without an extra level of nesting (but that is gone in ReScript syntax).

3 Likes

Not sure I understand this :thinking:

I too think fun is fun and would love to see it make a return to ReScript ^^

1 Like

I love fun too, it’s so close to the algebraic notation👌

5 Likes

If you’re reading a longish function you are sure that the function is not returning early for some conditions.

Ah I see! I was trying to fit it in with the guard clause syntax proposal. Since in that case your statement is no longer true. That’s why I couldn’t place it :slight_smile: