result.flow
Library for representing the Result
of a computation that may fail. Which is a type friendly alternative to handling errors than exceptions are.
Usage
A Result<x, a>
is either Ok<a>
meaning the computation succeeded with a
value, or it is an Error<x>
meaning that there was some x
failure.
type Result <x a> = | isOk:true value: a | isOk:false error:x
Actual Result<x, a>
interface is more complex as it provides full library API in form of methods as well, but actual type signature above is a good summary.
Import
All the examples above assume following import:
Construct results
Result.ok(value:a) => Result<x, a>
Funciton ok
constructs result of successful computaion:
Result // => {isOk:true, value:5}
Result.error(error:x) => Result<x, a>
Function error
constructs a failed computation:
Result // => {isOk:false, error:'Oops!'}
Result.fromMaybe(error:x, value:?a):Result<x, a>
Convert from a Maybe<a>
(which is ?a
) to a result.
const parseInt = input:string:null| { const value = Number if Number return null else return value } const readInt = input:string:ResultResult<string number> Result // => Result.ok(5) // => Result.error('Input: "a" can not be read as an Int')
P.S.: In the further examples we will make use of above defined readInt
.
Unbox results
result.isOk:boolean
You can use isOk:boolean
common member to differentiate between Ok<a>
and Error<x>
variants of Result<x,a>
and access to the corresponding properties:
const result = if resultisOk console else console
(resut:Result<x, a>).toValue(fallback:a):a
It is also possible unbox Result<x, a>
by providing a fallback:a
value in case result is a failed computation.
// => 123 // => 0
If the result is Ok<a>
it returns the value, but if the result is an Error
return a given fallback value.
Result.toValue(result:Result<x, a>, fallback:a>):a
Same API is also available as a function:
Result // => 123Result // => 0
(result:Result<x, a>).toMaybe():?a
If actual error is not needed it is also possible to covert Result<x, a>
to Maybe<a>
(More specifically undefined|null|void|a
):
// => 123 // => null
Result.toMaybe(result:Result<x, a>):?a
Same API is also available as a funciton:
Result // => 123Result // => null
Transform results
(result:Result<x, a>).map(f:(value:a) => b):Result<x, b>
Applies a function to a Result<x, a>
. If the result is Ok
underlaying value will be mapped. If the result is an Error
, the same error value will propagate through.
Result // => Result.ok(4)Result // => Result.error('bad input')
Result.map(f:(value:a) => a, result:Result<x, a>):Result<x, a>
Same API is also available as a function:
Result // => ok(4)Result // => error('bad input')
(result:Result<x,a>).format(f:(error:x) => y):Result<y, a>
It is also possible to map an error
of the result. For example, say the errors we get have too much information:
Result // => Result.error('Bad input') Result // => Result.ok(4)
Result.format(f:(error:x) => y, result:Result<x, a>):Result<y, a>
Same API is also avaiable as a funciton:
Result// => Result.error('Bad input') Result // => Result.ok(4)
(result:Result<x, a>).format(f:(error:x) => a):Result<x, a>
It is also possible to transform failed Result<x, a>
to succeeded result by mapping x
to a
:
Result // => Result.ok(Error('Bad input'))
Result.recover((error:x) => a, result:Result<x, a>):Result<x, a>
Same API is also available as a function:
Result // => Result.ok(Error('Bad Input'))
Chaining results
(result:Result<x, a>).chain(next:(value:a) => Result<x, b>):Result<x, b>
It is possible to chain a sequence of computations that may fail:
type Month = 1|2|3|4|5|6|7|8|9|10|11|12const toValidMonth = n:number:ResultResult<string Month> if n >= 1 && n <= 12 && Math === n return Result else return Result const parseMonth = input:string:Result<string Month> // => Result.ok(4) // => Result.error('Input: "a" can not be read as an Int') // => Result.error('Number 13 is not with-in 0 to 12 month range')
Result.chain(f:(value:a) => Result<x, b>, r:Result<x, a>):Result<x, b>
Same API is also available as a function:
const parseMonth = input:string:ResultResult<string Month> Result // => Result.ok(7) // => Result.error('Input: "Hi" can not be read as an Int') // => Result.error('Number: 0 is not with-in 0 to 12 month range')
(result:Result<x, a>).and(other:Result<x, b>):Result<x, b>
Sometimes you want to chain a sequence of computations, but unlike in previous example, result of next computation does not depend on result of previous one:
Result // => Result.error('late error')Result // => Result.error('early error') Result // => Result.error('early')Result // => Result.ok('diff result type')
Result.and(left:Result<x, a>, right:Result<x, b>):Result<x, b>
Same API is available through a function as well:
const ok error = ResultResult // => Result.error('late error')Result // => Result.error('early error') Result // => Result.error('early')Result // => Result.ok('diff result type')
(result:Result<x, a>).capture(f:(error:x) => Result<y, a>):Result<y, a>
It is also possible to chain a sequence of computations that may fail, such that next computation is performed when previous one fails:
const fromMonthName = month:string:Month| { } const readMonthByName = input: string:ResultResult<string Month> Result const readMonth = input:string:ResultResult<string Month> // => Result.ok(3) // => Result.ok(6) // => Result.error('Number: 17 is not with-in 0 to 12 month range & Input "17" is not a valid month name') // Result.error('Input: "Jude" can not be read as an Int & Input "Jude" is not a valid month name')
Result.capture(f:(error:x) => Result<y, a>, r:Result<x, a>):Result<y, a>
Same API is also available via function:
const readMonth = input:string:ResultResult<string Month> Result // => Result.ok(3) // => Result.ok(6) // => Result.error('Number: 17 is not with-in 0 to 12 month range & Input "17" is not a valid month name') // => Result.error('Input: "Jude" can not be read as an Int & Input "Jude" is not a valid month name')
(result:Result<x, a>).or(other:Result<y, a>):Result<y, a>
It is also possible to chain a fallback computation that is performed if original fails, but unlike example above ignoring the first error:
const ok error = Result // => Result.ok(2) // => Result.ok(3) // => Result.error('diff result type') // => Result.ok(2)
Result.or(left:Result<x, a>, right:Result<y, a>):Result<y, a>
As in all other cases same API is availabel via function as well:
const ok error = Result Result // => Result.ok(2)Result // => Result.ok(3)Result // => Result.error('diff result type')Result // => Result.ok(2)
Prior Art
This library is inspired by: