re: Compact or Verbose Codestyle? VIEW POST

TOP OF THREAD FULL DISCUSSION
re: The problem is that when not using a Result type this would actually be hard to use in JS. You would have to check const result = get('a','b') i...

Typically I would throw inside the get function here. I did not do this in the example because you can't throw inside the expression and I wanted to keep the logic identical between the two.

However, I do sometimes respond to the caller with something like:

{error: {
  msg,
  origin: new Error(msg)
}};

... then I handle that up the stack.

{
  setupView () {
    const something = await fetch ('something');

    if (something.error) {
      // handle error (throw or return  error depending on level of fatality)
      return something;
    }

    // Guarded logic
    updateView(something);
  }
}

I tend to bubble recoverable errors back up the stack.

Your comment about being idiomatic interested me. Can you elaborate? I might learn something valuable if you have the time to share an idiomatic example.

I don't think "Idiomatic JavaScript" is something to be particularly proud of, good patterns aren't the most popular here.

The approach with .error seems more conventional though, since this is similar to what you'd get in a JSON response on a remote error. (I only noticed the second example is literally await fetch after I finished writing this. haha)

Promises are already essentially Future<Result<T,E>>, with await really meaning (await promise).unwrap(), which IMHO is a big design flaw. This means that there isn't a built in Result type which we could use rich combinators with, while we can't properly express infalible futures like timeouts, which will never reject. And once await is added to the mix, we end up having to do the traditional, "idiomatic" try-catch around it.

What if you have a function that can throw, and on success returns a promise that might reject? Today you have to do:

try {
  const promise = beginThang()
} catch (err) {
  // handle E1
}
let /* wow, so glad this will be a mutable binding now */ result
try {
  result = await promise
} catch (err) {
  // handle E2
}
return makeUseOf(result)

While it could have been:

beginThang()
  .mapErr(err => {/*  handle E1  */}) // line optional, can just bubble error
  .andThen(async res => {
    (await res)
      .mapErr(err => {/*  handle E2  */}) // line optional, can just bubble error
      .map(makeUseOf)
  })

Note how we get a Result<Future<Result<T,E2>>,E1> from beginThang and ourselves return a Result<Future<Result<T2,E4>>,E3> (or Result<Future<Result<T2,E2>>,E1> if we just bubble errors), so immediate errors can be handles synchronously instead of wrapping our bad outcomes in a Promise.reject(err) so they wait until the next round of the event loop.

Another thing that is not used at all in "idiomatic JavaScript" is dependency injection.

code of conduct - report abuse