Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#### :rocket: New Feature

- Add `littleEndian` feature for `DataView` to Stdlib. https://github.com/rescript-lang/rescript/pull/7881
- Add `mapOkAsync`, `mapErrorAsync`, `flatMapOkAsync` and `flatMapErrorAsync` for async `result`'s to Stdlib. https://github.com/rescript-lang/rescript/pull/7906

#### :bug: Bug fix

Expand Down
24 changes: 24 additions & 0 deletions packages/@rescript/runtime/Stdlib_Result.res
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,27 @@ let all6 = ((a, b, c, d, e, f)) => {
}

external ignore: result<'res, 'err> => unit = "%ignore"

let mapOkAsync = async (res, f) =>
switch await res {
| Ok(value) => Ok(f(value))
| Error(err) => Error(err)
}

let mapErrorAsync = async (res, f) =>
switch await res {
| Ok(value) => Ok(value)
| Error(err) => Error(f(err))
}

let flatMapOkAsync = async (res, f) =>
switch await res {
| Ok(value) => await f(value)
| Error(err) => Error(err)
}

let flatMapErrorAsync = async (res, f) =>
switch await res {
| Ok(value) => Ok(value)
| Error(err) => await f(err)
}
118 changes: 102 additions & 16 deletions packages/@rescript/runtime/Stdlib_Result.resi
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ type t<'res, 'err> = result<'res, 'err> = Ok('res) | Error('err)
Result.getExn(Result.Ok(42)) == 42

switch Result.getExn(Error("Invalid data")) {
| exception _ => assert(true)
| _ => assert(false)
}
| exception _ => true
| _ => false
} == true

switch Result.getExn(Error("Invalid data"), ~message="was Error!") {
| exception _ => assert(true) // Throws a JsError with the message "was Error!"
| _ => assert(false)
}
| exception _ => true // Throws a JsError with the message "was Error!"
| _ => false
} == true
```
*/
@deprecated("Use 'getOrThrow' instead")
Expand All @@ -74,14 +74,14 @@ let getExn: (result<'a, 'b>, ~message: string=?) => 'a
Result.getOrThrow(Result.Ok(42)) == 42

switch Result.getOrThrow(Error("Invalid data")) {
| exception _ => assert(true)
| _ => assert(false)
}
| exception _ => true
| _ => false
} == true

switch Result.getOrThrow(Error("Invalid data"), ~message="was Error!") {
| exception _ => assert(true) // Throws a JsError with the message "was Error!"
| _ => assert(false)
}
| exception _ => true // Throws a JsError with the message "was Error!"
| _ => false
} == true
```
*/
let getOrThrow: (result<'a, 'b>, ~message: string=?) => 'a
Expand Down Expand Up @@ -259,8 +259,8 @@ let forEach: (result<'a, 'b>, 'a => unit) => unit

```rescript
let format = n => `Error code: ${n->Int.toString}`
Result.mapError(Error(14), format) // Error("Error code: 14")
Result.mapError(Ok("abc"), format) // Ok("abc")
Result.mapError(Error(14), format) == Error("Error code: 14")
Result.mapError(Ok("abc"), format) == Ok("abc")
```
*/
let mapError: (result<'a, 'b>, 'b => 'c) => result<'a, 'c>
Expand All @@ -269,8 +269,8 @@ let mapError: (result<'a, 'b>, 'b => 'c) => result<'a, 'c>
`all(results)` returns a result of array if all options are Ok, otherwise returns Error.
## Examples
```rescript
Result.all([Ok(1), Ok(2), Ok(3)]) // Ok([1, 2, 3])
Result.all([Ok(1), Error(1)]) // Error(1)
Result.all([Ok(1), Ok(2), Ok(3)]) == Ok([1, 2, 3])
Result.all([Ok(1), Error(1)]) == Error(1)
```
*/
let all: array<result<'a, 'b>> => result<array<'a>, 'b>
Expand Down Expand Up @@ -321,3 +321,89 @@ let all6: (
without having to store or process it further.
*/
external ignore: result<'res, 'err> => unit = "%ignore"

/**
`mapOkAsync(res, f)`: Asynchronously maps over the Ok value of a Result. When `res` is `Ok(n)`,
applies the async function `f` to `n` and wraps the result in `Ok`.
When `res` is `Error`, returns the error unchanged.

## Examples

```rescript
let asyncSquare = async x => x * x

let result1 = Result.mapOkAsync(Promise.resolve(Ok(4)), asyncSquare) // Returns promise that resolves to Ok(16)
let result2 = Result.mapOkAsync(Promise.resolve(Error("invalid")), asyncSquare) // Returns promise that resolves to Error("invalid")
```
*/
let mapOkAsync: (
promise<result<'ok, 'error>>,
'ok => 'mappedOk,
) => promise<result<'mappedOk, 'error>>

/**
`mapErrorAsync(res, f)`: Asynchronously maps over the Error value of a Result. When `res` is `Error(e)`,
applies the async function `f` to `e` and wraps the result in `Error`.
When `res` is `Ok`, returns the ok value unchanged.

## Examples

```rescript
let asyncFormatError = async e => `Error: ${e}`

let result1 = Result.mapErrorAsync(Promise.resolve(Ok(42)), asyncFormatError) // Returns promise that resolves to Ok(42)
let result2 = Result.mapErrorAsync(Promise.resolve(Error("invalid")), asyncFormatError) // Returns promise that resolves to Error("Error: invalid")
```
*/
let mapErrorAsync: (
promise<result<'ok, 'error>>,
'error => 'mappedError,
) => promise<result<'ok, 'mappedError>>

/**
`flatMapOkAsync(res, f)`: Asynchronously flat-maps over the Ok value of a Result. When `res` is `Ok(n)`,
applies the async function `f` to `n` which returns a Promise of a Result.
When `res` is `Error`, returns the error unchanged.

## Examples

```rescript
let asyncValidate = async x =>
if x > 0 {
Ok(x * 2)
} else {
Error("Must be positive")
}

let result1 = Result.flatMapOkAsync(Promise.resolve(Ok(5)), asyncValidate) // Returns promise that resolves to Ok(10)
let result2 = Result.flatMapOkAsync(Promise.resolve(Error("Already failed")), asyncValidate) // Returns promise that resolves to Error("Already failed")
```
*/
let flatMapOkAsync: (
promise<result<'ok, 'error>>,
'ok => promise<result<'mappedOk, 'error>>,
) => promise<result<'mappedOk, 'error>>

/**
`flatMapErrorAsync(res, f)`: Asynchronously flat-maps over the Error value of a Result. When `res` is `Error(e)`,
applies the async function `f` to `e` which returns a Promise of a Result.
When `res` is `Ok`, returns the ok value unchanged.

## Examples

```rescript
let asyncRecover = async error =>
if error === "timeout" {
Ok("default")
} else {
Error(error)
}

let result1 = Result.flatMapErrorAsync(Promise.resolve(Error("timeout")), asyncRecover) // Returns promise that resolves to Ok("default")
let result2 = Result.flatMapErrorAsync(Promise.resolve(Ok("default")), asyncRecover) // Returns promise that resolves to Ok("default")
```
*/
let flatMapErrorAsync: (
promise<result<'ok, 'error>>,
'error => promise<result<'ok, 'mappedError>>,
) => promise<result<'ok, 'mappedError>>
58 changes: 58 additions & 0 deletions packages/@rescript/runtime/lib/es6/Stdlib_Result.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,60 @@ function all6(param) {
}
}

async function mapOkAsync(res, f) {
let value = await res;
if (value.TAG === "Ok") {
return {
TAG: "Ok",
_0: f(value._0)
};
} else {
return {
TAG: "Error",
_0: value._0
};
}
}

async function mapErrorAsync(res, f) {
let value = await res;
if (value.TAG === "Ok") {
return {
TAG: "Ok",
_0: value._0
};
} else {
return {
TAG: "Error",
_0: f(value._0)
};
}
}

async function flatMapOkAsync(res, f) {
let value = await res;
if (value.TAG === "Ok") {
return await f(value._0);
} else {
return {
TAG: "Error",
_0: value._0
};
}
}

async function flatMapErrorAsync(res, f) {
let value = await res;
if (value.TAG === "Ok") {
return {
TAG: "Ok",
_0: value._0
};
} else {
return await f(value._0);
}
}

let getExn = getOrThrow;

let mapWithDefault = mapOr;
Expand Down Expand Up @@ -368,5 +422,9 @@ export {
all4,
all5,
all6,
mapOkAsync,
mapErrorAsync,
flatMapOkAsync,
flatMapErrorAsync,
}
/* No side effect */
58 changes: 58 additions & 0 deletions packages/@rescript/runtime/lib/js/Stdlib_Result.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,60 @@ function all6(param) {
}
}

async function mapOkAsync(res, f) {
let value = await res;
if (value.TAG === "Ok") {
return {
TAG: "Ok",
_0: f(value._0)
};
} else {
return {
TAG: "Error",
_0: value._0
};
}
}

async function mapErrorAsync(res, f) {
let value = await res;
if (value.TAG === "Ok") {
return {
TAG: "Ok",
_0: value._0
};
} else {
return {
TAG: "Error",
_0: f(value._0)
};
}
}

async function flatMapOkAsync(res, f) {
let value = await res;
if (value.TAG === "Ok") {
return await f(value._0);
} else {
return {
TAG: "Error",
_0: value._0
};
}
}

async function flatMapErrorAsync(res, f) {
let value = await res;
if (value.TAG === "Ok") {
return {
TAG: "Ok",
_0: value._0
};
} else {
return await f(value._0);
}
}

let getExn = getOrThrow;

let mapWithDefault = mapOr;
Expand All @@ -367,4 +421,8 @@ exports.all3 = all3;
exports.all4 = all4;
exports.all5 = all5;
exports.all6 = all6;
exports.mapOkAsync = mapOkAsync;
exports.mapErrorAsync = mapErrorAsync;
exports.flatMapOkAsync = flatMapOkAsync;
exports.flatMapErrorAsync = flatMapErrorAsync;
/* No side effect */
4 changes: 2 additions & 2 deletions tests/analysis_tests/tests/src/expected/Completion.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2613,13 +2613,13 @@ Path g
"kind": 12,
"tags": [1],
"detail": "(result<'a, 'b>, ~message: string=?) => 'a",
"documentation": {"kind": "markdown", "value": "Deprecated: Use 'getOrThrow' instead\n\n\n `getExn(res, ~message=?)` returns `n` if `res` is `Ok(n)`, otherwise throws an exception with the message provided, or a generic message if no message was provided.\n\n ```res example\n Result.getExn(Result.Ok(42)) == 42\n \n switch Result.getExn(Error(\"Invalid data\")) {\n | exception _ => assert(true)\n | _ => assert(false)\n }\n\n switch Result.getExn(Error(\"Invalid data\"), ~message=\"was Error!\") {\n | exception _ => assert(true) // Throws a JsError with the message \"was Error!\"\n | _ => assert(false)\n }\n ```\n"}
"documentation": {"kind": "markdown", "value": "Deprecated: Use 'getOrThrow' instead\n\n\n `getExn(res, ~message=?)` returns `n` if `res` is `Ok(n)`, otherwise throws an exception with the message provided, or a generic message if no message was provided.\n\n ```res example\n Result.getExn(Result.Ok(42)) == 42\n \n switch Result.getExn(Error(\"Invalid data\")) {\n | exception _ => true\n | _ => false\n } == true\n\n switch Result.getExn(Error(\"Invalid data\"), ~message=\"was Error!\") {\n | exception _ => true // Throws a JsError with the message \"was Error!\"\n | _ => false\n } == true\n ```\n"}
}, {
"label": "Result.getOrThrow",
"kind": 12,
"tags": [],
"detail": "(result<'a, 'b>, ~message: string=?) => 'a",
"documentation": {"kind": "markdown", "value": "\n `getOrThrow(res, ~message=?)` returns `n` if `res` is `Ok(n)`, otherwise throws an exception with the message provided, or a generic message if no message was provided.\n\n ```res example\n Result.getOrThrow(Result.Ok(42)) == 42\n \n switch Result.getOrThrow(Error(\"Invalid data\")) {\n | exception _ => assert(true)\n | _ => assert(false)\n }\n\n switch Result.getOrThrow(Error(\"Invalid data\"), ~message=\"was Error!\") {\n | exception _ => assert(true) // Throws a JsError with the message \"was Error!\"\n | _ => assert(false)\n }\n ```\n"}
"documentation": {"kind": "markdown", "value": "\n `getOrThrow(res, ~message=?)` returns `n` if `res` is `Ok(n)`, otherwise throws an exception with the message provided, or a generic message if no message was provided.\n\n ```res example\n Result.getOrThrow(Result.Ok(42)) == 42\n \n switch Result.getOrThrow(Error(\"Invalid data\")) {\n | exception _ => true\n | _ => false\n } == true\n\n switch Result.getOrThrow(Error(\"Invalid data\"), ~message=\"was Error!\") {\n | exception _ => true // Throws a JsError with the message \"was Error!\"\n | _ => false\n } == true\n ```\n"}
}, {
"label": "Result.getOr",
"kind": 12,
Expand Down
Loading