@leading-works/result
v0.0.6
Published
success or failure
Downloads
5
Readme
Result Monad Typescript Implementation
Usage
Use Result
as way to encapsulate the result of an operation that can either succeed or fail:
type Car = any // something useful
const cars<Result<Car[]>> = service.getData(); // some async function
// some middleware transformations
const processedCars: Result<Car[]> = service.toViewModel(cars)
// frontend
if (processedCars.isFailure()) {
// inform about the error
return <Error message={processedCars.getReasons()} />
} else {
return processedCars.join().map(car => <CarView car={car} />)
}
References
See:
- https://pragprog.com/titles/swdddf/domain-modeling-made-functional/
- https://adambennett.dev/2020/05/the-result-monad/
- https://csharp-functional.readthedocs.io/en/latest/result-monad.html
Raison d'être
The Result Monad shall abstract the concept of an operation that can either succeed or fail. Should the operation succeed, it returns a Success construct with the expected data. Should the operation fail, it will contain a Failure structure with a List of relevant data (e.g. a list of error messages or a list of Error objects with a Status Code, a Detailed Message and a Simple Message).
The idea is to provide a Structure that may abstract away Success and Failure, and provide a useful List of Reasons when the operation has failed. The solution shall offer developers and integrators the possibility to distinguish detailed Technical Reasons with simpler Messages for Hu-mans with an optional Status code or Error code. This possibility shall give Frontend developers the possibility to avoid leaking information on their GUI, yet give end users enough information to obtain sufficient support when needed. An application architect may centralize logs with some reconciliation IDs in that effect, or maintain a table of technical errors with a table of Error IDs for the end users.
With this idea in mind, the List of Reasons encapsulated in the Failure structure shall be generic, so that integrators can craft the Reason data structure however they like.
API
The Result Monad shall source its inspiration from the Maybe Monad and the Either Monad.
A Result Typescript Choice can either be a Success of T, or a Failure of Reason. Both structure offer the same API, as does the Just and Nothing types: they implement the same type class.
Convenience method will be added so that the Monad is easily usable in the Typescript / JavaScript world.
When other languages uses different method name (e.g. chain
, bind
, etc.), we will alias these names so that developers may use the semantics they are comfortable with.
In order to be a Monad, the Result Monad will implement the Functor type class, the Applicative type class and the Monad type class.
Pointed Functor type class
In order to be a Pointed Functor, the construct must implement of(...)
and map(...)
, where:
of :: (a) -> f a
fmap :: (a -> b) -> f a -> f b
Laws
It must also honour theses laws:
- the identity law:
fmap id = id
; - the associativity law:
fmap (g . f) = fmap g . fmap f
.
Applicative Functor type class
In order to be an Applicative Functor, the construct must implement ap(...)
, where:
pure :: a -> f a
ap :: f (a -> b) -> f a -> f b
Laws
It must also honour theses laws:
- the identity law:
pure id <*> v = v
; - the homomorphism law:
pure f <*> pure x = pure (f x)
; - the interchange law:
u <*> pure y = pure ($ y) <*> u
- the composition law:
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
.
Every Applicative Functor is also a Functor, map
/fmap
can be written in terms of ap
:
fmap g x = pure g <*> x
Monad type class
In order to be a Monad, the construct must implement return(...)
and chain(...)(aka
bind(...)`), where:
return :: a -> m a
chain :: m a -> (a -> m b) -> m b
Laws
It must also honour these laws:
- the left identity law:
unit(x) >>= f === f(x)
; - the right identity law:
ma >>= unit === ma
; - the associativity law:
ma >>= λx -> (f(x) >>= g) === (ma >>= f) >>= g
.