Quantcast
Channel: ReScript Forum - Latest posts
Viewing all articles
Browse latest Browse all 2592

Proposing new syntax for zero-cost unwrapping options/results

$
0
0

One of the nicer patterns that this enables is using result + polymorphic variants for errors (AKA “errors as values”), in a way that makes it super simple to accumulate any errors that can happen and then be forced to handle them explicitly:

type user = {
  id: string,
  name: string,
  token: string,
}

external fetchUser: string => promise<
  result<JSON.t, [> #NetworkError | #UserNotFound | #Unauthorized]>,
> = "fetchUser"

external decodeUser: JSON.t => result<user, [> #DecodeError]> = "decodeUser"

external ensureUserActive: user => promise<result<unit, [> #UserNotActive]>> =
  "ensureUserActive"

let getUser = async id => {
  let? Ok(user) = await fetchUser(id)
  let? Ok(decodedUser) = decodeUser(user)
  Console.log(`Got user ${decodedUser.name}!`)
  let? Ok() = await ensureUserActive(decodedUser)
  Ok(decodedUser)
}

// ERROR!
// You forgot to handle a possible case here, for example: 
//  | Error(#Unauthorized | #UserNotFound | #DecodeError | #UserNotActive)
let main = async () => {
  switch await getUser("123") {
  | Ok(user) => Console.log(user)
  | Error(#NetworkError) => Console.error("Uh-oh, network error...")
  }
}

Notice how all errors that can possibly happen from each function called with let? is automatically accumulated and exhaustively checked for, with no extra effort on your part. If any of the called functions were to add another possible error, or you call a new function, those errors will automatically be accumulated for you as well.

This is a very ergonomic and efficient pattern for error handling, that also produces performant code.


Viewing all articles
Browse latest Browse all 2592

Trending Articles