diff --git a/.all-contributorsrc b/.all-contributorsrc index c7bdf618f..b9e193075 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -54,7 +54,8 @@ "profile": "https://robertwpearce.com", "contributions": [ "bug", - "code" + "code", + "review" ] }, { @@ -65,6 +66,15 @@ "contributions": [ "bug" ] + }, + { + "login": "foxdonut", + "name": "Fred Daoud", + "avatar_url": "https://avatars3.githubusercontent.com/u/1706600?v=4", + "profile": "http://www.fdaoud.com", + "contributions": [ + "review" + ] } ] } diff --git a/README.md b/README.md index 2fe606ff5..6edb3a373 100644 --- a/README.md +++ b/README.md @@ -262,8 +262,8 @@ Thanks goes to these wonderful people ([emoji key][emojis]): -| [
Ian Hofmann-Hicks](https://github.com/evilsoft)
[💻](https://github.com/evilsoft/crocks/commits?author=evilsoft "Code") [📖](https://github.com/evilsoft/crocks/commits?author=evilsoft "Documentation") [📹](#video-evilsoft "Videos") | [
Ryan](https://github.com/rstegg)
[💻](https://github.com/evilsoft/crocks/commits?author=rstegg "Code") [🐛](https://github.com/evilsoft/crocks/issues?q=author%3Arstegg "Bug reports") | [
Andrew Van Slaars](http://vanslaars.io)
[📖](https://github.com/evilsoft/crocks/commits?author=avanslaars "Documentation") | [
Henrique Limas](https://github.com/HenriqueLimas)
[💻](https://github.com/evilsoft/crocks/commits?author=HenriqueLimas "Code") [📖](https://github.com/evilsoft/crocks/commits?author=HenriqueLimas "Documentation") | [
Robert Pearce](https://robertwpearce.com)
[🐛](https://github.com/evilsoft/crocks/issues?q=author%3Arpearce "Bug reports") [💻](https://github.com/evilsoft/crocks/commits?author=rpearce "Code") | [
Scott McCormack](https://github.com/flintinatux)
[🐛](https://github.com/evilsoft/crocks/issues?q=author%3Aflintinatux "Bug reports") | -| :---: | :---: | :---: | :---: | :---: | :---: | +| [
Ian Hofmann-Hicks](https://github.com/evilsoft)
[💻](https://github.com/evilsoft/crocks/commits?author=evilsoft "Code") [📖](https://github.com/evilsoft/crocks/commits?author=evilsoft "Documentation") [📹](#video-evilsoft "Videos") | [
Ryan](https://github.com/rstegg)
[💻](https://github.com/evilsoft/crocks/commits?author=rstegg "Code") [🐛](https://github.com/evilsoft/crocks/issues?q=author%3Arstegg "Bug reports") | [
Andrew Van Slaars](http://vanslaars.io)
[📖](https://github.com/evilsoft/crocks/commits?author=avanslaars "Documentation") | [
Henrique Limas](https://github.com/HenriqueLimas)
[💻](https://github.com/evilsoft/crocks/commits?author=HenriqueLimas "Code") [📖](https://github.com/evilsoft/crocks/commits?author=HenriqueLimas "Documentation") | [
Robert Pearce](https://robertwpearce.com)
[🐛](https://github.com/evilsoft/crocks/issues?q=author%3Arpearce "Bug reports") [💻](https://github.com/evilsoft/crocks/commits?author=rpearce "Code") [👀](#review-rpearce "Reviewed Pull Requests") | [
Scott McCormack](https://github.com/flintinatux)
[🐛](https://github.com/evilsoft/crocks/issues?q=author%3Aflintinatux "Bug reports") | [
Fred Daoud](http://www.fdaoud.com)
[👀](#review-foxdonut "Reviewed Pull Requests") | +| :---: | :---: | :---: | :---: | :---: | :---: | :---: | ### Course/Videos diff --git a/docs/src/pages/docs/functions/helpers.md b/docs/src/pages/docs/functions/helpers.md index 11c26480a..58965878b 100644 --- a/docs/src/pages/docs/functions/helpers.md +++ b/docs/src/pages/docs/functions/helpers.md @@ -423,7 +423,7 @@ mapProps(mapping, { `crocks/helpers/mapReduce` ```haskell -mapReduce :: Foldable f => (a -> b) -> (c -> b -> c) -> f a -> c +mapReduce :: Foldable f => (a -> b) -> (c -> b -> c) -> c -> f a -> c ``` Sometimes you need the power provided by [`mreduceMap`](#mreducemap) but you do @@ -846,7 +846,7 @@ get() `crocks/Maybe/safe` ```haskell -safe :: ((a -> Boolean) | Pred) -> a -> Maybe a +safe :: ((a -> Boolean) | Pred) -> b -> Maybe a ``` When using a `Maybe`, it is a common practice to lift into a `Just` or a @@ -861,7 +861,7 @@ true and a `Nothing` if false. `crocks/Maybe/safeLift` ```haskell -safeLift :: ((a -> Boolean) | Pred) -> (a -> b) -> a -> Maybe b +safeLift :: ((c -> Boolean) | Pred) -> (a -> b) -> c -> Maybe b ``` While [`safe`](#safe) is used to lift a value into a `Maybe`, you can reach for diff --git a/docs/src/pages/docs/functions/index.md b/docs/src/pages/docs/functions/index.md index 6ae1984b0..39b170edb 100644 --- a/docs/src/pages/docs/functions/index.md +++ b/docs/src/pages/docs/functions/index.md @@ -72,7 +72,7 @@ need to account for for the rest of your flow. | [`liftA2`][lifta2] | `Applicative m => (a -> b -> c) -> m a -> m b -> m c` | `crocks/helpers/liftA2` | | [`liftA3`][lifta3] | `Applicative m => (a -> b -> c -> d) -> m a -> m b -> m c -> m d` | `crocks/helpers/liftA3` | | [`mapProps`][mapprops] | `Object -> Object -> Object` | `crocks/helpers/mapProps` | -| [`mapReduce`][mapreduce] | `Foldable f => (a -> b) -> (c -> b -> c) -> f a -> c` | `crocks/helpers/mapReduce` | +| [`mapReduce`][mapreduce] | `Foldable f => (a -> b) -> (c -> b -> c) -> c -> f a -> c` | `crocks/helpers/mapReduce` | | [`mconcat`][mconcat] | `Monoid m, Foldable f => m -> f a -> m a` | `crocks/helpers/mconcat` | | [`mconcatMap`][mconcatmap] | `Monoid m, Foldable f => m -> (b -> a) -> f b -> m a` | `crocks/helpers/mconcatMap` | | [`mreduce`][mreduce] | `Monoid m, Foldable f => m -> f a -> a` | `crocks/helpers/mreduce` | diff --git a/src/Maybe/README.md b/src/Maybe/README.md new file mode 100644 index 000000000..a88b9ec35 --- /dev/null +++ b/src/Maybe/README.md @@ -0,0 +1,1323 @@ +# Maybe + +```haskell +Maybe a = Nothing | Just a +``` + +Defined as a Sum Type with its left side fixed to `()` (`Nothing`), `Maybe` is +well suited for capturing disjunction when the cause of the "error" case does +not need to be communicated. For example, providing default values on specific +conditions. + +A `Maybe` represents disjunction by using (2) constructors, `Nothing` or `Just`. +A `Just` instance represents the truth case while `Nothing` is considered +false. With the exception of [`coalesce`](#coalesce), all `Maybe` returning +methods on an instance will be applied to a `Just` returning the result. If an +instance is a `Nothing`, then all application is skipped and another `Nothing` +is returned. + +It is recommended to use the available [`Just`](#just) and [`Nothing`](#nothing) +constructors to construct `Maybe` instances in most cases. You can use the +`Maybe` constructor to construct a `Just`, but it may read better to just use +`Just`. + +```javascript +const { Just, Nothing } = require('crocks/Maybe') + +const chain = require('crocks/pointfree/chain') +const compose = require('crocks/helpers/compose') +const ifElse = require('crocks/logic/ifElse') +const isNumber = require('crocks/predicates/isNumber') + +// gt5 :: Number -> Boolean +const gt5 = + x => x > 5 + +// safe :: (a -> Boolean) -> a -> Maybe b +const safe = pred => + ifElse(pred, Just, Nothing) + +// safeNumber :: a -> Maybe Number +const safeNumber = + safe(isNumber) + +// maybeBig :: Number -> Maybe Number +const maybeBig = + safe(gt5) + +// bigNumber :: a -> Maybe Number +const bigNumber = compose( + chain(maybeBig), safeNumber +) + +safeNumber(45) +//=> Just 45 + +safeNumber('99') +//=> Nothing + +maybeBig(99) +//=> Just 99 + +maybeBig(2) +//=> Nothing + +bigNumber(34) +//=> Just 34 + +bigNumber('string') +//=> Nothing + +bigNumber(3) +//=> Nothing +``` + +## Implements +`Setoid`, `Semigroup`, `Functor`, `Alt`, `Plus`, `Apply`, `Traversable`, +`Chain`, `Applicative`, `Alternative`, `Monad` + +## Constructor Methods + +#### Nothing + +```haskell +Maybe.Nothing :: () -> Maybe a +``` + +Used to construct a `Nothing` instance that represents the "false" portion of +a disjunction. When an instance is a `Nothing`, most `Maybe` returning methods +will just return another `Nothing`. Anything passed to the constructor will +be thrown out and mapped to `()`. + +```javascript +const { Just, Nothing } = require('crocks/Maybe') + +const chain = require('crocks/pointfree/chain') +const isNumber = require('crocks/predicates/isNumber') +const safeLift = require('crocks/Maybe/safeLift') + +// add10 :: Number -> Number +const add10 = + x => x + 10 + +// safeAdd10 :: a -> Maybe Number +const safeAdd10 = + safeLift(isNumber, add10) + +Just(23) + .map(add10) +//=> Just 33 + +Nothing(23) + .map(add10) +//=> Nothing + +chain(safeAdd10, Just(10)) +//=> Just 20 + +chain(safeAdd10, Nothing()) +//=> Nothing +``` + +#### Just + +```haskell +Maybe.Just :: a -> Maybe a +``` + +Used to construct a `Just` instance that represents the "true" portion of a +disjunction or a valid value. `Just` will wrap any given value in +a `Just`, signalling the validity of the wrapped value. + +```javascript +const { Just, Nothing } = require('crocks/Maybe') + +const compose = require('crocks/helpers/compose') +const ifElse = require('crocks/logic/ifElse') +const isString = require('crocks/predicates/isString') +const map = require('crocks/pointfree/map') + +// toUpper :: String -> String +const toUpper = + x => x.toUpperCase() + +// safe :: (a -> Boolean) -> a -> Maybe a +const safe = + pred => ifElse(pred, Just, Nothing) + +// safeShout :: a -> Maybe String +const safeShout = compose( + map(toUpper), + safe(isString) +) + +safeShout(45) +//=> Nothing + +safeShout('Hey there!') +//=> Just "HEY THERE!" +``` + +#### of + +```haskell +Maybe.of :: a -> Maybe a +``` + +Used to wrap any value into a `Maybe` as a `Just`, `of` is used mostly by +helper functions that work "generically" with instances of +either `Applicative` or `Monad`. When working specifically with +the `Maybe` type, the [`Just`](#just) constructor should be used. Reach +for `of` when working with functions that will work with +ANY `Applicative`/`Monad`. + +```javascript +const Maybe = require('crocks/Maybe') +const { Just } = Maybe + +const curry = require('crocks/helpers/curry') +const isString = require('crocks/predicates/isString') +const safe = require('crocks/Maybe/safe') + +Maybe(35) +//=> Just 35 + +Just(35) +//=> Just 35 + +Maybe.of(35) +//=> Just 35 + +const safeString = + safe(isString) + +// lift2 :: Applicative m => (a -> b -> c) -> m a -> m b -> m c +const lift2 = curry( + (fn, x, y) => x.of(fn).ap(x).ap(y) +) + +// join :: Applicative m => m String -> m String -> m String +const join = + lift2(a => b => `${a} ${b}`) + +join(safeString('Brad'), safeString('Pitt')) +//=> Just "Brad Pitt" + +join(safeString(34), safeString('Pitt')) +//=> Nothing +``` + +#### zero + +```haskell +Maybe.zero :: () -> Maybe a +``` + +When working with `Alt`s, `zero` provides a sort of `empty` or identity for +`Maybe` when used with [`alt`](#alt). `zero` takes no arguments and returns a +`Nothing` instance. Just like an `empty` method on a given `Monoid`, `zero` +can be used to fold a collection of `Alt`s under `alt`. + +```javascript +const { Nothing, Just, zero } = require('crocks/Maybe') + +const alt = require('crocks/pointfree/alt') +const flip = require('crocks/combinators/flip') +const isNumber = require('crocks/predicates/isNumber') +const mapReduce = require('crocks/helpers/mapReduce') +const safe = require('crocks/Maybe/safe') + +// firstValid :: [ * ] -> Maybe Number +const firstValid = + mapReduce(safe(isNumber), flip(alt), zero()) + +Just(33) + .alt(zero()) +//=> Just 33 + +zero() + .alt(Just(33)) +//=> Just 33 + +Nothing() + .alt(zero()) +//=> Nothing + +zero() + .alt(Nothing()) +//=> Nothing + +firstValid([ null, 'nope', 10, 45 ]) +//=> Just 10 + +firstValid([ 75, null, 'nope' ]) +//=> Just 75 + +firstValid([ null, undefined, 'wrong' ]) +//=> Nothing +``` + +#### type + +```haskell +Maybe.type :: () -> String +``` + +`type` provides a string representation of the type name for a given type in +`crocks`. While it is used mostly internally for law validation, it can be +useful to the end user for debugging and building out custom types based on the +standard `crocks` types. While type comparisons can easily be done manually by +calling `type` on a given type, using the `isSameType` function hides much of +the boilerplate. `type` is available on both the Constructor and the Instance +for convenience. + +```javascript +const Maybe = require('crocks/Maybe') +const { Just, Nothing } = Maybe + +const Any = require('crocks/Any') +const isSameType = require('crocks/predicates/isSameType') + +Maybe.type() //=> "Maybe" + +isSameType(Maybe, Nothing()) //=> true +isSameType(Maybe, Just(3)) //=> true +isSameType(Nothing(), Just(23)) //=> true +isSameType(Maybe, Any(true)) //=> false +isSameType(Maybe(null), Any) //=> false +``` + +## Instance Methods + +#### equals + +```haskell +Maybe a ~> b -> Boolean +``` + +Used to compare the underlying values of (2) `Maybe` instances for equality by +value, `equals` takes any given argument and returns `true` if the passed +arguments is a `Maybe` with an underlying value equal to the underlying value +of the `Maybe` the method is being called on. If the passed argument is not +a `Maybe` or the underlying values are not equal, `equals` will return `false`. + +```javascript +const { Nothing, Just } = require('crocks/Maybe') + +const equals = require('crocks/pointfree/equals') + +Just(33) + .equals(Just(33)) +//=> true + +Nothing() + .equals(Nothing()) +//=> true + +Nothing() + .equals(Just(33)) +//=> false + +// by value, not reference for most types +Just({ a: 86, b: true }) + .equals(Just({ a: 86, b: true })) +//=> true + +equals(Just(95), 95) +//=> false + +equals(undefined, Nothing()) +//=> false + +equals(Just([ 2, 3 ]), Just([ 2, 3 ])) +//=> true +``` + +#### concat + +```haskell +Semigroup s => Maybe s ~> Maybe s -> Maybe s +``` + +When an underlying value of a given `Maybe` is fixed to a `Semigroup`, `concat` +can be used to concat another `Maybe` instance with an underlying `Semigroup` +of the same type. Expecting a `Maybe` wrapping a `Semigroup` of the same type, +`concat` will give back a new `Maybe` instance wrapping the result of combining +the (2) underlying `Semigroup`s. When called on a `Nothing` instance, `concat` +will return a `Nothing`. + +```javascript +const { Nothing, Just } = require('crocks/Maybe') +const Sum = require('crocks/Sum') + +const compose = require('crocks/helpers/compose') +const concat = require('crocks/pointfree/concat') +const flip = require('crocks/combinators/flip') +const isNumber = require('crocks/predicates/isNumber') +const map = require('crocks/pointfree/map') +const mapReduce = require('crocks/helpers/mapReduce') +const safeLift = require('crocks/Maybe/safeLift') +const valueOf = require('crocks/pointfree/valueOf') + +// safeSum :: a -> Maybe Sum +const safeSum = + safeLift(isNumber, Sum) + +// empty :: Maybe Sum +const empty = + Just(Sum.empty()) + +// sumList :: [ * ] -> Maybe Number +const sumList = compose( + map(valueOf), + mapReduce(safeSum, flip(concat), empty) +) + +Just([ 34 ]) + .concat(Just([ 92 ])) +//=> Just [ 34, 92 ] + +Just([ 34 ]) + .concat(Nothing()) +//=> Nothing + +sumList([ 3, 4, 5 ]) +//=> Just 12 + +sumList([ 'three', 4, 'five' ]) +//=> Nothing +``` + +#### map + +```haskell +Maybe a ~> (a -> b) -> Maybe b +``` + +Used to apply transformations to values in the safety of a `Maybe`, `map` takes +a function that it will lift into the context of the `Maybe` and apply to it +the wrapped value. When ran on a `Just` instance, `map` will apply the wrapped +value to the provided function and return the result in a new `Just` instance. + + +```javascript +const { Nothing, Just } = require('crocks/Maybe') + +const assign = require('crocks/helpers/assign') +const compose = require('crocks/helpers/compose') +const isObject = require('crocks/predicates/isObject') +const isString = require('crocks/predicates/isString') +const map = require('crocks/pointfree/map') +const safe = require('crocks/Maybe/safe') + +// add10 :: Number -> Number +const add10 = + x => x + 10 + +// toUpper :: String -> String +const toUpper = x => + x.toUpperCase() + +// safeObj :: a -> Maybe Object +const safeObj = + safe(isObject) + +// shout :: a -> Maybe String +const shout = x => + safe(isString, x) + .map(toUpper) + +// setProcessed :: a -> Maybe Object +const setProcessed = compose( + map(assign({ processed: true })), + safeObj +) + +Just(0) + .map(add10) +//=> Just 10 + +Nothing() + .map(add10) +//=> Nothing + +shout('good news') +//=> Just "GOOD NEWS" + +shout(33) +//=> Nothing + +setProcessed({ cheese: true }) +//=> Just { cheese: true, processed: true } + +setProcessed(null) +//=> Nothing +``` + +#### alt + +```haskell +Maybe a ~> Maybe a -> Maybe a +``` + +Providing a means for a fallback or alternative value, `alt` combines (2) +`Maybe` instances and will return the first `Just` it encounters or `Nothing` +if it does not have a `Just`. This can be used in conjunction with +[`zero`](#zero) to return the first valid value in contained in a `Foldable` +structure. + +```javascript +const { zero, Nothing, Just } = require('crocks/Maybe') + +const alt = require('crocks/pointfree/alt') +const isArray = require('crocks/predicates/isArray') +const flip = require('crocks/combinators/flip') +const mapReduce = require('crocks/helpers/mapReduce') +const safe = require('crocks/Maybe/safe') + +// firstArray :: Foldable f => f * -> Maybe Array +const firstArray = + mapReduce(safe(isArray), flip(alt), zero()) + +Nothing() + .alt(Just(33)) +//=> Just 33 + +Just(42) + .alt(Nothing()) + .alt(Just(99)) +//=> Just 42 + +firstArray([ 'Not Array', null, [ 2, 3, 4 ], [ 1, 2 ] ]) +//=> Just [ 2, 3, 4 ] + +firstArray([ null, 5, '76' ]) +//=> Nothing +``` + +#### ap + +```haskell +Maybe (a -> b) ~> Maybe a -> Maybe b +``` + +Short for apply, `ap` is used to apply a `Maybe` instance containing a value +to another `Maybe` instance that contains a function, resulting in new `Maybe` +instance with the result. `ap` requires that it is called on an `instance` that +is either a `Nothing` or a `Just` that wraps a curried polyadic function. + +When either `Maybe` is a `Nothing`, `ap` will return a `Nothing`. This can be +used to safely combine multiple values under a given combination function. If +any of the inputs results in a `Nothing` than they will never be applied to +the function and not provide exceptions or unexpected results. + +```javascript +const Maybe = require('crocks/Maybe') +const { Nothing, Just } = Maybe + +const compose = require('crocks/helpers/compose') +const chain = require('crocks/pointfree/chain') +const curry = require('crocks/helpers/curry') +const fanout = require('crocks/helpers/fanout') +const isString = require('crocks/predicates/isString') +const liftA2 = require('crocks/helpers/liftA2') +const merge = require('crocks/Pair/merge') +const prop = require('crocks/Maybe/prop') +const safe = require('crocks/Maybe/safe') + +// add :: Number -> Number -> Number +const add = + x => y => x + y + +// joinWith :: String -> String -> String -> String +const joinWith = curry( + (del, x, y) => x + del + y +) + +// stringProp :: String -> a -> Maybe String +const stringProp = key => compose( + chain(safe(isString)), + prop(key) +) + +// getNames :: a -> Pair (Maybe String) (Maybe String) +const getNames = fanout( + stringProp('first'), + stringProp('last') +) + +// joinNames :: Pair (Maybe String) (Maybe String) -> Maybe String +const joinNames = + merge(liftA2(joinWith(' '))) + +// fullName :: a -> Maybe String +const fullName = + compose(joinNames, getNames) + +Maybe.of(add) + .ap(Just(5)) + .ap(Just(27)) +//=> Just 32 + +Just('hello') + .map(joinWith(' -- ')) + .ap(Just('friend')) +//=> Just "hello -- friend" + +Maybe.of(add) + .ap(Just(29)) + .ap(Nothing()) +//=> Nothing + +fullName({ first: 'Joey', last: 'Fella' }) +//=> Just "Joey Fella" + +fullName(null) +//=> Nothing + +fullName({ first: 'Lizzy' }) +//=> Nothing +``` + +#### sequence + +```haskell +Applicative f => Maybe (f a) ~> (a -> f a) -> f (Maybe a) +``` + +When an instance of `Maybe` wraps a possible `Applicative` instance, `sequence` +can be used to "swap" the "type sequence". `sequence` requires that the `of`, +or `Applicative` function of the wrapped `Applicative` instance is provided for +the case of when the `Maybe` instance is a `Nothing`. `sequence` can be derived +from [`traverse`](#traverse) by passing it an `identity` function (`x => x`). + +```javascript +const { Nothing, Just } = require('crocks/Maybe') + +const Identity = require('crocks/Identity') +const sequence = require('crocks/pointfree/sequence') + + +// seqId :: Maybe Identity a -> Identity Maybe a +const seqId = + sequence(Identity.of) + +seqId(Just(Identity(34))) +//=> Identity Just 34 + +seqId(Nothing()) +//=> Identity Nothing +``` + +#### traverse + +```haskell +Applicative f => Maybe a ~> ((a -> f a), (a -> f b)) -> f Maybe b +``` + +Used to apply the "effect" of an `Applicative` to a value inside of a `Maybe`, +`traverse`, combines both the "effects" of the `Applicative` and the `Maybe` by +returning a new instance of the `Applicative` wrapping the result of the +`Applicative`s "effect" on the value in the `Maybe`. + +`traverse` requires the `of` function of the target `Applicative` and a function +that is used to apply the `Applicative` to the value inside of the `Maybe`. Both +functions must return an instance of the `Applicative`. + +```javascript +const IO = require('crocks/IO') + +const compose = require('crocks/helpers/compose') +const isNumber = require('crocks/predicates/isNumber') +const safe = require('crocks/Maybe/safe') +const traverse = require('crocks/pointfree/traverse') + +let someGlobal = 10 + +// addToGlobal :: Number -> IO Number +const addToGlobal = x => IO(function() { + someGlobal = someGlobal + x + return someGlobal +}) + +// safeAddToGlobal :: a -> IO (Maybe Number) +const safeAddToGlobal = compose( + traverse(IO.of, addToGlobal), + safe(isNumber) +) + +safeAddToGlobal(32) + .run() +//=> Just 42 +//someGlobal => 42 + +safeAddToGlobal(undefined) + .run() +//=> Nothing +//someGlobal => 42 +``` + +#### 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, `Maybe` returning function +as its argument. When invoked on a `Nothing`, `chain` will not run the function, +but will instead return another `Nothing`. When called on a `Just` however, the +inner value will be passed to provided function, returning the result as the +new instance. + +```javascript +const { Nothing, Just } = require('crocks/Maybe') + +const chain = require('crocks/pointfree/chain') +const compose = require('crocks/helpers/compose') +const isNumber = require('crocks/predicates/isNumber') +const isString = require('crocks/predicates/isString') +const prop = require('crocks/Maybe/prop') +const safe = require('crocks/Maybe/safe') +const safeLift = require('crocks/Maybe/safeLift') + +// double :: Number -> Number +const double = + x => x + x + +// chainNumber :: Maybe a -> Maybe Number +const chainNumber = + chain(safe(isNumber)) + +// doubleValue :: a -> Maybe Number +const doubleValue = compose( + chain(safeLift(isNumber, double)), + prop('value') +) + +chainNumber(Just(45)) +//=> Just 45 + +chainNumber(Nothing()) +//=> Nothing + +Just(45) + .chain(safe(isString)) +//=> Nothing + +doubleValue(undefined) +//=> Nothing + +doubleValue({ value: '45' }) +//=> Nothing + +doubleValue({ number: 45 }) +//=> Nothing + +doubleValue({ value: 45 }) +//=> Just 90 +``` + +#### coalesce + +```haskell +Maybe a ~> ((() -> b), (a -> b))) -> Maybe b +``` + +When one would like to [`option`](#option) a `Maybe` but would like to remain +within a `Maybe` type, `coalesce` can be used. `coalesce` expects (2) functions +for it's inputs. + +The first function is used when invoked on a `Nothing` and will return a `Just` +instance wrapping the result of the function. The second function is used when +`coalesce` is invoked on a `Just` and is used to map the original value, +returning a new `Just` instance wrapping the result of the second function. + +```javascript +const { Nothing, Just } = require('crocks/Maybe') + +const compose = require('crocks/helpers/compose') +const composeK = require('crocks/helpers/composeK') +const coalesce = require('crocks/pointfree/coalesce') +const constant = require('crocks/combinators/constant') +const identity = require('crocks/combinators/identity') +const isString = require('crocks/predicates/isString') +const map = require('crocks/pointfree/map') +const objOf = require('crocks/helpers/objOf') +const prop = require('crocks/Maybe/prop') +const safe = require('crocks/Maybe/safe') + +// shout :: String -> String +const shout = + x => x.toUpperCase() + +// defaultString :: Maybe String -> Maybe String +const defaultString = + coalesce(constant(''), identity) + +// shoutOut :: String -> Object +const shoutOut = compose( + objOf('shout'), + shout +) + +// stringValue :: a -> Maybe String +const stringValue = composeK( + safe(isString), + prop('value') +) + +// shoutValue :: a -> Maybe Object +const shoutValue = compose( + map(shoutOut), + defaultString, + stringValue +) + +Just(76) + .coalesce(constant(0), identity) +//=> Just 76 + +Nothing() + .coalesce(constant(0), identity) +//=> Just 0 + +shoutValue({ value: 'hello' }) +//=> Just { shout: 'HELLO' } + +shoutValue(undefined) +//=> Just { shout: '' } + +shoutValue({ value: 49 }) +//=> Just { shout: '' } + +shoutValue({}) +//=> Just { shout: '' } +``` + +#### option + +```haskell +Maybe a ~> a -> a +``` + +Used as the primary way to "fold" a value out of a `Maybe`, `option` expects a +default value. The default value provided will be returned when `option` is +invoked on a `Nothing` instance. When invoked on a `Just`, 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 `Maybe` arises, +then [`either`](#either) may be employed to combine it in one operation. + +```javascript +const { Nothing, Just } = require('crocks/Maybe') + +Nothing() + .option(0) +//=> 0 + +Just(99) + .option(0) +//=> 99 +``` + +#### either + +```haskell +Maybe a ~> ((() -> b), (a -> b)) -> b +``` + +Used to provide a means to map a given `Maybe` instance while optioning out the +wrapped value. [`option`](#option) can handle most cases for optioning `Maybe`, +but does not provide a means to map a given value at the time of +optioning. `either` expects (2) functions as its arguments. The first is a +pointed function that will be used when invoked on a `Nothing`. While the second +will map the value wrapped in a given `Just` and return the result of that +mapping. + + +```javascript +const { Nothing, Just } = require('crocks/Maybe') + +const either = require('crocks/pointfree/either') + +// wrap :: a -> [ a ] +const wrap = + x => [ x ] + +// empty :: () -> [ a ] +const empty = + () => [] + +// toArray :: Maybe a -> [ a ] +const toArray = + either(empty, wrap) + +toArray(Just(56)) +//=> [ 56 ] + +toArray(Nothing()) +//=> [] +``` + +## Helper Functions + +#### prop + +`crocks/Maybe/prop` + +```haskell +prop :: (String | Integer) -> a -> Maybe b +``` + +If you want some safety around pulling a value out of an `Object` or `Array` +with a single key or index, you can always reach for `prop`. Well, as long +as you are working with non-nested data that is. Just tell `prop` either the key +or index you are interested in, and you will get back a function that will take +anything and return a `Just` with the wrapped value if the key/index exists. If +the key/index does not exist however, you will get back a `Nothing`. + +```javascript +const composeK = require('crocks/helpers/composeK') +const prop = require('crocks/Maybe/prop') + +// getValue :: a -> Maybe b +const getValue = + prop('value') + +// getHead :: a -> Maybe b +const getHead = + prop(0) + +// getFirstValue :: a -> Maybe b +const getFirstValue = composeK( + getHead, + getValue +) + +getValue({ some: false }) +//=> Nothing + +getValue(undefined) +//=> Nothing + +getValue({ value: 'correct' }) +//=> Just "correct" + +getFirstValue({ value: [] }) +//=> Nothing + +getFirstValue({ value: 84 }) +//=> Nothing + +getFirstValue(null) +//=> Nothing + +getFirstValue({ value: [ 'a', 'b' ] }) +//=> Just "a" +``` + +#### propPath + +`crocks/Maybe/propPath` + +```haskell +propPath :: Foldable f => f (String | Integer) -> a -> Maybe b +``` + +While [`prop`](#prop) is good for simple, single-level structures, there may +come a time when you have to work with nested POJOs or Arrays. When you run into +this situation, just pull in `propPath` and pass it a left-to-right traversal +path of keys, indices or a combination of both (gross...but possible). This will +kick you back a function that behaves just like [`prop`](#prop). You pass it +some data, and it will attempt to resolve your provided path. If the path is +valid, it will return the value residing there (`null` included!) in a `Just`. +But if at any point that path "breaks" it will give you back a `Nothing`. + +```javascript +const composeK = require('crocks/helpers/composeK') +const isString = require('crocks/predicates/isString') +const propPath = require('crocks/Maybe/propPath') +const safe = require('crocks/Maybe/safe') + +// getFirstValue :: a -> Maybe b +const getFirstValue = + propPath([ 'value', 0 ]) + +// getStringFirst :: a -> Maybe String +const getStringFirst = composeK( + safe(isString), + getFirstValue +) + +getFirstValue({ value: [] }) +//=> Nothing + +getFirstValue({ value: 84 }) +//=> Nothing + +getFirstValue(undefined) +//=> Nothing + +getFirstValue({ value: [ 'a', 'b' ] }) +//=> Just "a" + +getStringFirst(false) +//=> Nothing + +getStringFirst({ towel: true }) +//=> Nothing + +getStringFirst({ value: [ 0, 54 ] }) +//=> Nothing + +getStringFirst({ value: [ 'nice', 'jobb' ] }) +//=> Just "nice" +``` + +#### safe + +`crocks/Maybe/safe` + +```haskell +safe :: ((b -> Boolean) | Pred) -> b -> Maybe a +``` + +When using a `Maybe`, it is a common practice to lift into a `Just` or a +`Nothing` depending on a condition on the value to be lifted. It is so common +that it warrants a function, and that function is called `safe`. Provide a +predicate (a function that returns a Boolean) and a value to be lifted. The +value will be evaluated against the predicate, and will lift it into a `Just` if +true and a `Nothing` if false. + +```javascript +const Pred = require('crocks/Pred') + +const isArray = require('crocks/predicates/isArray') +const safe = require('crocks/Maybe/safe') + +// length :: Array -> Number +const length = + x => x.length + +// lte2 :: Number -> Boolean +const lte2 = + x => x <= 2 + +// isSmall :: Pred a +const isSmall = + Pred(isArray) + .concat(Pred(lte2).contramap(length)) + +safe(lte2, 87) +//=> Nothing + +safe(lte2, 1) +//=> Just 1 + +safe(isArray, {}) +//=> Nothing + +safe(isArray, [ 1, 2, 3 ]) +//=> Just [ 1, 2, 3 ] + +safe(isSmall, [ 1, 2, 3 ]) +//=> Nothing + +safe(isSmall, { ar: [ 1, 2, 3 ] }) +//=> Nothing + +safe(isSmall, null) +//=> Nothing + +safe(isSmall, [ 1, 2 ]) +//=> Just [ 1, 2 ] +``` + +#### safeLift + +`crocks/Maybe/safeLift` + +```haskell +safeLift :: ((c -> Boolean) | Pred) -> (a -> b) -> c -> Maybe b +``` + +While [`safe`](#safe) is used to lift a value into a `Maybe`, you can reach for +`safeLift` when you want to run a function in the safety of the `Maybe` context. +Just like [`safe`](#safe), you pass it either a `Pred` or a predicate function +to determine if you get a `Just` or a `Nothing`, but then instead of a value, +you pass it a unary function. `safeLift` will then give you back a new function +that will first lift its argument into a `Maybe` and then maps your original +function over the result. + +```javascript +const Pred = require('crocks/Pred') + +const isNumber = require('crocks/predicates/isNumber') +const safeLift = require('crocks/Maybe/safeLift') + +// doubleOf :: Number -> Number +const doubleOf = + x => x * 2 + +// halfOf :: Number -> Number +const halfOf = + x => x / 2 + +// gt100 :: Number -> Boolean +const gt100 = + x => x > 100 + +// safeDouble :: a -> Maybe Number +const safeDouble = + safeLift(isNumber, doubleOf) + +// isLargeNumber :: Pred a +const isLargeNumber = + Pred(isNumber) + .concat(Pred(gt100)) + +// halfLarge :: a -> Maybe Number +const halfLarge = + safeLift(isLargeNumber, halfOf) + +safeDouble(null) +// Nothing + +safeDouble('33') +// Nothing + +safeDouble(33) +// Just 66 + +halfLarge('1000') +// Nothing + +halfLarge(100) +// Nothing + +halfLarge(false) +// Nothing + +halfLarge(786) +// Just 383 + +halfLarge(100) +// Nothing + +halfLarge(false) +// Nothing +``` + +## Transformation Functions + +#### eitherToMaybe + +`crocks/Maybe/eitherToMaybe` + +```haskell +eitherToMaybe :: Either b a -> Maybe a +eitherToMaybe :: (a -> Either c b) -> a -> Maybe b +``` + +Used to transform a given `Either` instance to a `Maybe` +instance, `eitherToMaybe` will turn a `Right` instance into a `Just` wrapping +the original value contained in the `Right`. All `Left` instances will map to +a `Nothing`, mapping the originally contained value to a `Unit`. Values on the +`Left` will be lost and as such this transformation is considered lossy in +that regard. + +Like all `crocks` transformation functions, `eitherToMaybe` has (2) possible +signatures and will behave differently when passed either an `Either` instance +or a function that returns an instance of `Either`. When passed the instance, +a transformed `Maybe` is returned. When passed an `Either` returning function, +a function will be returned that takes a given value and returns a `Maybe`. + +```javascript +const { Nothing, Just } = require('crocks/Maybe') +const { Left, Right } = require('crocks/Either') +const eitherToMaybe = require('crocks/Maybe/eitherToMaybe') + +const constant = require('crocks/combinators/constant') +const ifElse = require('crocks/logic/ifElse') +const isNumber = require('crocks/predicates/isNumber') + +// someNumber :: a -> Either String Number +const someNumber = ifElse( + isNumber, + Right, + constant(Left('Nope')) +) + +eitherToMaybe(Left(56)) +//=> Nothing + +eitherToMaybe(Right('correct')) +//=> Just "correct" + +Just('ten') + .chain(eitherToMaybe(someNumber)) +//=> Nothing + +Nothing() + .chain(eitherToMaybe(someNumber)) +//=> Nothing + +Just(99) + .chain(eitherToMaybe(someNumber)) +//=> Just 99 +``` + +#### firstToMaybe + +`crocks/Maybe/firstToMaybe` + +```haskell +firstToMaybe :: First a -> Maybe a +firstToMaybe :: (a -> First b) -> a -> Maybe b +``` + +Used to transform a given `First` instance to a `Maybe` +instance, `firstToMaybe` will turn a non-empty instance into a `Just` wrapping +the original value contained within the `First`. All empty instances will map +to a `Nothing`. + +Like all `crocks` transformation functions, `firstToMaybe` has (2) possible +signatures and will behave differently when passed either a `First` instance +or a function that returns an instance of `First`. When passed the instance, +a transformed `Maybe` is returned. When passed a `First` returning function, +a function will be returned that takes a given value and returns a `Maybe`. + +```javascript +const { Nothing, Just } = require('crocks/Maybe') +const First = require('crocks/First') +const firstToMaybe = require('crocks/Maybe/firstToMaybe') + +const mconcat = require('crocks/helpers/mconcat') + +// firstValue :: [ a ] -> First a +const firstValue = + mconcat(First) + +firstToMaybe(First.empty()) +//=> Nothing + +firstToMaybe(First('winner')) +//=> Just "Winner" + +Nothing() + .chain(firstToMaybe(firstValue)) +//=> Nothing + +Just([]) + .chain(firstToMaybe(firstValue)) +//=> Nothing + +Just([ 'first', 'second', 'third' ]) + .chain(firstToMaybe(firstValue)) +//=> Just "first" +``` + +#### lastToMaybe + +`crocks/Maybe/lastToMaybe` + +```haskell +lastToMaybe :: Last a -> Maybe a +lastToMaybe :: (a -> Last b) -> a -> Maybe b +``` + +Used to transform a given `Last` instance to a `Maybe` instance, `lastToMaybe` +will turn a non-empty instance into a `Just` wrapping the original value +contained within the `Last`. All empty instances will map to a `Nothing`. + +Like all `crocks` transformation functions, `lastToMaybe` has (2) possible +signatures and will behave differently when passed either a `Last` instance +or a function that returns an instance of `Last`. When passed the instance, +a transformed `Maybe` is returned. When passed a `Last` returning function, +a function will be returned that takes a given value and returns a `Maybe`. + +```javascript +const { Nothing, Just } = require('crocks/Maybe') +const Last = require('crocks/Last') +const lastToMaybe = require('crocks/Maybe/lastToMaybe') + +const mconcat = require('crocks/helpers/mconcat') + +// lastValue :: [ a ] -> Last a +const lastValue = + mconcat(Last) + +lastToMaybe(Last.empty()) +//=> Nothing + +lastToMaybe(Last('the end')) +//=> Just "the end" + +Nothing() + .chain(lastToMaybe(lastValue)) +//=> Nothing + +Just([]) + .chain(lastToMaybe(lastValue)) +//=> Nothing + +Just([ 'first', 'second', 'third' ]) + .chain(lastToMaybe(lastValue)) +//=> Just "third" +``` + +#### resultToMaybe + +`crocks/Maybe/resultToMaybe` + +```haskell +resultToMaybe :: Result e a -> Maybe a +resultToMaybe :: (a -> Result e b) -> a -> Maybe b +``` + +Used to transform a given `Result` instance to a `Maybe` +instance, `resultToMaybe` will turn an `Ok` instance into a `Just` wrapping +the original value contained in the `Ok`. All `Err` instances will map to +a `Nothing`, mapping the originally contained value to a `Unit`. Values on the +`Err` will be lost and as such this transformation is considered lossy in +that regard. + +Like all `crocks` transformation functions, `resultToMaybe` has (2) 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 `Maybe` is returned. When passed a `Result` returning function, +a function will be returned that takes a given value and returns a `Maybe`. + +```javascript +const { Nothing, Just } = require('crocks/Maybe') +const { Err, Ok } = require('crocks/Result') +const resultToMaybe = require('crocks/Maybe/resultToMaybe') + +const identity = require('crocks/combinators/identity') +const tryCatch = require('crocks/Result/tryCatch') + +function datErrTho() { + throw new Error('something amiss') +} + +resultToMaybe(Err('this is bad')) +//=> Nothing + +resultToMaybe(Ok('this is great')) +//=> Just "this is great" + +Nothing() + .chain(resultToMaybe(identity)) +//=> Nothing + +Just('so good') + .chain(resultToMaybe(tryCatch(datErrTho))) +//=> Nothing + +Just('so good') + .chain(resultToMaybe(Ok)) +//=> Just "so good" +```