Suggestion to add operations on Result type

The “railway oriented programming” solves an important problem of highlighting the positive flow, and handling all the errors in one place. Also it makes code much cleaner in languages without early return (and with early return too).
But we don’t have a builtin way of doing it. There are already posts asking for Either, but the answer was that there’s already the Result type. Ok, it’s not a problem at all, but there’s no any operations on result type, that makes developers every time reinvent it in their own manner, eg:

From jzon:

module ResultX = {
  let mapError = (result, fn) =>
    switch result {
    | Ok(_) as ok => ok
    | Error(err) => Error(fn(err))
    }

  let sequence = (results: array<result<'ok, 'err>>): result<array<'ok>, 'err> => {
    results->Array.reduce(Ok([]), (maybeAcc, res) => {
      maybeAcc->Result.flatMap(acc => res->Result.map(x => acc->Array.concat([x])))
    })
  }
}

From reason-graphql:

module Result = {
  let bind = (x, f) =>
    bind(
      x,
      fun
      | Ok(x') => f(x')
      | Error(_) as err => return(err),
    );

  let mapError = (x, f) =>
    map(
      x,
      fun
      | Ok(_) as ok => ok
      | Error(err) => Error(f(err)),
    );

  let map = (x, f) =>
    map(
      x,
      fun
      | Ok(x') => Ok(f(x'))
      | Error(_) as err => err,
    );

  let let_ = bind;
};

Or my Either implementation:

module Either = {
  type t<'error, 'value> = Left('error) | Right('value)

  let map = (self: t<'error, 'value>, fn: 'value => 'newValue): t<'error, 'newValue> => {
    switch self {
    | Left(error) => Left(error)
    | Right(value) => Right(fn(value))
    }
  }

  let mapLeft = (self: t<'error, 'value>, fn: 'error => 'newError): t<'newError, 'value> => {
    switch self {
    | Left(error) => Left(fn(error))
    | Right(value) => Right(value)
    }
  }

  let asyncMap = (self: t<'error, 'value>, fn: 'value => Promise.t<'newValue>): Promise.t<
    t<'error, 'newValue>,
  > => {
    switch self {
    | Left(error) => Left(error)->Promise.resolve
    | Right(value) => fn(value)->Promise.thenResolve(newValue => Right(newValue))
    }
  }

  let asyncMapLeft = (self: t<'error, 'value>, fn: 'error => Promise.t<'newError>): Promise.t<
    t<'newError, 'value>,
  > => {
    switch self {
    | Left(error) => fn(error)->Promise.thenResolve(newError => Left(newError))
    | Right(value) => Right(value)->Promise.resolve
    }
  }

  let chain = (self: t<'error, 'value>, fn: 'value => t<'newError, 'newValue>): t<
    'newError,
    'newValue,
  > => {
    switch self {
    | Left(error) => Left(error)
    | Right(value) => fn(value)
    }
  }

  let asyncChain = (
    self: t<'error, 'value>,
    fn: 'value => Promise.t<t<'newError, 'newValue>>,
  ): Promise.t<t<'newError, 'newValue>> => {
    switch self {
    | Left(error) => Left(error)->Promise.resolve
    | Right(value) => fn(value)
    }
  }
}

At first I’d like to know is there such a pain in community.
After this together create a proposal library such as https://github.com/ryyppy/rescript-promise

5 Likes

I think those functions should be added to Belt, there’s no reason why they would be refused by the maintainers.

6 Likes

Actually I’ve found what I wanted in the https://github.com/rescript-lang/rescript-compiler/blob/master/jscomp/others/belt_Result.ml.
The initial confusion was because of the Js.Result | ReScript API page that’s completely empty.
Belt.Result has everything I need in my work. The only thing that’s missing for me is integration with promises, but I don’t think it should be in the standard library.

1 Like

Although mapError would be useful. It exists in every modification of Result I used as examples.

2 Likes

yes, don’t hesitate to create a PR to add mapError to Belt.Result, I think it would be a nice addition!

Yeah, I’m going to do it in a near future. It doesn’t look like something difficult.

1 Like

Where is the Belt repo? I’d like to start this, but can’t find it anywhere!

In the rescript-compiler repo: rescript-compiler/tree/master/jscomp/others
(all still written in .ml)

2 Likes

It would be nice. I’m currently busy with escaping Russia, so I don’t really have time for coding

2 Likes

I hope you’re doing alright, good luck!