diff --git a/docs/src/pages/docs/crocks/Identity.md b/docs/src/pages/docs/crocks/Identity.md new file mode 100644 index 000000000..50987bdcf --- /dev/null +++ b/docs/src/pages/docs/crocks/Identity.md @@ -0,0 +1,342 @@ +--- +title: "Identity" +description: "Identity Crock" +layout: "guide" +weight: 60 +--- + +```haskell +Identity a +``` + +`Identity` is a `crock` that can be used to wrap a common interface around +existing Javascript types and functions. It maintains integrity by lifting +and applying functions and types as is, without adding any additional structure +or effects. By not applying and additional structure to existing functions, +`Identity` can be swapped in and out for other `Functor`s that do apply their +own structure and effects. + +```javascript +import Identity from 'crocks/Identity' + +Identity(10) +//=> Identity 10 +``` +
+ +## Implements +`Setoid`, `Semigroup`, `Functor`, `Traversable`, `Apply`, `Chain`, `Applicative`, `Monad` + +
+ +
+ +## Construction + +```haskell +Identity :: a -> Identity a +``` + +The contstructor for an `Identity` is a unary function. When a value is passed +in an `Identity` of the given value is returned ready for `map` or `chain`. + +```javascript +import Identity from 'crocks/Identity' + +const fromComputerCode = String.fromCharCode + +Identity(42) + .map(fromComputerCode) +//=> Identity '*' +``` + +
+ +
+ +## Constructor Methods + +#### of + +```haskell +Identity.of :: a -> Identity a +``` + +`of` is used to construct an `Identity` with any given value. It is there to +allow `Identity` to work as a pointed functor. + +```javascript +import Identity from 'crocks/Identity' + +Identity.of(42) +//=> Identity 42 + +Identity.of(true) +//=> Identity true +``` + +
+ +
+ +## Instance Methods + +#### equals + +```haskell +Identity a ~> b -> Boolean +``` + +Used to compare the underlying values of two `Identity` instances for equality by +value, `equals` takes any given argument and returns `true` if the passed +arguments is an `Identity` with an underlying value equal to the underlying value +of the `Identity` the method is being called on. If the passed argument is not +an `Identity` or the underlying values are not equal, `equals` will return `false`. + +```javascript +import Identity from 'crocks/Identity' + +import equals from 'crocks/pointfree/equals' + +Identity(33) + .equals(Identity(33)) +//=> true + +Identity(33) + .equals(Identity('33')) +//=> false + +// by value, not reference for most types +Identity({ a: 86, b: true }) + .equals(Identity({ a: 86, b: true })) +//=> true + +equals(Identity(95), 95) +//=> false + +equals(Identity([ 2, 3 ]), Identity([ 2, 3 ])) +//=> true +``` + +#### concat + +```haskell +Identity s => Identity s ~> Identity s -> Identity s +``` + +When an underlying value of a given `Identity` is fixed to a `Semigroup`, +`concat` can be used to concat another `Identity` instance with an underlying +`Semigroup` of the same type. Expecting an `Identity` wrapping a `Semigroup` of +the same type, `concat` will give back a new `Identity` instance wrapping the +result of combining the two underlying `Semigroup`s. + +```javascript +import Identity from 'crocks/Identity' + +import Sum from 'crocks/Sum' + +import compose from 'crocks/helpers/compose' +import concat from 'crocks/pointfree/concat' +import flip from 'crocks/combinators/flip' +import map from 'crocks/pointfree/map' +import mapReduce from 'crocks/helpers/mapReduce' +import valueOf from 'crocks/pointfree/valueOf' + +// empty :: Identity Sum +const empty = + Identity(Sum.empty()) + +// sumList :: [ * ] -> Identity Number +const sumList = compose( + map(valueOf), + mapReduce(compose(Identity, Sum), flip(concat), empty) +) + +Identity([ 34 ]) + .concat(Identity([ 92 ])) +//=> Identity [ 34, 92 ] + +sumList([ 3, 4, 5 ]) +//=> Identity 12 +``` + +#### map + +```haskell +Identity a ~> (a -> b) -> Identity b +``` + +Used to apply transformations to values you've lifted into an `Identity`, `map` +takes a function that it will lift into the context of the `Identity` and apply +to it the wrapped value. `Identity` contains no behavior and will do nothing +more than apply the value inside the `Identity` to the function. + +```javascript +import Identity from 'crocks/Identity' +import map from 'crocks/pointfree/map' + +const prod = a => b => a * b + +const mapDouble = map(prod(2)) + +mapDouble(Identity(5)) +//=> Identity 10 +``` + +#### ap + +```haskell +Identity (a -> b) ~> Identity a -> Identity b +``` + +`ap` allows for values wrapped in an `Identity` to be applied to functions also +wrapped in an `Identity`. In order to use `ap`, the `Identity` must contain a +function as its value. Under the hood, `ap` unwraps both the function +and the value to be applied and applies the value to the function. Finally it +will wrap the result of that application back into an `Identity`. It is required +that the inner function is curried. + +```javascript +import Identity from 'crocks/Identity' + +const prod = a => b => a * b +const double = prod(2) + +Identity(double) + .ap(5) +//=> Identity 10 +``` + +#### sequence + +```haskell +Apply f => Identity (f a) ~> (b -> f b) -> f (Identity a) +Applicative f => Identity (f a) ~> TypeRep f -> f (Identity a) +``` + +When an instance of `Identity` wraps an `Apply` instance, `sequence` can be used to +swap the type sequence. `sequence` requires either an `Applicative TypeRep` or +an `Apply` returning function to be provided for its argument. + +`sequence` can be derived from [`traverse`](#traverse) by passing it an +`identity` function (`x => x`). + +```javascript +import Identity from 'crocks/Identity' + +import Maybe from 'crocks/Maybe' +import sequence from 'crocks/pointfree/sequence' + +// seqId :: Identity Maybe a -> Maybe Identity a +const seqMaybe = + sequence(Maybe) + +seqMaybe(Identity(Maybe(42))) +//=> Just Identity 42 +``` + +#### traverse + +```haskell +Apply f => Identity a ~> (a -> f b) -> f (Identity b) +Applicative f => Identity a ~> (TypeRep f, (a -> f b)) -> f (Identity a b) +``` + +Used to apply the "effect" of an `Apply` to a value inside of a `Identity`, +`traverse` combines both the "effects" of the `Apply` and the `Identity` by +returning a new instance of the `Apply`, wrapping the result of the +`Apply`s "effect" on the value in the `Identity`. + +`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 `Identity`. Both arguments must provide +an instance of the target `Apply`. + +```javascript +import Identity from 'crocks/Identity' +import IO from 'crocks/IO' + +import compose from 'crocks/helpers/compose' +import isNumber from 'crocks/predicates/isNumber' +import traverse from 'crocks/pointfree/traverse' +import ifElse from 'crocks/logic/ifElse' + +// someGlobal :: Number +let someGlobal = 10 + +// addToGlobal :: Number -> IO Number +const addToGlobal = x => IO(() => someGlobal + x) + +// safeAddToGlobal :: a -> IO (Maybe Number) +const safeAddToGlobal = compose( + traverse(IO, addToGlobal), + Identity, + ifElse(isNumber, x => x, () => NaN) +) + +safeAddToGlobal(32) + .run() +//=> Identity 42 +//someGlobal => 42 + +safeAddToGlobal(32) + .run() + .valueOf() +//=> 42 + +safeAddToGlobal(undefined) + .run() + .valueOf() +//=> NaN +``` + +#### chain + +```haskell +Identity a ~> (a -> Identity b) -> Identity b +``` + +Normally one of the ways `Monad`s like `Identity` are able to be combined and +have their effects applied is through `chain`. However `Identity` is different +because there are no effects to apply. `chain` will simply take a function that +returns `Identity` and applies it to its value. + +```javascript +import Identity from 'crocks/Identity' +import compose from 'crocks/helpers/compose' +import chain from 'crocks/pointfree/chain' + +const prod = a => b => a * b +const doubleAsIdentity = compose(Identity, prod(2)) + +doubleAsIdentity(21) +//=> Identity 42 + +chain(doubleAsIdentity, Identity(5)) +//=> Identity 10 +``` + +#### valueOf + +```haskell +Identity a ~> () -> a +``` + +`valueOf` is used as a means of extraction. This function is used primarily for +convenience for some of the helper functions that ship with `crocks`. Calling +`valueOf` on an `Identity` instance will return the value being contained. + +```javascript +import Identity from 'crocks/Identity' + +Identity(42) + .valueOf() +//=> 33 + +Identity(20) + .concat(Identity(22)) + .valueOf() +//=> 35 +``` + +
diff --git a/docs/src/pages/docs/crocks/Maybe.md b/docs/src/pages/docs/crocks/Maybe.md index 0c188783b..e5b04b3a1 100644 --- a/docs/src/pages/docs/crocks/Maybe.md +++ b/docs/src/pages/docs/crocks/Maybe.md @@ -730,7 +730,6 @@ safeAddToGlobal(32) safeAddToGlobal(undefined) .run() //=> Nothing -//someGlobal => 42 ``` #### chain diff --git a/docs/src/pages/docs/crocks/Pair.md b/docs/src/pages/docs/crocks/Pair.md index 0b0377fc9..e10754936 100644 --- a/docs/src/pages/docs/crocks/Pair.md +++ b/docs/src/pages/docs/crocks/Pair.md @@ -480,7 +480,7 @@ flow(pair) #### traverse ```haskell -Apply f => Pair a b ~> (d -> f d), (b -> f c)) -> f (Pair a c) +Apply f => Pair a b ~> ((d -> f d), (b -> f c)) -> f (Pair a c) Applicative f => Pair a b ~> (TypeRep f, (b -> f c)) -> f (Pair a c) ``` diff --git a/docs/src/pages/docs/crocks/index.md b/docs/src/pages/docs/crocks/index.md index ffca0caf3..515fb1b82 100644 --- a/docs/src/pages/docs/crocks/index.md +++ b/docs/src/pages/docs/crocks/index.md @@ -22,7 +22,7 @@ names, but what they do from type to type may vary. | [`Const`][const] | [`empty`][const-empty], [`of`][const-of] | [`ap`][const-ap], [`concat`][const-concat], [`empty`][const-empty], [`equals`][const-equals], [`map`][const-map], [`of`][const-of], [`valueOf`][const-valueof] | | [`Either`][either] | [`Left`][either-left], [`Right`][either-right], [`of`][either-of]| [`alt`][either-alt], [`ap`][either-ap], [`bimap`][either-bimap], [`chain`][either-chain], [`coalesce`][either-coalesce], [`concat`][either-concat], [`either`][either-either], [`equals`][either-equals], [`map`][either-map], [`of`][either-of], [`sequence`][either-sequence], [`swap`][either-swap], [`traverse`][either-traverse] | | [`Equiv`][equiv] | [`empty`][equiv-empty] | [`concat`][equiv-concat], [`contramap`][equiv-contra], [`compareWith`][equiv-compare], [`valueOf`][equiv-value] | -| `Identity` | `of` | `ap`, `chain`, `concat`, `equals`, `map`, `of`, `sequence`, `traverse`, `valueOf` | +| [`Identity`][identity] | [`of`][identity-of] | [`ap`][identity-ap], [`chain`][identity-chain], [`concat`][identity-concat], [`equals`][identity-equals], [`map`][identity-map], [`of`][identity-of], [`sequence`][identity-sequence], [`traverse`][identity-traverse], [`valueOf`][identity-valueof] | | `IO` | `of` | `ap`, `chain`, `map`, `of`, `run` | | `List` | `empty`, `fromArray`, `of` | `ap`, `chain`, `concat`, `cons`, `empty`, `equals`, `filter`, `fold`, `foldMap`, `head`, `map`, `of`, `reduce`, `reduceRight`, `reject`, `sequence`, `tail`, `toArray`, `traverse`, `valueOf` | | [`Maybe`][maybe] | [`Nothing`][maybe-nothing], [`Just`][maybe-just], [`of`][maybe-of], [`zero`][maybe-zero] | [`alt`][maybe-alt], [`ap`][maybe-ap], [`chain`][maybe-chain], [`coalesce`][maybe-coalesce], [`concat`][maybe-concat], [`equals`][maybe-equals], [`either`][maybe-either], [`map`][maybe-map], [`of`][maybe-of], [`option`][maybe-option], [`sequence`][maybe-sequence], [`traverse`][maybe-traverse], [`zero`][maybe-zero] | @@ -96,6 +96,18 @@ names, but what they do from type to type may vary. [either-swap]: Either.html#swap [either-traverse]: Either.html#traverse +[identity]: Identity.html +[identity-of]: Identity.html#of +[identity-alt]: Identity.html#alt +[identity-ap]: Identity.html#ap +[identity-chain]: Identity.html#chain +[identity-concat]: Identity.html#concat +[identity-equals]: Identity.html#equals +[identity-map]: Identity.html#map +[identity-sequence]: Identity.html#sequence +[identity-traverse]: Identity.html#traverse +[identity-valueof]: Identity.html#valueof + [equiv]: Equiv.html [equiv-empty]: Equiv.html#empty [equiv-concat]: Equiv.html#concat