From b975fa23d787287863ffe6f651a4a87bc804b90e Mon Sep 17 00:00:00 2001 From: Dale Francis Date: Fri, 7 Dec 2018 15:22:05 -0800 Subject: [PATCH 1/9] Initial commit for Result documentation --- docs/src/pages/docs/crocks/Result.md | 177 +++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 docs/src/pages/docs/crocks/Result.md diff --git a/docs/src/pages/docs/crocks/Result.md b/docs/src/pages/docs/crocks/Result.md new file mode 100644 index 000000000..c3a5ce78d --- /dev/null +++ b/docs/src/pages/docs/crocks/Result.md @@ -0,0 +1,177 @@ +--- +title: "Result" +description: "Result Crock" +layout: "guide" +functions: ["trycatch", "eithertoresult", "firsttoresult", "lasttoresult", "maybetoresult"] +weight: 90 +--- + +```haskell +Result e a = Err e | Ok a +``` + +Result is a Sum Type a step above [`Result`][maybe]. With a [`Result`][maybe] the +left side contains no information, where as with a `Result` the left contains +the error information from an operation. `Result` is well suited for capturing +disjunction when the cause of the "error" case needs to be communicated. For +example, when executing a function and you exception is important or useful. + +A `Result` represents disjunction by using two constructors, [`Err`](#err) or [`Ok`](#ok). +An [`Ok`](#ok) instance represents the positive result while [`Err`](#err) is considered +the negative. With the exception of [`coalesce`](#coalesce), [`swap`](#swap) +and [`bimap`](#bimap), all `Result` returning methods on an instance will be +applied to a [`Ok`](#ok) returning the result. If an instance is a [`Err`](#err), then all application is skipped and another [`Err`](#err) is returned. + +It is recommended to use the available [`Ok`](#ok) and [`Err`](#err) +constructors to construct `Result` instances in most cases. You can use the +`Result` constructor to construct a [`Ok`](#ok), but it will read better to just use +`Ok`. + +```javascript +``` + +
+ +## Implements +`Setoid`, `Semigroup`, `Functor`, `Alt`, `Apply`, `Traversable`, +`Chain`, `Applicative`, `Monad` + +
+ +
+ +## Construction + +```haskell +Result :: a -> Result e a +``` + +Most of the time, `Result` is constructed using functions of your own making +and helper functions like [`tryCatch`](#trycatch) or by employing one of the +instance constructors, [`Ok`](#ok) or [`Err`](#err). This is due to the nature +of `Result` and most other Sum Types. + +As a matter of consistency and completion, a `Result` instance can also be +constructed using its TypeRep like any other type. The `Result` constructor is a +unary function that accepts any type `a` and returns a [`Ok`](#ok) instance, wrapping +the value passed to its argument. + +```javascript +import Result from 'crocks/Result' +import equals from 'crocks/pointfree/equals' + +const { Ok, Err, of } = Result + +Result('some string') +//=> Ok "some string" + +Result(null) +//=> Ok null + +Result(undefined) +//=> Ok undefined + +of('some string') +//=> Ok "some string" + +of(null) +//=> Ok null + +of(undefined) +//=> Ok undefined + +Ok('some string') +//=> Ok "some string" + +Ok(null) +//=> Ok null + +Ok(undefined) +//=> Ok undefined +//=> Ok undefined + +Err('some string') +//=> Err "some string" + +Err(null) +//=> Err null + +Err(undefined) +//=> Err undefined + +equals( + Result.Ok([ 1, 2, 3 ]), + Result.of([ 1, 2, 3 ]) +) +//=> true + +equals( + of({ a: 100 }), + Result({ a: 100 }) +) +//=> true +``` + +
+ +
+ +## Constructor Methods + +#### Err + +```haskell +Result.Err :: e -> Result e a +``` + +Used to construct an [`Err`](#err) instance that represents the "false" or +"Negative" portion of a disjunction. When an instance is an [`Err`](#err), most +`Result` returning methods will just return another [`Err`](#err). + +```javascript +import Result from 'crocks/Result' + +import chain from 'crocks/pointfree/chain' +import isNumber from 'crocks/predicates/isNumber' +import ifElse from 'crocks/logic/ifElse' + +const { Ok, Err } = Result + +// buildError :: String -> String +const buildError = x => Err(`${x} is not a valid number`) + +// add10 :: Number -> Number +const add10 = + x => x + 10 + +// protectedAdd10 :: a -> Result String Number +const protectedAdd10 = + ifElse(isNumber, x => Ok(add10(x)), buildError) + +Ok(23) + .map(add10) +//=> Ok 33 + +Err(23) + .map(add10) +//=> Err + +chain(protectedAdd10, Ok(10)) +//=> Ok 20 + +chain(protectedAdd10, Err('d')) +//=> Err "d is not a valid number" +``` + +#### Ok + +```haskell +Result.Ok :: a -> Result e a +``` + +Used to construct a [`Ok`](#ok) instance that represents the "true" portion of a +disjunction or a valid value. [`Ok`](#ok) will wrap any given value in +a [`Ok`](#ok), signaling the validity of the wrapped value. + +```javascript +``` \ No newline at end of file From ab8b68680701c5965b53fc5363ea38191233ae7d Mon Sep 17 00:00:00 2001 From: Dale Francis Date: Tue, 11 Dec 2018 16:38:50 -0800 Subject: [PATCH 2/9] More scaffolding work --- docs/src/pages/docs/crocks/Result.md | 426 ++++++++++++++++++++++++++- 1 file changed, 421 insertions(+), 5 deletions(-) diff --git a/docs/src/pages/docs/crocks/Result.md b/docs/src/pages/docs/crocks/Result.md index c3a5ce78d..852d2a949 100644 --- a/docs/src/pages/docs/crocks/Result.md +++ b/docs/src/pages/docs/crocks/Result.md @@ -3,14 +3,14 @@ title: "Result" description: "Result Crock" layout: "guide" functions: ["trycatch", "eithertoresult", "firsttoresult", "lasttoresult", "maybetoresult"] -weight: 90 +weight: 130 --- ```haskell Result e a = Err e | Ok a ``` -Result is a Sum Type a step above [`Result`][maybe]. With a [`Result`][maybe] the +Result is a Sum Type a step above `Result`. With a `Result` the left side contains no information, where as with a `Result` the left contains the error information from an operation. `Result` is well suited for capturing disjunction when the cause of the "error" case needs to be communicated. For @@ -125,8 +125,8 @@ Result.Err :: e -> Result e a ``` Used to construct an [`Err`](#err) instance that represents the "false" or -"Negative" portion of a disjunction. When an instance is an [`Err`](#err), most -`Result` returning methods will just return another [`Err`](#err). +"Negative" portion of a disjunction. When an instance is an [`Err`](#err), most + `Result` returning methods will just return another [`Err`](#err). ```javascript import Result from 'crocks/Result' @@ -174,4 +174,420 @@ disjunction or a valid value. [`Ok`](#ok) will wrap any given value in a [`Ok`](#ok), signaling the validity of the wrapped value. ```javascript -``` \ No newline at end of file +import Result from 'crocks/Result' + +import compose from 'crocks/helpers/compose' +import ifElse from 'crocks/logic/ifElse' +import isString from 'crocks/predicates/isString' +import map from 'crocks/pointfree/map' + +const { Ok, Err } = Result + +// buildError :: String -> String +const buildError = x => Err(`${x} is not a valid string`) + +// toUpper :: String -> String +const toUpper = + x => x.toUpperCase() + +const ensureString = + ifElse(isString, Ok, buildError) + +Ok(32) +//=> Ok 32 + +// safeShout :: a -> Result String String +const safeShout = compose( + map(toUpper), + ensureString +) + +safeShout(45) +//=> Err "45 is not a valid string" + +safeShout('Hey there!') +//=> Ok "HEY THERE!" +``` + +#### of + +```haskell +Result.of :: a -> Result e a +``` + +Used to wrap any value into a `Result` as a [`Ok`](#ok), `of` is used mostly by +helper functions that work "generically" with instances of +either `Applicative` or `Monad`. When working specifically with +the `Result` type, the [`Ok`](#ok) constructor should be used. Reach +for `of` when working with functions that will work with +ANY `Applicative`/`Monad`. + +```javascript +``` + +
+ +
+ +## Instance Methods + +#### equals + +```haskell +Result e a ~> b -> Boolean +``` + +Used to compare the underlying values of two `Result` instances for equality by +value, `equals` takes any given argument and returns `true` if the passed +arguments is a `Result` with an underlying value equal to the underlying value +of the `Result` the method is being called on. If the passed argument is not +a `Result` or the underlying values are not equal, `equals` will return `false`. + +```javascript +``` + +#### concat + +```haskell +Semigroup s => Result e s ~> Result e s -> Result e s +``` + +When an underlying value of a given `Result` is fixed to a `Semigroup`, `concat` +can be used to concat another `Result` instance with an underlying `Semigroup` +of the same type. Expecting a `Result` wrapping a `Semigroup` of the same type, +`concat` will give back a new `Result` instance wrapping the result of combining +the two underlying `Semigroup`s. When called on a [`Err`](#err) instance, `concat` +will return an [`Err`](#err) and attempt to concat the errors. + +```javascript +``` + +#### map + +```haskell +Maybe a ~> (a -> b) -> Maybe b +``` + +Used to apply transformations to values in the safety of a `Result`, `map` takes +a function that it will lift into the context of the `Result` and apply to it +the wrapped value. When ran on a [`Ok`](#ok) instance, `map` will apply the wrapped +value to the provided function and return the result in a new [`Ok`](#ok) instance. + +```javascript +``` + +#### alt + +```haskell +Maybe a ~> Maybe a -> Maybe a +``` + +Providing a means for a fallback or alternative value, `alt` combines two +`Result` instances and will return the first [`Ok`](#ok) it encounters or [`Err`](#err) +if it does not have a [`Ok`](#ok). This can be used in conjunction with +[`zero`](#zero) to return the first valid value in contained in a `Foldable` +structure. + +```javascript +``` + +#### ap + +```haskell +Maybe (a -> b) ~> Maybe a -> Maybe b +``` + +Short for apply, `ap` is used to apply a `Result` instance containing a value +to another `Result` instance that contains a function, resulting in new `Result` +instance with the result. `ap` requires that it is called on an `instance` that +is either a [`Err`](#err) or a [`Ok`](#ok) that wraps a curried polyadic function. + +When either `Result` is a [`Err`](#err), `ap` will return a [`Err`](#err). This can be +used to safely combine multiple values under a given combination function. If +any of the inputs results in a [`Err`](#err) than they will never be applied to +the function and not provide exceptions or unexpected results. + +```javascript +``` + +#### sequence + +```haskell +Apply f => Maybe (f a) ~> (b -> f b) -> f (Maybe a) +Applicative f => Maybe (f a) ~> TypeRep f -> f (Maybe a) +``` + +When an instance of `Result` wraps an `Apply` instance, `sequence` can be used to +swap the type sequence. `sequence` requires either an `Applicative TypeRep` or +an `Apply` returning function is provided for its argument. This will be used in +the case that the `Result` instance is a [`Err`](#err). + +`sequence` can be derived from [`traverse`](#traverse) by passing it an +`identity` function (`x => x`). + +```javascript +``` + +#### traverse + +```haskell +Apply f => Maybe a ~> (c -> f c), (a -> f b)) -> f Maybe b +Applicative f => Maybe a ~> (TypeRep f, (a -> f b)) -> f Maybe b +``` + +Used to apply the "effect" of an `Apply` to a value inside of a `Result`, +`traverse` combines both the "effects" of the `Apply` and the `Result` by +returning a new instance of the `Apply`, wrapping the result of the +`Apply`s "effect" on the value in the `Result`. + +`traverse` requires either an `Applicative TypeRep` or an `Apply` returning +function as its first argument and a function that is used to apply the "effect" +of the target `Apply` to the value inside of the `Result`. This will be used in +the case that the `Result` instance is a [`Err`](#err). Both arguments must provide +an instance of the target `Apply`. + +```javascript +``` + +#### chain + +```haskell +Maybe a ~> (a -> Maybe b) -> Maybe b +``` + +Combining a sequential series of transformations that capture disjunction can be +accomplished with `chain`. `chain` expects a unary, `Result` returning function +as its argument. When invoked on a [`Err`](#err), `chain` will not run the function, +but will instead return another [`Err`](#err). When called on a [`Ok`](#ok) however, the +inner value will be passed to provided function, returning the result as the +new instance. + +```javascript +``` + +#### coalesce + +```haskell +Maybe a ~> ((() -> b), (a -> b))) -> Maybe b +``` + +When one would like to [`option`](#option) a `Result` but would like to remain +within a `Result` type, `coalesce` can be used. `coalesce` expects two functions +for it's inputs. + +The first function is used when invoked on a [`Err`](#err) and will return a [`Ok`](#ok) +instance wrapping the result of the function. The second function is used when +`coalesce` is invoked on a [`Ok`](#ok) and is used to map the original value, +returning a new [`Ok`](#ok) instance wrapping the result of the second function. + +```javascript +``` + +#### option + +```haskell +Maybe a ~> a -> a +``` + +Used as the primary way to "fold" a value out of a `Result`, `option` expects a +default value. The default value provided will be returned when `option` is +invoked on a [`Err`](#err) instance. When invoked on a [`Ok`](#ok), the underlying value +is returned, discarding the provided default value. `option` is typically ran +at the "edge" of a flow, to provide default values for complicated +representations of disjunction. + +When the need to immediately map the result of optioning a `Result` arises, +then [`either`](#either) may be employed to combine it in one operation. + +```javascript +``` + +#### either + +```haskell +Maybe a ~> ((() -> b), (a -> b)) -> b +``` + +Used to provide a means to map a given `Result` instance while optioning out the +wrapped value. [`option`](#option) can handle most cases for optioning `Result`, +but does not provide a means to map a given value at the time of +optioning. `either` expects two functions as its arguments. The first is a +pointed function that will be used when invoked on a [`Err`](#err). While the second +will map the value wrapped in a given [`Ok`](#ok) and return the result of that +mapping. + + +```javascript +``` + +
+ +
+ +## Helper Functions + + +#### TryCatch + +`crocks/Result/tryCatch` + +```haskell +TryCatch :: (a -> b) -> a -> Result e a +``` + +```javascript +``` +
+ +
+ +## Transformation Functions + +#### eitherToResult + +`crocks/Result/eitherToResult` + +```haskell +eitherToResult :: Either b a -> Result b a +eitherToResult :: (a -> Either c b) -> a -> Result c b +``` + +Used to transform a given [`Either`][either] instance to a `Result` +instance or flatten a `Result` of `Either` into a `Result` when chained, +`eitherToMaybe` will turn a [`Right`][right] instance into +a [`Ok`](#ok) wrapping the original value contained in the [`Right`][right]. +All [`Left`][left] instances will map to a [`Err`](#err), mapping the +originally contained value to a `Unit`. Values on the [`Left`][left] will be +lost and as such this transformation is considered lossy in that regard. + +Like all `crocks` transformation functions, `eitherToMaybe` has two possible +signatures and will behave differently when passed either +an [`Either`][either] instance or a function that returns an instance +of [`Either`][either]. When passed the instance, a transformed `Result` is +returned. When passed an [`Either`][either] returning function, a function will +be returned that takes a given value and returns a `Result`. + +```javascript +``` + +#### firstToResult + +`crocks/Result/firstToResult` + +```haskell +firstToResult :: e -> First a -> Result e a +firstToResult :: e -> (a -> First b) -> a -> Result e b +``` + +Used to transform a given [`First`][first] instance to a `Result` +instance or flatten a `Result` of [`First`][first] into a `Result` when chained, +`firstToMaybe` will turn a non-empty instance into an [`Ok`](#ok) wrapping +the original value contained within the [`First`][first]. All empty instances +will map to an [`Err`](#err) with the given value. + +Like all `crocks` transformation functions, `firstToMaybe` has two possible +signatures and will behave differently when passed either +a [`First`][first] instance or a function that returns an instance +of [`First`][first]. When passed the instance, a transformed `Result` is +returned. When passed a [`First`][first] returning function, a function will be +returned that takes a given value and returns a `Result`. + +```javascript +import First from 'crocks/First' + +import firstToResult from 'crocks/Result/firstToResult' +import flip from 'crocks/combinators/flip' +import prop from 'crocks/Maybe/prop' +import mapReduce from 'crocks/helpers/mapReduce' +import compose from 'crocks/helpers/compose' +import concat from 'crocks/pointfree/concat' + +const { empty } = First + +// Person :: (String, Number) + +// createPerson :: (String, Number) -> Person +const createPerson = (name, age) => ({ + name, age +}) + +// liftName :: Person -> First (Maybe String) +const liftName = compose(First, prop('name')) + +// mergeFirstName :: [Person] -> First (Maybe String) +const mergeFirstName = compose(firstToResult('(No name found)'), mapReduce(liftName, flip(concat), empty())) + +mergeFirstName([ + createPerson('John', 30), + createPerson('Jon', 33) +]) +//=> Ok "Jon" + +mergeFirstName([ + createPerson(undefined, 30), + createPerson(undefined, 33) +]) +//=> Err "(No name found)" +``` + +#### lastToResult + +`crocks/Result/lastToResult` + +```haskell +lastToResult :: e -> Last a -> Result e a +lastToResult :: e -> (a -> Last b) -> a -> Result e b +``` + +Used to transform a given [`Last`][last] instance to a `Result` instance or +flatten a `Result` of [`Last`][last] into a `Result` when chained, `lastToResult` will +turn a non-empty instance into a [`Ok`](#ok) wrapping the original value +contained within the [`Last`][last]. All empty instances will map to a +[`Err`](#err). + +Like all `crocks` transformation functions, `lastToResult` has two possible +signatures and will behave differently when passed either +a [`Last`][last] instance or a function that returns an instance +of [`Last`][last]. When passed the instance, a transformed `Result` is returned. +When passed a [`Last`][last] returning function, a function will be returned +that takes a given value and returns a `Result`. + +```javascript +``` + +#### maybeToResult + +`crocks/Result/maybeToResult` + +```haskell +maybeToResult :: e -> Maybe a -> Result e a +maybeToResult :: e -> (a -> Maybe b) -> a -> Result e b +``` + +Used to transform a given [`Maybe`][maybe] instance to a `Result` +instance or flatten a `Result` of [`Maybe`][maybe] into a `Result` when chained, +`maybeToResult` will turn an [`Just`][just] instance into a [`Ok`](#ok) wrapping the +original value contained in the [`Just`][just]. +All [`Err`](#err) instances will map to a [`Err`](#err), mapping the originally +contained value to a `Unit`. Values on the [`Err`](#err) will be lost and as such this +transformation is considered lossy in that regard. + +Like all `crocks` transformation functions, `maybeToResult` has two possible +signatures and will behave differently when passed either an `Result` instance +or a function that returns an instance of `Result`. When passed the instance, +a transformed `Result` is returned. When passed a `Result` returning function, +a function will be returned that takes a given value and returns a `Result`. + +```javascript +``` +
+ +[pred]: ./Pred.html +[either]: ./Either.html +[left]: ./Either.html#left +[right]: ./Either.html#right +[first]: ../monoids/First.html +[last]: ../monoids/Last.html +[maybe]: ../monoids/Maybe.html +[just]: ./Maybe.html#just +[nothing]: ./Maybe.html#nothing From 5d2e453a6d14d353c372d5b9ef372ef5a870ee0c Mon Sep 17 00:00:00 2001 From: Dale Francis Date: Thu, 13 Dec 2018 00:44:18 -0800 Subject: [PATCH 3/9] Updated signatures, added bi-map, updated either description --- docs/src/pages/docs/crocks/Result.md | 51 +++++++++++++--------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/docs/src/pages/docs/crocks/Result.md b/docs/src/pages/docs/crocks/Result.md index 852d2a949..8dd0b1713 100644 --- a/docs/src/pages/docs/crocks/Result.md +++ b/docs/src/pages/docs/crocks/Result.md @@ -246,6 +246,15 @@ a `Result` or the underlying values are not equal, `equals` will return `false`. ```javascript ``` +#### bimap + +```haskell +Semigroup s => Result e s ~> Result e s -> Result e s +``` + +```javascript +``` + #### concat ```haskell @@ -265,12 +274,12 @@ will return an [`Err`](#err) and attempt to concat the errors. #### map ```haskell -Maybe a ~> (a -> b) -> Maybe b +Result e a ~> (a -> b) -> Result e b ``` Used to apply transformations to values in the safety of a `Result`, `map` takes a function that it will lift into the context of the `Result` and apply to it -the wrapped value. When ran on a [`Ok`](#ok) instance, `map` will apply the wrapped +the wrapped value. When ran on an [`Ok`](#ok) instance, `map` will apply the wrapped value to the provided function and return the result in a new [`Ok`](#ok) instance. ```javascript @@ -279,7 +288,7 @@ value to the provided function and return the result in a new [`Ok`](#ok) instan #### alt ```haskell -Maybe a ~> Maybe a -> Maybe a +Result e a ~> Result e a -> Result e a ``` Providing a means for a fallback or alternative value, `alt` combines two @@ -294,7 +303,7 @@ structure. #### ap ```haskell -Maybe (a -> b) ~> Maybe a -> Maybe b +Result e (a -> b) ~> Result e a -> Result e b ``` Short for apply, `ap` is used to apply a `Result` instance containing a value @@ -313,8 +322,8 @@ the function and not provide exceptions or unexpected results. #### sequence ```haskell -Apply f => Maybe (f a) ~> (b -> f b) -> f (Maybe a) -Applicative f => Maybe (f a) ~> TypeRep f -> f (Maybe a) +Apply f => Result e (f a) ~> (b -> f b) -> f (Result e a) +Applicative f => Result e (f a) ~> TypeRep f -> f (Result e a) ``` When an instance of `Result` wraps an `Apply` instance, `sequence` can be used to @@ -331,8 +340,8 @@ the case that the `Result` instance is a [`Err`](#err). #### traverse ```haskell -Apply f => Maybe a ~> (c -> f c), (a -> f b)) -> f Maybe b -Applicative f => Maybe a ~> (TypeRep f, (a -> f b)) -> f Maybe b +Apply f => Result e a ~> (c -> f c), (a -> f b)) -> f Result e b +Applicative f => Result e a ~> (TypeRep f, (a -> f b)) -> f Result e b ``` Used to apply the "effect" of an `Apply` to a value inside of a `Result`, @@ -352,7 +361,7 @@ an instance of the target `Apply`. #### chain ```haskell -Maybe a ~> (a -> Maybe b) -> Maybe b +Result e a ~> (a -> Result e b) -> Result e b ``` Combining a sequential series of transformations that capture disjunction can be @@ -368,7 +377,7 @@ new instance. #### coalesce ```haskell -Maybe a ~> ((() -> b), (a -> b))) -> Maybe b +Result e a ~> ((e -> b), (a -> b))) -> Result c b ``` When one would like to [`option`](#option) a `Result` but would like to remain @@ -383,36 +392,24 @@ returning a new [`Ok`](#ok) instance wrapping the result of the second function. ```javascript ``` -#### option +#### swap ```haskell -Maybe a ~> a -> a +Result e a ~> ((e -> a), (a -> e)) -> Result e a ``` -Used as the primary way to "fold" a value out of a `Result`, `option` expects a -default value. The default value provided will be returned when `option` is -invoked on a [`Err`](#err) instance. When invoked on a [`Ok`](#ok), the underlying value -is returned, discarding the provided default value. `option` is typically ran -at the "edge" of a flow, to provide default values for complicated -representations of disjunction. - -When the need to immediately map the result of optioning a `Result` arises, -then [`either`](#either) may be employed to combine it in one operation. - ```javascript ``` #### either ```haskell -Maybe a ~> ((() -> b), (a -> b)) -> b +Result e a ~> ((e -> b), (a -> b)) -> b ``` Used to provide a means to map a given `Result` instance while optioning out the -wrapped value. [`option`](#option) can handle most cases for optioning `Result`, -but does not provide a means to map a given value at the time of -optioning. `either` expects two functions as its arguments. The first is a -pointed function that will be used when invoked on a [`Err`](#err). While the second +wrapped value. `either` expects two functions as its arguments. The first is a +function that will be used to map an [`Err`](#err). While the second will map the value wrapped in a given [`Ok`](#ok) and return the result of that mapping. From 082ba5f590c8b13ec3e93af2f6af65daa5762076 Mon Sep 17 00:00:00 2001 From: Dale Francis Date: Thu, 13 Dec 2018 22:54:28 -0800 Subject: [PATCH 4/9] Updated Of and Equals with examples --- docs/src/pages/docs/crocks/Result.md | 92 +++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 17 deletions(-) diff --git a/docs/src/pages/docs/crocks/Result.md b/docs/src/pages/docs/crocks/Result.md index 8dd0b1713..5c372f1c9 100644 --- a/docs/src/pages/docs/crocks/Result.md +++ b/docs/src/pages/docs/crocks/Result.md @@ -215,14 +215,34 @@ safeShout('Hey there!') Result.of :: a -> Result e a ``` -Used to wrap any value into a `Result` as a [`Ok`](#ok), `of` is used mostly by -helper functions that work "generically" with instances of -either `Applicative` or `Monad`. When working specifically with -the `Result` type, the [`Ok`](#ok) constructor should be used. Reach -for `of` when working with functions that will work with -ANY `Applicative`/`Monad`. +Used to wrap any value into a `Result` as an [`Ok`](#ok), `of` is used mostly +by helper functions that work "generically" with instances of either +`Applicative` or `Monad`. When working specifically with the `Result` type, the +[`Ok`](#ok) constructor should be used. Reach for `of` when working with +functions that will work with ANY `Applicative`/`Monad`. ```javascript +import Result from 'crocks/Result' + +const { Ok, of } = Result + +of('Some result!') +//=> Ok "Some result!" + +of(undefined) +//=> Ok undefined + +Ok('Some result!') +//=> Ok "Some result!" + +Ok(undefined) +//=> Ok undefined + +Result('Some result!') +//=> Ok "Some result!" + +Result(undefined) +//=> Ok undefined ``` @@ -238,21 +258,39 @@ Result e a ~> b -> Boolean ``` Used to compare the underlying values of two `Result` instances for equality by -value, `equals` takes any given argument and returns `true` if the passed -arguments is a `Result` with an underlying value equal to the underlying value -of the `Result` the method is being called on. If the passed argument is not -a `Result` or the underlying values are not equal, `equals` will return `false`. +value. `equals` takes any given argument and returns `true` if the passed +arguments is a `Result` ([`Ok`](#ok) or [`Err`](#err)) with an underlying value +equal to the underlying value of the `Result` the method is being called on. If +the passed argument is not a `Result` or the underlying values are not equal, +`equals` will return `false`. ```javascript -``` +import Result from 'crocks/Result' -#### bimap +import equals from 'crocks/pointfree/equals' -```haskell -Semigroup s => Result e s ~> Result e s -> Result e s -``` +const { Ok, Err } = Result + +Ok('result') + .equals(Ok('result')) +//=> true + +Ok(null) + .equals(Ok(null)) +//=> true + +Ok('error') + .equals(Err('error')) +//=> false + +// by value, not reference for most types +Ok([ 1, { a: 2 }, 'string' ]) + .equals(Ok([ 1, { a: 2 }, 'string' ])) +//=> true + +equals(Ok('result'), 'result') +//=> false -```javascript ``` #### concat @@ -300,6 +338,26 @@ structure. ```javascript ``` +#### bimap + +```haskell +Result e a ~> ((e -> d), (a -> b)) -> Result d b +``` + +While it's more common to only need to [`map`](#map) over a `Result` that's an +[`Ok`](#ok) there comes a time when you need to map over a `Result` regardless +of wether it's an [`Ok`](#ok) or an [`Err`](#err). + +`bimap` takes two mapping functions as its arguments. The first function is +used to map a [`Err`](#err) instance, while the second maps a [`Ok`](#ok). +`Result` only provides a means to map an [`Ok`](#ok) instance exclusively using +[`map`](#map). If the need arises to map a [`Err`](#err) instance exclusively, +then `bimap` can be used, passing the mapping function to the first argument +and an [`identity`][identity] to the second. + +```javascript +``` + #### ap ```haskell @@ -429,7 +487,7 @@ mapping. `crocks/Result/tryCatch` ```haskell -TryCatch :: (a -> b) -> a -> Result e a +TryCatch :: (* -> a) -> * -> Result e a ``` ```javascript From e2c7548211ba3ffe493262abd2491bb77df97363 Mon Sep 17 00:00:00 2001 From: Dale Francis Date: Sun, 16 Dec 2018 00:00:27 -0800 Subject: [PATCH 5/9] Added concat, map and alt examples, updated descriptions --- docs/src/pages/docs/crocks/Result.md | 121 +++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 5 deletions(-) diff --git a/docs/src/pages/docs/crocks/Result.md b/docs/src/pages/docs/crocks/Result.md index 5c372f1c9..de8e8d579 100644 --- a/docs/src/pages/docs/crocks/Result.md +++ b/docs/src/pages/docs/crocks/Result.md @@ -304,9 +304,31 @@ can be used to concat another `Result` instance with an underlying `Semigroup` of the same type. Expecting a `Result` wrapping a `Semigroup` of the same type, `concat` will give back a new `Result` instance wrapping the result of combining the two underlying `Semigroup`s. When called on a [`Err`](#err) instance, `concat` -will return an [`Err`](#err) and attempt to concat the errors. +will return an [`Err`](#err) will return the first [`Err`](#err) in the chain. ```javascript +import Result from 'crocks/Result' + +import concat from 'crocks/pointfree/concat' + +const { Ok, Err } = Result + +Ok([ 1, 2, 3 ]) + .concat(Ok([ 4, 5, 6 ])) +//=> [ 1, 2, 3, 4, 5, 6 ] + +Ok([ 1, 2, 3 ]) + .concat(Err([ 4, 5, 6 ])) +//=> Err [ 4, 5, 6 ] + +concat(Ok('Result'), Err('Error')) +//=> Err "Error" + +concat(Err('Error'), Ok('Result')) +//=> Err "Error" + +concat(Err('Error 1'), Err('Error 2')) +//=> Err "Error 1" ``` #### map @@ -317,10 +339,62 @@ Result e a ~> (a -> b) -> Result e b Used to apply transformations to values in the safety of a `Result`, `map` takes a function that it will lift into the context of the `Result` and apply to it -the wrapped value. When ran on an [`Ok`](#ok) instance, `map` will apply the wrapped +the wrapped value. When run on an [`Ok`](#ok) instance, `map` will apply the wrapped value to the provided function and return the result in a new [`Ok`](#ok) instance. +When run on an ['Err'](#err) instance `map` with return the error value in a new +[`Err`](#err) instance. ```javascript +import Result from 'crocks/Result' + +import map from 'crocks/pointfree/map' +import merge from 'crocks/pointfree/merge' +import assign from 'crocks/helpers/assign' +import objOf from 'crocks/helpers/objOf' +import fanout from 'crocks/Pair/fanout' +import isNumber from 'crocks/predicates/isNumber' +import ifElse from 'crocks/logic/ifElse' +import compose from 'crocks/core/compose' + +const { Ok, Err } = Result + +// buildError :: String -> String +const buildError = x => Err(`${x} is not a valid string`) + +const prod = + x => y => x * y + +const fromNumber = + ifElse(isNumber, Ok, buildError) + +const double = + compose( + map(prod(2)), + fromNumber + ) + +double(21) +//=> Ok 42 + +double('number') +//=> Err "number is not a valid string" + +const getParity = compose( + objOf('parity'), + n => n % 2 === 0 ? 'Even' : 'Odd' +) + +const getInfo = compose( + map(merge(assign)), + map(fanout(objOf('value'), getParity)), + fromNumber +) + +getInfo(5324) +//=> Ok { parity: "Even", value: 5324 } + +getInfo('number') +//=> Err "number is not a valid string" ``` #### alt @@ -331,11 +405,48 @@ Result e a ~> Result e a -> Result e a Providing a means for a fallback or alternative value, `alt` combines two `Result` instances and will return the first [`Ok`](#ok) it encounters or [`Err`](#err) -if it does not have a [`Ok`](#ok). This can be used in conjunction with -[`zero`](#zero) to return the first valid value in contained in a `Foldable` -structure. +if it does not have an [`Ok`](#ok). ```javascript +import Result from 'crocks/result' + +import alt from 'crocks/pointfree/alt' +import map from 'crocks/pointfree/map' +import reduce from 'crocks/pointfree/reduce' +import flip from 'crocks/combinators/flip' +import ifElse from 'crocks/logic/ifElse' +import compose from 'crocks/core/compose' +import curry from 'crocks/core/curry' + +const { Ok, Err } = Result + +const check = + pred => ifElse(pred, Ok, Err) + +// gte :: Number -> Number -> Boolean +const gte = + x => y => y >= x + +const find = + curry(pred => compose(reduce(flip(alt), Err('Not found')), map(check(pred)))) + +Err('Error') + .alt(Ok('Result')) +//=> Ok "Result" + +Ok('Result') + .alt(Err('Error')) +//=> Ok "Result" + +Err('Error 1') + .alt(Err('Error 2')) +//=> Ok "Error 1" + +find(gte(41), [ 17, 25, 38, 42 ]) +//=> Ok 42 + +find(gte(11), [ 1, 2, 3, 4 ]) +//=> Err "Not found" ``` #### bimap From 8cd34627100008add1a70762cdfb636897a49c4e Mon Sep 17 00:00:00 2001 From: Dale Francis Date: Mon, 17 Dec 2018 00:34:00 -0800 Subject: [PATCH 6/9] Added in bimap and ap examples --- docs/src/pages/docs/crocks/Result.md | 96 ++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 6 deletions(-) diff --git a/docs/src/pages/docs/crocks/Result.md b/docs/src/pages/docs/crocks/Result.md index de8e8d579..0def70dc6 100644 --- a/docs/src/pages/docs/crocks/Result.md +++ b/docs/src/pages/docs/crocks/Result.md @@ -359,7 +359,7 @@ import compose from 'crocks/core/compose' const { Ok, Err } = Result // buildError :: String -> String -const buildError = x => Err(`${x} is not a valid string`) +const buildError = x => Err(`${x} is not a valid number`) const prod = x => y => x * y @@ -376,8 +376,8 @@ const double = double(21) //=> Ok 42 -double('number') -//=> Err "number is not a valid string" +double('down') +//=> Err "down is not a valid number" const getParity = compose( objOf('parity'), @@ -393,8 +393,8 @@ const getInfo = compose( getInfo(5324) //=> Ok { parity: "Even", value: 5324 } -getInfo('number') -//=> Err "number is not a valid string" +getInfo('down') +//=> Err "down is not a valid number" ``` #### alt @@ -455,7 +455,7 @@ find(gte(11), [ 1, 2, 3, 4 ]) Result e a ~> ((e -> d), (a -> b)) -> Result d b ``` -While it's more common to only need to [`map`](#map) over a `Result` that's an +While it's more common to [`map`](#map) over a `Result` that's an [`Ok`](#ok) there comes a time when you need to map over a `Result` regardless of wether it's an [`Ok`](#ok) or an [`Err`](#err). @@ -467,6 +467,52 @@ then `bimap` can be used, passing the mapping function to the first argument and an [`identity`][identity] to the second. ```javascript +import Result from 'crocks/Result' + +import map from 'crocks/pointfree/map' +import ifElse from 'crocks/logic/ifElse' +import setProp from 'crocks/helpers/setProp' +import compose from 'crocks/core/compose' +import objOf from 'crocks/helpers/objOf' +import isNumber from 'crocks/predicates/isNumber' +import bimap from 'crocks/pointfree/bimap' + +const { Ok, Err } = Result + +// buildError :: String -> String +const buildError = x => Err(`${x} is not a valid number`) + +const prod = + x => y => x * y + +const fromNumber = + ifElse(isNumber, Ok, buildError) + +// hasError :: Boolean -> Object -> Object +const hasError = + setProp('hasError') + +// buildResult :: (String, Boolean) -> a -> Object +const buildResult = (key, isError) => + compose(hasError(isError), objOf(key)) + +const finalize = + bimap( + buildResult('error', true), + buildResult('result', false) + ) + +const double = + compose( + map(prod(2)), + fromNumber + ) + +finalize(double(21)) +//=> Ok { result: 42, hasError: false } + +finalize(double('unk')) +//=> Err { error: "unk is not a valid number", hasError: true } ``` #### ap @@ -486,6 +532,44 @@ any of the inputs results in a [`Err`](#err) than they will never be applied to the function and not provide exceptions or unexpected results. ```javascript +import Result from 'crocks/Result' + +import map from 'crocks/pointfree/map' +import ifElse from 'crocks/logic/ifElse' +import isNumber from 'crocks/predicates/isNumber' +import liftA2 from 'crocks/helpers/liftA2' + +const { Ok, Err } = Result + +// buildError :: String -> String +const buildError = x => Err(`${x} is not a valid number`) + +const prod = + x => y => x * y + +const fromNumber = + ifElse(isNumber, Ok, buildError) + +map(prod, fromNumber(2)) + .ap(fromNumber(5)) +//=> Ok 10 + +map(prod, fromNumber('string')) + .ap(fromNumber(5)) +//=> Err "string is not a valid number" + +Ok(prod) + .ap(fromNumber(2)) + .ap(fromNumber(21)) +//=> Ok 42 + +Ok(prod) + .ap(fromNumber('string')) + .ap(fromNumber(21)) +//=> Err "string is not a valid number" + +liftA2(prod, fromNumber(2), fromNumber(21)) +//=> Ok 42 ``` #### sequence From 8b4d47654bad012917b50ad4ce31505ceb9507c5 Mon Sep 17 00:00:00 2001 From: Dale Francis Date: Tue, 18 Dec 2018 00:08:01 -0800 Subject: [PATCH 7/9] Adding examples for Chain --- docs/src/pages/docs/crocks/Result.md | 58 ++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/docs/src/pages/docs/crocks/Result.md b/docs/src/pages/docs/crocks/Result.md index 0def70dc6..cce887d11 100644 --- a/docs/src/pages/docs/crocks/Result.md +++ b/docs/src/pages/docs/crocks/Result.md @@ -625,6 +625,64 @@ inner value will be passed to provided function, returning the result as the new instance. ```javascript +import Result from 'crocks/Result' + +import map from 'crocks/pointfree/map' +import ifElse from 'crocks/logic/ifElse' +import compose from 'crocks/core/compose' +import isDefined from 'crocks/core/isDefined' +import isNumber from 'crocks/predicates/isNumber' +import chain from 'crocks/pointfree/chain' +import not from 'crocks/logic/not' +import isNil from 'crocks/predicates/isNil' +import bimap from 'crocks/pointfree/bimap' +import identity from 'crocks/combinators/identity' + +const { Ok, Err } = Result + +const ensure = (pred, f) => ifElse(pred, Ok, compose(Err, f)) + +const ensureNotIsNil = ensure(not(isNil), () => 'The value given was nil') + +const ensureDefined = ensure(isDefined, () => 'The value given was undefined') + +const prop = + name => + compose( + bimap(() => `${name} does not exist on the value given`, identity), + chain(ensureDefined), + map(x => x[name]), + ensureNotIsNil + ) + +const fromNumber = ensure(isNumber, x => `${x} is not a valid number`) + +const getAge = prop('age') + +// protectedAdd10 :: a -> Result String Number +const protectedAdd10 = compose(map(x => x + 10), fromNumber) + +getAge({ name: 'Sarah', age: 21 }) +//=> Ok 21 + +getAge({ name: 'Sarah', age: 'unk' }) +//=> Ok "unk" + +chain(fromNumber, getAge({ name: 'Sarah', age: 21 })) +//=> Ok 21 + +getAge({ name: 'Sarah', age: 21 }) + .chain(fromNumber) + .chain(protectedAdd10) +//=> Ok 31 + +getAge({ name: 'Sarah', age: 'unk' }) + .chain(fromNumber) + .chain(protectedAdd10) +//=> Err "unk is not a valid number" + +getAge({ name: 'Sarah' }) +//=> Err "age does not exist on the value given" ``` #### coalesce From 3bbe51ce3757e70b2789597bb607d7b1410f6dcb Mon Sep 17 00:00:00 2001 From: Dale Francis Date: Wed, 19 Dec 2018 00:24:03 -0800 Subject: [PATCH 8/9] Minor re-work, added either example --- docs/src/pages/docs/crocks/Result.md | 115 +++++++++++++++++++-------- 1 file changed, 83 insertions(+), 32 deletions(-) diff --git a/docs/src/pages/docs/crocks/Result.md b/docs/src/pages/docs/crocks/Result.md index cce887d11..b77a56f76 100644 --- a/docs/src/pages/docs/crocks/Result.md +++ b/docs/src/pages/docs/crocks/Result.md @@ -10,22 +10,24 @@ weight: 130 Result e a = Err e | Ok a ``` -Result is a Sum Type a step above `Result`. With a `Result` the -left side contains no information, where as with a `Result` the left contains -the error information from an operation. `Result` is well suited for capturing -disjunction when the cause of the "error" case needs to be communicated. For +Result is a Sum Type a step above [`Maybe`][maybe]. With a [`Maybe`][maybe] the left side +contains no information, where as with a `Result` the left contains the +error information from an operation. `Result` is well suited for capturing +disjunction when the cause of the "error" case needs to be communicated. For example, when executing a function and you exception is important or useful. -A `Result` represents disjunction by using two constructors, [`Err`](#err) or [`Ok`](#ok). -An [`Ok`](#ok) instance represents the positive result while [`Err`](#err) is considered -the negative. With the exception of [`coalesce`](#coalesce), [`swap`](#swap) -and [`bimap`](#bimap), all `Result` returning methods on an instance will be -applied to a [`Ok`](#ok) returning the result. If an instance is a [`Err`](#err), then all application is skipped and another [`Err`](#err) is returned. +A `Result` represents disjunction by using two constructors, [`Err`](#err) or +[`Ok`](#ok). An [`Ok`](#ok) instance represents the positive result while +[`Err`](#err) is considered the negative. With the exception of +[`coalesce`](#coalesce), [`swap`](#swap) and [`bimap`](#bimap), all `Result` +returning methods on an instance will be applied to a [`Ok`](#ok) returning the +result. If an instance is a [`Err`](#err), then all application is skipped and +another [`Err`](#err) is returned. It is recommended to use the available [`Ok`](#ok) and [`Err`](#err) constructors to construct `Result` instances in most cases. You can use the -`Result` constructor to construct a [`Ok`](#ok), but it will read better to just use -`Ok`. +`Result` constructor to construct a [`Ok`](#ok), but it will read better to +just use [`Ok`](#ok). ```javascript ``` @@ -52,9 +54,9 @@ instance constructors, [`Ok`](#ok) or [`Err`](#err). This is due to the nature of `Result` and most other Sum Types. As a matter of consistency and completion, a `Result` instance can also be -constructed using its TypeRep like any other type. The `Result` constructor is a -unary function that accepts any type `a` and returns a [`Ok`](#ok) instance, wrapping -the value passed to its argument. +constructed using its TypeRep like any other type. The `Result` constructor is +a unary function that accepts any type `a` and returns a [`Ok`](#ok) instance, +wrapping the value passed to its argument. ```javascript import Result from 'crocks/Result' @@ -124,9 +126,13 @@ equals( Result.Err :: e -> Result e a ``` -Used to construct an [`Err`](#err) instance that represents the "false" or -"Negative" portion of a disjunction. When an instance is an [`Err`](#err), most - `Result` returning methods will just return another [`Err`](#err). +Used to construct an [`Err`](#err) instance that represents the "false" or +"Negative" portion of a disjunction. When an instance is an [`Err`](#err), most +`Result` returning methods will just return another [`Err`](#err). + +The power of the [`Err`](#err) as opposed to a [`Nothing`] is that it can hold +meaningful information on why the flow is in this path. This works as a core +tool when using Railway Orientated Programming concepts. ```javascript import Result from 'crocks/Result' @@ -169,9 +175,9 @@ chain(protectedAdd10, Err('d')) Result.Ok :: a -> Result e a ``` -Used to construct a [`Ok`](#ok) instance that represents the "true" portion of a -disjunction or a valid value. [`Ok`](#ok) will wrap any given value in -a [`Ok`](#ok), signaling the validity of the wrapped value. +Used to construct an [`Ok`](#ok) instance that represents the "true" portion of +a disjunction or a valid value. [`Ok`](#ok) will wrap any given value in an +[`Ok`](#ok), signaling the validity of the wrapped value. ```javascript import Result from 'crocks/Result' @@ -257,11 +263,11 @@ Result(undefined) Result e a ~> b -> Boolean ``` -Used to compare the underlying values of two `Result` instances for equality by +Used to compare the contained values of two `Result` instances for equality by value. `equals` takes any given argument and returns `true` if the passed -arguments is a `Result` ([`Ok`](#ok) or [`Err`](#err)) with an underlying value -equal to the underlying value of the `Result` the method is being called on. If -the passed argument is not a `Result` or the underlying values are not equal, +arguments is a `Result` ([`Ok`](#ok) or [`Err`](#err)) with an contained value +equal to the contained value of the `Result` the method is being called on. If +the passed argument is not a `Result` or the contained values are not equal, `equals` will return `false`. ```javascript @@ -290,7 +296,6 @@ Ok([ 1, { a: 2 }, 'string' ]) equals(Ok('result'), 'result') //=> false - ``` #### concat @@ -646,6 +651,8 @@ const ensureNotIsNil = ensure(not(isNil), () => 'The value given was nil') const ensureDefined = ensure(isDefined, () => 'The value given was undefined') +const fromNumber = ensure(isNumber, x => `${x} is not a valid number`) + const prop = name => compose( @@ -655,8 +662,6 @@ const prop = ensureNotIsNil ) -const fromNumber = ensure(isNumber, x => `${x} is not a valid number`) - const getAge = prop('age') // protectedAdd10 :: a -> Result String Number @@ -691,9 +696,9 @@ getAge({ name: 'Sarah' }) Result e a ~> ((e -> b), (a -> b))) -> Result c b ``` -When one would like to [`option`](#option) a `Result` but would like to remain -within a `Result` type, `coalesce` can be used. `coalesce` expects two functions -for it's inputs. +There will come a time in your flow that you will want to ensure you have an +[`Ok`](#ok) of a given type. `coalesce` allows you to `map` over both the +[`Ok`](#ok) and the [`Err`](#err) and return an [`Ok`](#ok). `coalesce` expects two functions for it's inputs. The first function is used when invoked on a [`Err`](#err) and will return a [`Ok`](#ok) instance wrapping the result of the function. The second function is used when @@ -718,14 +723,60 @@ Result e a ~> ((e -> a), (a -> e)) -> Result e a Result e a ~> ((e -> b), (a -> b)) -> b ``` -Used to provide a means to map a given `Result` instance while optioning out the -wrapped value. `either` expects two functions as its arguments. The first is a +Used to provide a means to map a given `Result` instance folding it out of it's +container. `either` expects two functions as its arguments. The first is a function that will be used to map an [`Err`](#err). While the second will map the value wrapped in a given [`Ok`](#ok) and return the result of that mapping. +By using composing `either` you can create functions that us the power of +`ADT`s while returning a plain javascript type. ```javascript +import Result from 'crocks/Result' + +import either from 'crocks/pointfree/either' +import map from 'crocks/pointfree/map' +import ifElse from 'crocks/logic/ifElse' +import compose from 'crocks/core/compose' +import isNumber from 'crocks/predicates/isNumber' +import setProp from 'crocks/helpers/setProp' +import objOf from 'crocks/helpers/objOf' + +const { Ok, Err } = Result + +const ensure = (pred, f) => ifElse(pred, Ok, compose(Err, f)) + +const fromNumber = ensure(isNumber, x => `${x} is not a valid number`) + +const prod = + x => y => x * y + +// hasError :: Boolean -> Object -> Object +const hasError = + setProp('hasError') + +// buildResult :: (String, Boolean) -> a -> Object +const buildResult = (key, isError) => + compose(hasError(isError), objOf(key)) + +const createResult = + either( + buildResult('error', true), + buildResult('result', false) + ) + +const double = compose( + createResult, + map(prod(2)), + fromNumber +) + +double(42) +//=> { result: 84, hasError: false } + +double('value') +//=> { error: 'value is not a valid number', hasError: true } ``` From dbca5760ed2602f704e0f903733e37383c620d22 Mon Sep 17 00:00:00 2001 From: Dale Francis Date: Wed, 19 Dec 2018 23:49:29 -0800 Subject: [PATCH 9/9] Updating and adding examples for Natural Transforms --- docs/src/pages/docs/crocks/Result.md | 147 +++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 7 deletions(-) diff --git a/docs/src/pages/docs/crocks/Result.md b/docs/src/pages/docs/crocks/Result.md index b77a56f76..be86cb48d 100644 --- a/docs/src/pages/docs/crocks/Result.md +++ b/docs/src/pages/docs/crocks/Result.md @@ -827,6 +827,75 @@ returned. When passed an [`Either`][either] returning function, a function will be returned that takes a given value and returns a `Result`. ```javascript +import Result from 'crocks/Result' + +import Either from 'crocks/Either' +import assign from 'crocks/helpers/assign' +import compose from 'crocks/helpers/compose' +import composeK from 'crocks/helpers/composeK' +import fanout from 'crocks/Pair/fanout' +import isNumber from 'crocks/predicates/isNumber' +import liftA2 from 'crocks/helpers/liftA2' +import map from 'crocks/pointfree/map' +import maybeToEither from 'crocks/Result/maybeToEither' +import merge from 'crocks/Pair/merge' +import objOf from 'crocks/helpers/objOf' +import prop from 'crocks/Maybe/prop' +import eitherToResult from 'crocks/Either/eitherToResult' +import safeLift from 'crocks/Maybe/safeLift' + +const { Left, Right } = Either +const { Ok } = Result + +eitherToResult(Left('no good')) +//=> Err "no good" + +eitherToResult(Right('so good')) +//=> Ok "so good" + +// safeInc :: a -> Maybe Number +const safeInc = + safeLift(isNumber, x => x + 1) + +// incProp :: String -> a -> Maybe Number +const incProp = key => + composeK(safeInc, prop(key)) + +// incResult :: String -> a -> Result [ String ] Object +const incResult = key => maybeToEither( + [ `${key} is not valid` ], + compose(map(objOf(key)), incProp(key)) +) + +// incThem :: a -> Result [ String ] Object +const incThem = compose( + merge(liftA2(assign)), + fanout(incResult('b'), incResult('a')) +) + +Result.of({}) + .chain(eitherToResult(incThem)) +//=> Err [ "b is not valid", "a is not valid" ] + +Result.of({ a: 33 }) + .chain(eitherToResult(incThem)) +//=> Err [ "b is not valid" ] + +Result.of({ a: 99, b: '41' }) + .chain(eitherToResult(incThem)) +//=> Err [ "b is not valid" ] + +Result.of({ a: 99, b: 41 }) + .chain(eitherToResult(incThem)) +//=> Ok { a: 100, b: 42 } + +Ok(Left('Err')) + .chain(eitherToResult) +//=> Err "Err" + +Ok(Right('42')) + .chain(eitherToResult) +//=> Ok "42" ``` #### firstToResult @@ -912,6 +981,37 @@ When passed a [`Last`][last] returning function, a function will be returned that takes a given value and returns a `Result`. ```javascript +import Result from 'crocks/Result' + +import Last from 'crocks/Last' +import lastToResult from 'crocks/Result/lastToResult' + +import mconcat from 'crocks/helpers/mconcat' + +const { Ok, Err } = Result + +// lastValue :: [ a ] -> Last a +const lastValue = + mconcat(Last) + +lastToResult('Error occurred!', Last('the end')) +//=> Ok "the end" + +Err('Error occurred!') + .chain(lastToResult('Error occurred!', lastValue)) +//=> Err "Error occurred!" + +Ok([]) + .chain(lastToResult('Error occurred!', lastValue)) +//=> Err "Error occurred!" + +Ok([ 'first', 'second', 'third' ]) + .chain(lastToResult('Error occurred!', lastValue)) +//=> Ok "third" + +Ok(Last('last')) + .chain(lastToResult('Error occurred!')) +//=> Ok "last" ``` #### maybeToResult @@ -925,19 +1025,51 @@ maybeToResult :: e -> (a -> Maybe b) -> a -> Result e b Used to transform a given [`Maybe`][maybe] instance to a `Result` instance or flatten a `Result` of [`Maybe`][maybe] into a `Result` when chained, -`maybeToResult` will turn an [`Just`][just] instance into a [`Ok`](#ok) wrapping the +`maybeToResult` will turn a [`Just`][just] instance into an [`Ok`](#ok) wrapping the original value contained in the [`Just`][just]. -All [`Err`](#err) instances will map to a [`Err`](#err), mapping the originally -contained value to a `Unit`. Values on the [`Err`](#err) will be lost and as such this -transformation is considered lossy in that regard. +All [`Nothing`](nothing) instances will map to a [`Err`](#err), containing the +given `e` value. Like all `crocks` transformation functions, `maybeToResult` has two possible -signatures and will behave differently when passed either an `Result` instance -or a function that returns an instance of `Result`. When passed the instance, -a transformed `Result` is returned. When passed a `Result` returning function, +signatures and will behave differently when passed either a [`Maybe`][maybe] instance +or a function that returns an instance of [`Maybe`][maybe]. When passed the instance, +a transformed `Result` is returned. When passed a [`Maybe`][maybe] returning function, a function will be returned that takes a given value and returns a `Result`. +This means that when used with the [`Maybe-helpers`][helpers] and `compose` you +have a larger collection of `Result` returning functions. ```javascript +import Result from 'crocks/Result' + +import maybeToResult from 'crocks/Result/maybeToResult' +import Maybe from 'crocks/Maybe' +import prop from 'crocks/Maybe/prop' +import compose from 'crocks/helpers/compose' + +const { Ok } = Result +const { Just, Nothing } = Maybe + +maybeToResult('An error occurred', Just('21')) +//=> Ok "21" + +maybeToResult('An error occurred', Nothing()) +//=> Err "An error occurred" + +const getProp = compose(maybeToResult('The requested prop did not exist or was undefined'), prop) + +getProp('name', { name: 'Jobn', age: 21 }) +//=> Ok "Jobn" + +getProp('name', { age: 27 }) +//=> Err "The requested prop did not exist or was undefined" + +Ok(Just('in time!')) + .chain(maybeToResult('An error occurred')) +//=> Ok "in time!" + +Ok(Nothing()) + .chain(maybeToResult('An error occurred')) +//=> Err "An error occurred" ``` @@ -950,3 +1082,4 @@ a function will be returned that takes a given value and returns a `Result`. [maybe]: ../monoids/Maybe.html [just]: ./Maybe.html#just [nothing]: ./Maybe.html#nothing +[helpers]: ./Maybe.html#helper-functions