From 25bf0879f00252b08fa2a4b22e02fc3f57b69a58 Mon Sep 17 00:00:00 2001 From: "Ian Hofmann-Hicks (evil)" Date: Mon, 1 Jan 2018 13:09:08 -0800 Subject: [PATCH] add crocks and fixup docs some more --- docs/electric.config.js | 4 + docs/src/layouts/guide.soy | 2 +- docs/src/layouts/notopic.soy | 73 ++ docs/src/pages/docs/crocks/Arrow.md | 509 +++++++++++ docs/src/pages/docs/crocks/Equiv.md | 372 ++++++++ docs/src/pages/docs/crocks/Pred.md | 402 +++++++++ docs/src/pages/docs/crocks/Reader.md | 837 ++++++++++++++++++ docs/src/pages/docs/crocks/State.md | 695 +++++++++++++++ .../docs/{api/crocks.md => crocks/index.md} | 7 +- .../docs/{api => functions}/combinators.md | 28 +- .../pages/docs/{api => functions}/helpers.md | 299 +++---- .../pages/docs/{api => functions}/index.md | 21 +- .../{api => functions}/logic-functions.md | 5 +- .../{api => functions}/pointfree-functions.md | 7 +- .../{api => functions}/predicate-functions.md | 51 +- .../transformation-functions.md | 7 +- docs/src/pages/docs/getting-started.md | 6 +- docs/src/pages/docs/monoids/All.md | 146 +++ docs/src/pages/docs/monoids/Any.md | 149 ++++ .../docs/{api/monoids.md => monoids/index.md} | 5 +- docs/src/pages/index.soy | 16 - 21 files changed, 3390 insertions(+), 251 deletions(-) create mode 100644 docs/src/layouts/notopic.soy create mode 100644 docs/src/pages/docs/crocks/Arrow.md create mode 100644 docs/src/pages/docs/crocks/Equiv.md create mode 100644 docs/src/pages/docs/crocks/Pred.md create mode 100644 docs/src/pages/docs/crocks/Reader.md create mode 100644 docs/src/pages/docs/crocks/State.md rename docs/src/pages/docs/{api/crocks.md => crocks/index.md} (98%) rename docs/src/pages/docs/{api => functions}/combinators.md (93%) rename docs/src/pages/docs/{api => functions}/helpers.md (75%) rename docs/src/pages/docs/{api => functions}/index.md (68%) rename docs/src/pages/docs/{api => functions}/logic-functions.md (98%) rename docs/src/pages/docs/{api => functions}/pointfree-functions.md (99%) rename docs/src/pages/docs/{api => functions}/predicate-functions.md (74%) rename docs/src/pages/docs/{api => functions}/transformation-functions.md (99%) create mode 100644 docs/src/pages/docs/monoids/All.md create mode 100644 docs/src/pages/docs/monoids/Any.md rename docs/src/pages/docs/{api/monoids.md => monoids/index.md} (97%) diff --git a/docs/electric.config.js b/docs/electric.config.js index ad0789394..beec4c7c9 100644 --- a/docs/electric.config.js +++ b/docs/electric.config.js @@ -16,6 +16,10 @@ module.exports = { basePath: '' } }, + markdownOptions: { + html: true, + linkify: true + }, metalComponents: [ 'electric-marble-components', 'marble-topbar' diff --git a/docs/src/layouts/guide.soy b/docs/src/layouts/guide.soy index 3ebff15f6..cf1359202 100644 --- a/docs/src/layouts/guide.soy +++ b/docs/src/layouts/guide.soy @@ -37,7 +37,7 @@
-

{$page.title} Guide

+

{$page.title}

diff --git a/docs/src/layouts/notopic.soy b/docs/src/layouts/notopic.soy new file mode 100644 index 000000000..bf44efae7 --- /dev/null +++ b/docs/src/layouts/notopic.soy @@ -0,0 +1,73 @@ +{namespace notopic} + +/** + * @param page + * @param site + */ +{template .render} +
+
+
+ {call Topbar.render} + {param items: $site.topbar /} + {param style: 'topbar-light topbar-docs' /} + {param logo: [ + 'text': $site.title, + 'icon': $site.icon, + 'href': $site.basePath + ] /} + {/call} + {call Sidebar.render} + {param section: $site.index.children['docs'] /} + {param site: $site /} + {/call} + {call .notopic data="all" /} +
+
+
+{/template} + +/** + * @param page + * @param site + * @param content + */ +{template .notopic} + +{/template} + +/** + * @param page + * @param site + */ +{template .contribute} +
+
+ +
+
+

Contribute on Github! Edit this section.

+
+
+{/template} diff --git a/docs/src/pages/docs/crocks/Arrow.md b/docs/src/pages/docs/crocks/Arrow.md new file mode 100644 index 000000000..4233fb87d --- /dev/null +++ b/docs/src/pages/docs/crocks/Arrow.md @@ -0,0 +1,509 @@ +--- +title: "Arrow" +description: "Arrow Crock" +layout: "guide" +weight: 10 +--- + +```haskell +Arrow a b +``` + +`Arrow` is a `Profunctor` that lifts a function of type `a -> b` and allows for +lazy execution of the function. `Arrow` can be considered a `Strong Profunctor` +if the underlying data running throw the `Arrow` is a `Pair`, typically in the +form of `Arrow (Pair a c) (Pair b d)`. This will allow you to split execution +into two distinct paths, applying `Arrow` to a specific path. The parameters of +`Arrow` represent the function that it wraps, with the input being on the left, +and the output on the right. When an `Arrow` wraps an endomorphism, the signature +typically represents both the input and output. + +```javascript +const Arrow = require('crocks/Arrow') + +const chain = require('crocks/pointfree/chain') +const compose = require('crocks/helpers/compose') +const isString = require('crocks/predicates/isString') +const option = require('crocks/pointfree/option') +const prop = require('crocks/Maybe/prop') +const safe = require('crocks/Maybe/safe') + +// arrUpper :: Arrow String +const arrUpper = + Arrow(str => str.toUpperCase()) + +arrUpper + .runWith('nice') +//=> 'NICE' + +// getName :: a -> String +const getName = compose( + option('no name'), + chain(safe(isString)), + prop('name') +) + +// arrUpperName :: Arrow a String +const arrUpperName = + arrUpper + .contramap(getName) + +arrUpperName + .runWith({ name: 'joey' }) +//=> 'JOEY' + +arrUpperName + .runWith({ age: 23 }) +//=> 'NO NAME' + +arrUpperName + .runWith({ name: false }) +//=> 'NO NAME' +``` + +
+ +## Implements +`Semigroupoid`, `Category`, `Functor`, `Contravariant`, `Profunctor` + +
+ +
+ +## Constructor Methods + +#### id +```haskell +Arrow.id :: () -> Arrow a +``` + +`id` provides the identity for the `Arrow` in that when it is composed to either +the left or right side of a given function, it will essentially result in a +morphism that is, for all intents and purposes, the given function. For `Arrow`, +`id` is the simple `identity` function that echoes it's given argument +(`x => x`). As a convenience, `id` is also available on the `Arrow` instance. + +```javascript +const Arrow = require('crocks/Arrow') + +// arrId :: Arrow a +const id = Arrow.id() + +// arrow :: Arrow a String +const arrow = + Arrow(x => x.toString()) + +// left :: Arrow a String +const left = + id.compose(arrow) + +// right :: Arrow a String +const right = + arrow.compose(id) + +right.runWith(12) //=> '12' +left.runWith(12) //=> '12' +``` + + +#### type +```haskell +Arrow.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 Arrow = require('crocks/Arrow') +const Identity = require('crocks/Identity') + +const I = require('crocks/combinators/identity') +const isSameType = require('crocks/predicates/isSameType') + +Arrow.type() //=> "Arrow" + +isSameType(Arrow, Arrow(x => x + 3)) //=> true +isSameType(Arrow, Arrow) //=> true +isSameType(Arrow, Identity(0)) //=> false +isSameType(Arrow(I), Identity) //=> false +``` + +
+ +
+ +## Instance Methods + +#### both +```haskell +Pair p => Arrow a b ~> () -> Arrow (p a a) (p b b) +``` + +`both` allows for the mode of a given `Arrow` to switch to a manner that applies +itself to both slots of a `Pair` that is passed through the `Arrow`. As noted in +the type signature, `both` will give back an `Arrow` has a new signature that +utilizes a `Pair` on both sides. + +```javascript +const Arrow = require('crocks/Arrow') +const Pair = require('crocks/Pair') + +const merge = require('crocks/Pair/merge') + +// double :: Number -> Number +const double = + x => x * 2 + +// add :: (Number, Number) -> Number +const add = + (x, y) => x + y + +// arrDouble :: Arrow Number +const arrDouble = + Arrow(double) + +// arrDoubleAndAdd :: Arrow (Pair Number Number) Number +const arrDoubleAndAdd = + arrDouble + .both() + .map(merge(add)) + +arrDouble + .runWith(200) //=> 400 + +arrDoubleAndAdd + .runWith(Pair(200, 10)) //=> 420 +``` + +#### compose +```haskell +Arrow a b ~> Arrow b c -> Arrow a c +``` +`compose` allows you to compose (2) `Arrow`s together, resulting in a new +`Arrow` that is the result of the composition. + +```javascript +const Arrow = require('crocks/Arrow') + +const filter = require('crocks/pointfree/filter') +const map = require('crocks/pointfree/map') + +// arrFilterEven :: Arrow [ Number ] +const arrFilterEven = + Arrow(filter(x => !(x % 2))) + +// arrDoubleNumbers :: Arrow [ Number ] +const arrDoubleNumbers = + Arrow(map(x => x * 2)) + +// arrLength :: Arrow [ a ] -> Number +const arrLength = + Arrow(x => x.length) + +// arrDoubleEven :: Arrow [ Number ] +const arrDoubleEven = + arrFilterEven + .compose(arrDoubleNumbers) + +// arrEvenCount :: Arrow [ Number ] Number +const arrEvenCount = + arrFilterEven + .compose(arrLength) + +// data :: [ Number ] +const data = + [ 12, 2, 36, 35 ] + +arrDoubleEven + .runWith(data) +//=> [ 24, 4, 72 ] + +arrEvenCount + .runWith(data) +//=> 3 +``` + +#### contramap +```haskell +Arrow a b ~> (c -> a) -> Arrow c b +``` + +When using `contramap` on an `Arrow`, a function can be lifted that will map a +given type into the type required for the original `Arrow`'s input. This allows +for "adaption" of given `Arrow`'s input for better reuse. The resulting type of +the lifted function must match the input type of the `Arrow`. + +```javascript +const Arrow = require('crocks/Arrow') + +const chain = require('crocks/pointfree/chain') +const compose = require('crocks/helpers/compose') +const isNumber = require('crocks/predicates/isNumber') +const option = require('crocks/pointfree/option') +const prop = require('crocks/Maybe/prop') +const safe = require('crocks/Maybe/safe') + +// getValue :: (String, Number) -> a -> Number +const getValue = (key, def) => compose( + option(def), + chain(safe(isNumber)), + prop(key) +) + +// arrAdd10 :: Arrow Number +const arrAdd10 = + Arrow(x => x + 10) + +// arrAdd10Value :: Arrow Object Number +const arrAdd10Value = + arrAdd10 + .contramap(getValue('value', 0)) + +arrAdd10 + .runWith(23) +//=> 33 + +arrAdd10Value + .runWith({ value: 23 }) + //=> 33 + +arrAdd10Value + .runWith({ value: '23' }) + //=> 10 + +arrAdd10Value + .runWith({ num: '23' }) +//=> 10 +``` + +#### first +```haskell +Pair p => Arrow a b ~> () -> Arrow (p a c) (p b c) +``` + +When calling `first` on an `Arrow`, a new `Arrow` is returned that will expect +a `Pair` with the original input type in the first slot of the `Pair`. When run, +the `Arrow` will only be applied to the first slot in the `Pair`, leaving +the second slot untouched. + +```javascript +const Arrow = require('crocks/Arrow') +const branch = require('crocks/Pair/branch') + +// arrToUpper :: Arrow String +const arrToUpper = + Arrow(x => x.toUpperCase()) + +arrToUpper + .runWith('burrito bounce') +//=> 'BURRITO BOUNCE' + +// join :: Pair String -> Object +const join = p => ({ + original: p.snd(), + result: p.fst() +}) + +// flow :: Arrow String Object +const flow = + arrToUpper + .first() + .promap(branch, join) + +flow + .runWith('taco time') +//=> { original: 'taco time', result: 'TACO TIME' } +``` + +#### map +```haskell +Arrow a b ~> (b -> c) -> Arrow a c +``` + +`map` allows a function to be lifted that will map the right side of the +`Arrow`. Where `contramap` is used to map the input, `map` maps the result +of the `Arrow`, allowing the result to be "adapted" or modified. The input type +to the lifted function must match the result the `Arrow`. + +```javascript +const Arrow = require('crocks/Arrow') + +const B = require('crocks/combinators/composeB') + +// arrFullScale :: Arrow Number +const arrFullScale = + Arrow(x => 20 * Math.log10(Math.abs(x))) + +arrFullScale + .runWith(-0.35) +//=> -9.118639112994488 + +// round :: Number -> Number +const round = + x => Math.floor(x * 100) / 100 + +// stringRep :: Number -> String +const stringRep = + x => `${x} dBFS` + +// Arrow :: Number String +const arrStringFS = + arrFullScale + .map(B(stringRep, round)) + +arrStringFS + .runWith(0.35) +//=> '-9.12 dbFS' +``` + +#### promap +```haskell +Arrow a b ~> ((c -> a), (b -> d)) -> Arrow c d +``` + +`promap` can be used to adapt BOTH ends of an `Arrow` allowing for existing +`Arrow`s to be reused in places in a flow where the types do not line up. It +combines both `map` and `conramap` into one operation. Just pass the `contramap` +function as the first argument and the `map` function as the second. + +```javascript +const Arrow = require('crocks/Arrow') + +const chain = require('crocks/pointfree/chain') +const compose = require('crocks/helpers/compose') +const isString = require('crocks/predicates/isString') +const objOf = require('crocks/helpers/objOf') +const option = require('crocks/pointfree/option') +const prop = require('crocks/Maybe/prop') +const safe = require('crocks/Maybe/safe') + +// upperFirst :: String -> String +const upperFirst = x => + x.charAt(0) + .toUpperCase() + .concat(x.slice(1).toLowerCase()) + +// arrTitleize :: Arrow String +const arrTitleize = + Arrow(x => x.split(' ').map(upperFirst).join(' ')) + +arrTitleize + .runWith('tHis is siLLy') +//=> 'This Is Silly' + +// stringProp :: String -> Object -> String +const stringProp = key => compose( + option(''), + chain(safe(isString)), + prop(key) +) + +// arrTitleObject :: Arrow Object +const arrTitleObject = + arrTitleize + .promap(stringProp('title'), objOf('title')) + +arrTitleObject + .runWith({ title: 'SaY wHaT!?!' }) +// { title: 'Say What!?!' } + +arrTitleObject + .runWith({ title: true }) +// { title: '' } +``` + +#### runWith +```haskell +Arrow a b ~> a -> b +``` + +`Arrow`s are lazy to make combining and extending them easy. Once you have your +final computation built out and you are ready to execute it, all you have to +do is call `runWith` on it, passing in the argument you what to run it with. + +```javascript +const Arrow = require('crocks/Arrow') +const Sum = require('crocks/Sum') + +const branch = require('crocks/Pair/branch') +const merge = require('crocks/Pair/merge') +const mreduce = require('crocks/helpers/mreduce') + +// data :: [ Number ] +const data = + [ 35, 60, 22, 100 ] + +// arrLength :: Arrow [ a ] Number +const arrLength = + Arrow(x => x.length) + +arrLength + .runWith(data) +//=> 4 + +// arrSum :: Arrow [ Number ] Number +const arrSum = + Arrow(mreduce(Sum)) + +arrSum + .runWith(data) +//=> 217 + +// arrAvgList :: Arrow [ Number ] Number +const arrAvgList = + arrSum.first() + .compose(arrLength.second()) + .promap(branch, merge((x, y) => x / y)) + +arrAvgList + .runWith(data) +//=> 54.25 +``` + +#### second +```haskell +Pair p => Arrow a b ~> () -> Arrow (p c a) (p c b) +``` + +Used to apply a given `Arrow` over the second slot of a `Pair`, leaving the +first slot untouched. The input to the `Arrow` must match the expected type on +the second slot of the incoming `Pair`. + +```javascript +const Arrow = require('crocks/Arrow') + +const assign = require('crocks/helpers/assign') +const branch = require('crocks/Pair/branch') +const merge = require('crocks/Pair/merge') +const objOf = require('crocks/helpers/objOf') + +// names :: Object +const names = { + first: 'Joey', + last: 'Fella' +} + +// arrFull :: Arrow Object +const arrFull = + Arrow(({ first, last }) => `${first} ${last}`) + .map(objOf('full')) +//=> { full: 'Joey Fella' } + +// arrAddFull :: Arrow Object +const arrAddFull = + arrFull + .second() + .promap(branch, merge(assign)) + +arrAddFull + .runWith(names) +//=> { full: 'Joey Fella', first: 'Joey', last: 'Fella' } +``` + +
diff --git a/docs/src/pages/docs/crocks/Equiv.md b/docs/src/pages/docs/crocks/Equiv.md new file mode 100644 index 000000000..67d2f673b --- /dev/null +++ b/docs/src/pages/docs/crocks/Equiv.md @@ -0,0 +1,372 @@ +--- +title: "Equiv" +description: "Equiv Crock" +layout: "guide" +weight: 50 +--- + +```haskell +Equiv a a Boolean +``` + +Defined as a Monoidal Contravariant datatype, `Equiv` can be used to test +equivalence between (2) values of a given type. It does this by wrapping a +binary equivalence function of the form `(a, a) -> Boolean`. Most of the time +strict equality is used, but other functions of the required form can provide +some powerful results. + +While the far right parameter is always fixed to `Boolean` it cannot be +Covariant, but is Contravariant allowing both inputs to vary in their type. +`Equiv` is also a `Monoid` and will concat the results of (2) `Equiv`s under +logical conjunction, with it's empty value always returning `true`. + +As `Equiv` wraps a function, it is lazy and a given instance will not produce +a result until both arguments are satisfied. A given instance can be run by +calling the method [`compareWith`](#comparewith), providing both values for +comparison. + +```javascript +const Equiv = require('crocks/Equiv') + +const equals = require('crocks/pointfree/equals') + +// toString :: a -> String +const toString = + x => x.toString() + +// length :: a -> Number +const length = x => + x && x.length ? x.length : 0 + +// eq :: Equiv a a +const eq = + Equiv(equals) + +eq.contramap(toString) + .compareWith('123', 123) +//=> true + +eq.contramap(length) + .compareWith([ 1, 2, 3 ], [ 'a', 'b' ]) +//=> false +``` + +
+ +## Implements +`Semigroup`, `Monoid`, `Contravariant` + +
+ +
+ +## Constructor Methods + +#### empty +```haskell +Equiv.empty :: () -> Equiv a a +``` + +`empty` provides the identity for the `Monoid` in that when the value it +provides is `concat`ed to any other value, it will return the other value. In +the case of `Equiv` the result of `empty` is an `Equiv` that will always return +`true`. `empty` is available on both the Constructor and the Instance for +convenience. + +```javascript +const Equiv = require('crocks/Equiv') + +const equals = require('crocks/pointfree/equals') + +const eq = + Equiv(equals) + +const empty = + Equiv.empty() + +eq + .concat(empty) + .compareWith({ a: 32 }, { a: 32 }) +//=> true + +empty + .concat(eq) + .compareWith({ a: 32 }, { a: 32 }) +//=> true + +empty + .concat(eq) + .compareWith({ a: 32, b: 19 }, { a: 32 }) +//=> false +``` + +#### type +```haskell +Equiv.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 Equiv = require('crocks/Equiv') + +const Endo = require('crocks/Endo') +const equals = require('crocks/pointfree/equals') +const isSameType = require('crocks/predicates/isSameType') + +Equiv.type() //=> "Equiv" + +isSameType(Equiv, Equiv(equals)) //=> true +isSameType(Equiv, Equiv) //=> true +isSameType(Equiv, Endo(x => x * 2)) //=> false +isSameType(Equiv(equals), Endo) //=> false +``` + +
+ +
+ +## Instance Methods + +#### concat +```haskell +Equiv a a ~> Equiv a a -> Equiv a a +``` + +`concat` is used to combine (2) `Semigroup`s of the same type under an operation +specified by the `Semigroup`. In the case of `Equiv`, the results of both +`Equiv`s are combined under logical conjunction. + +```javascript +const Equiv = require('crocks/Equiv') + +const compareWith = require('crocks/pointfree/compareWith') +const equals = require('crocks/pointfree/equals') +const isSameType = require('crocks/predicates/isSameType') +const propOr = require('crocks/helpers/propOr') + +// objLength :: Object -> Number +const objLength = + x => Object.keys(x).length + +// eq :: Equiv a a +const eq = + Equiv(equals) + +// sameType :: Equiv a a +const sameType = + Equiv(isSameType) + +// sameType :: Equiv Object Object +const length = + eq.contramap(objLength) + +// sameType :: Equiv a a +const sameTypeProp = key => + sameType.contramap(propOr(null, key)) + +// run :: Equiv Object Object +const run = compareWith( + { a: 19, b: 'string' }, + { a: 32, c: false } +) + +run(length) +//=> true + +run(sameTypeProp('a')) +//=> true + +run(sameTypeProp('b')) +//=> false + +run( + sameTypeProp('a') + .concat(length) +) +// true + +run( + sameTypeProp('b') + .concat(length) +) +// false +``` + +#### contramap +```haskell +Equiv a a ~> (b -> a) -> Equiv b b +``` + +The far right parameter of `Equiv` fixed to `Boolean` which means we cannot map +the value as expected. However the left two parameters can vary, although they +must vary in the same manner. + +This is where `contramap` comes into play as it can be used to adapt an `Equiv` +of a given type to accept a different type or modify the value. Provide it a +function that has a return type that matches the input types of the `Equiv`. +This will return a new `Equiv` matching the input type of the provided +function. + +```javascript +const Equiv = require('crocks/Equiv') + +const equals = require('crocks/pointfree/equals') + +// length :: String -> Number +const length = + x => x.length + +// eq :: Equiv a a +const eq = + Equiv(equals) + +// sameLength :: Equiv String String +const sameLength = + eq.contramap(length) + +// sameAmplitude :: Equiv Float Float +const sameAmplitude = + eq.contramap(Math.abs) + +sameAmplitude + .compareWith(-0.5011, 0.5011) +//=> true + +sameAmplitude + .compareWith(-0.755, 0.8023) +//=> false + +sameLength + .compareWith('aBcD', '1234') +//=> true + +sameLength + .compareWith('AB', 'ABC') +//=> false +``` + +#### valueOf +```haskell +Equiv a a ~> () -> a -> a -> Boolean +``` + +`valueOf` is used on all `crocks` `Monoid`s as a means of extraction. While the +extraction is available, types that implement `valueOf` are not necessarily a +`Comonad`. This function is used primarily for convenience for some of the +helper functions that ship with `crocks`. Calling `valueOf` on an `Equiv` +instance will result in the underlying curried equivalence function. + +```javascript +const Equiv = require('crocks/Equiv') + +const compose = require('crocks/helpers/compose') +const equals = require('crocks/pointfree/equals') +const propOr = require('crocks/helpers/propOr') + +// toLower :: String -> String +const toLower = + x => x.toLowerCase() + +// length :: String -> String +const length = + x => x.length + +// lowerName :: Object -> String +const lowerName = + compose(toLower, propOr('', 'name')) + +// itemsLen :: Object -> Number +const itemsLen = + compose(length, propOr('', 'items')) + +// eq :: Equiv a a +const eq = + Equiv(equals) + +// checkName :: Equiv Object Object +const checkName = + eq.contramap(lowerName) + +// checkName :: Equiv Object Object +const checkItems = + eq.contramap(itemsLen) + +// test :: Object -> Object -> Boolean +const test = + checkName + .concat(checkItems) + .valueOf() + +test( + { name: 'Bob', items: [ 1, 2, 4 ] }, + { name: 'bOb', items: [ 9, 12, 9 ] } +) +//=> true +``` + +#### compareWith +```haskell +Equiv a a ~> a -> a -> Boolean +``` + +As `Equiv` wraps a function, it needs a means to be run with two values for +comparison. Instances provide a curried method called `compareWith` that takes +two values for comparison and will run them through the equivalence function, +returning the resulting `Boolean`. + +Due to the laziness of this type, complicated comparisons can be built out from +combining and mapping smaller, simpler units of equivalence comparison. + +```javascript +const Equiv = require('crocks/Equiv') + +// both :: Equiv Boolean Boolean +const both = + Equiv((x, y) => x && y) + +// isEven :: Number -> Boolean +const isEven = + x => x % 2 === 0 + +// isBig :: Number -> Boolean +const isBig = + x => x > 10 + +// bothEven :: Equiv Number Number +const bothEven = + both.contramap(isEven) + +// bothBig :: Equiv Number Number +const bothBig = + both.contramap(isBig) + +bothEven + .compareWith(12, 20) +//=> true + +bothEven + .compareWith(17, 20) +//=> false + +bothBig + .compareWith(17)(20) +//=> true + +bothBig + .compareWith(7)(20) +//=> false + +bothBig + .concat(bothEven) + .compareWith(8)(54) +//=> false +``` + +
diff --git a/docs/src/pages/docs/crocks/Pred.md b/docs/src/pages/docs/crocks/Pred.md new file mode 100644 index 000000000..639624672 --- /dev/null +++ b/docs/src/pages/docs/crocks/Pred.md @@ -0,0 +1,402 @@ +--- +title: "Pred" +description: "Pred Crock" +layout: "guide" +weight: 100 +--- + +```haskell +Pred a Boolean +``` + +Defined as a Monoidal Contravariant datatype, `Pred` wraps a predicate function +of the form `(a -> Boolean)`. + +The far right parameter of `Pred` is always fixed to the type of `Boolean`, so +the result of the wrapped predicate function can never be mapped. While the +right parameter is fixed, the input to the predicate can vary. + +Another property of `Pred` instances is that they can be combined using their +Monodial interface. Combining instances will result in a new instance that +returns the result of each `Pred` under logical conjunction. + +As `Pred` wraps a function, it is lazy and will not execute until its argument +is satisfied. A given instance is run but calling the [`runWith`](#runwith) +method, supplying it the argument to test. + +One of the features of `crocks` is the ability to use both normal predicate +functions and `Pred` instances interchangeably. For any `crocks` function that +takes a predicate, either a predicate function or a `Pred` instance can be used. + +This implementation of `Pred` was heavily inspired by [this article](https://medium.com/@drboolean/monoidal-contravariant-functors-are-actually-useful-1032211045c4#.polugsx2a). + +```javascript +const Pred = require('crocks/Pred') + +const isNumber = require('crocks/predicates/isNumber') +const propOr = require('crocks/helpers/propOr') +const filter = require('crocks/pointfree/filter') + +// largeNumber :: Pred Number +const largeNumber = + Pred(isNumber) + .concat(Pred(x => x > 100)) + +// largeItem :: Pred Object +const largeItem = + largeNumber + .contramap(propOr(null, 'item')) + +largeNumber + .runWith(45) +//=> false + +largeNumber + .runWith(175) +//=> true + +largeItem + .runWith({ item: 190 }) +//=> true + +largeItem + .runWith({ item: 9 }) +//=> false + +largeItem + .runWith(9) +//=> false + +filter(largeNumber, [ 200, 375, 15 ]) +//=> [ 200, 375 ] +``` + +
+ +## Implements +`Semigroup`, `Monoid`, `Contravariant` + +
+ +
+ +## Constructor Methods + +#### empty +```haskell +Pred.empty :: () -> Pred a +``` + +`empty` provides the identity for the `Monoid` in that when the value it +provides is `concat`ed to any other value, it will return the other value. In +the case of `Pred` the result of `empty` is a `Pred` that will always return +`true`. `empty` is available on both the Constructor and the Instance for +convenience. + +```javascript +const Pred = require('crocks/Pred') + +const isEmpty = require('crocks/predicates/isEmpty') +const not = require('crocks/logic/not') + +// empty :: Pred a +const empty = + Pred.empty() + +// notEmpty :: Pred a +const notEmpty = + Pred(not(isEmpty)) + +empty + .runWith('') +//=> true + +notEmpty + .concat(empty) + .runWith([]) +//=> false + +notEmpty + .concat(empty) + .runWith([ 1, 2, 3 ]) +//=> true + +empty + .concat(notEmpty) + .runWith('') +//=> false + +empty + .concat(notEmpty) + .runWith('123') +//=> true +``` + +#### type +```haskell +Pred.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 Pred = require('crocks/Pred') + +const Maybe = require('crocks/Maybe') +const isNil = require('crocks/predicates/isEmpty') +const isSameType = require('crocks/predicates/isSameType') + +Pred.type() //=> "Pred" + +isSameType(Pred, Pred(isNil)) //=> true +isSameType(Pred, Pred) //=> true +isSameType(Pred, Maybe.Just(4)) //=> false +isSameType(Pred(isNil), Maybe) //=> false +``` + +
+ +
+ +## Instance Methods + +#### concat +```haskell +Pred a ~> Pred a -> Pred a +``` + +`concat` is used to combine (2) `Semigroup`s of the same type under an operation +specified by the `Semigroup`. In the case of `Pred`, the results of both +`Preds`s are combined under logical conjunction. + +```javascript +const Pred = require('crocks/Pred') + +const or = require('crocks/logic/or') +const not = require('crocks/logic/not') +const filter = require('crocks/pointfree/filter') + +const isEven = + x => !(x % 2) + +// isOdd :: Pred Number +const isOdd = + Pred(not(isEven)) + +// lt20 :: Pred Number +const lt20 = + Pred(x => x < 20) + +// gt5 :: Pred Number +const gt5 = + Pred(x => x > 5) + +// inRange :: Pred Number +const inRange = + lt20.concat(gt5) + +// isOddInRange :: Pred Number +const isOddInRange = + isOdd.concat(inRange) + +// isValid :: Pred Number +const isValid = + Pred(or(isEven, isOddInRange)) + +// data :: [ Number ] +const data = +[ 1, 4, 12, 19, 32, 99, 76, 7 ] + +isOdd + .runWith(5) +//=> true + +isOdd + .runWith(8) +//=> false + +filter(isOdd, data) +//=> [ 1, 19, 99, 7 ] + +filter(lt20, data) +//=> [ 1, 4, 12, 19, 7 ] + +filter(gt5, data) +//=> [ 12, 19, 32, 99, 76, 7 ] + +filter(inRange, data) +//=> [ 12, 19, 7 ] + +filter(isOddInRange, data) +//=> [ 19, 7 ] + +filter(isEven, data) +// [ 4, 12, 32, 76 ] + +filter(isValid, data) +//=> [ 4, 12, 19, 32, 76, 7 ] +``` + +#### contramap +```haskell +Pred a ~> (b -> a) -> Pred b +``` + +While the output of a `Pred` is fixed to `Boolean`, the input can vary type and +value. This allows a given `Pred` to be adapted by mapping on the input, before +it hits the wrapped predicate function. Using `contramap`, functions are lifted, +mapping the input to now accept the type of the input of the given function. + +```javascript +const Pred = require('crocks/Pred') +const contramap = require('crocks/pointfree/contramap') +const propOr = require('crocks/helpers/propOr') + +// Length :: String | Function | Array +// length :: Length -> Number +const length = + propOr(0, 'length') + +// gt5 :: Pred Number +const gt5 = + Pred(x => x > 5) + +// lengthGt5 :: Pred Length +const validLength = + contramap(length, gt5) + +// validItemLength :: Pred Object +const validItemLength = + contramap(propOr(null, 'item'), validLength) + +gt5 + .runWith(5) +//=> false + +gt5 + .runWith(10) +//=> true + +validLength + .runWith([ 1, 2, 3, 4, 5, 6 ]) +//=> true + +validLength + .runWith(null) +//=> false + +validLength + .runWith('1234') +//=> false + +validItemLength + .runWith({ item: 'this is an item' }) +//=> true +``` + +#### valueOf +```haskell +Pred a ~> () -> a -> Boolean +``` + +`valueOf` is used on all `crocks` `Monoid`s as a means of extraction. While the +extraction is available, types that implement `valueOf` are not necessarily a +`Comonad`. This function is used primarily for convenience for some of the +helper functions that ship with `crocks`. + +Calling `valueOf` on a `Pred` instance will result in the underlying predicate +function. Most of the time this will not be required when working with `crocks` +because all `crocks` functions that take a predicate function can also take a +`Pred` instance. + +```javascript +const Pred = require('crocks/Pred') + +const isArray = require('crocks/predicates/isArray') +const isString = require('crocks/predicates/isString') +const equals = require('crocks/pointfree/equals') +const or = require('crocks/logic/or') + +// lengthIsThree :: Pred a +const lengthIsThree = + Pred(equals(3)) + .contramap(x => x.length) + +// pred :: Pred a +const pred = + Pred(or(isArray, isString)) + .concat(lengthIsThree) + +// fn :: a -> Boolean +const fn = + pred.valueOf() + +pred + .runWith(null) +//=> false + +pred + .runWith([ 1, 2, 3 ]) +//=> true + +pred + .runWith('This is fun') +//=> true + +fn(null) // false +fn([ 1, 2, 3 ]) // true +fn('This is fun') // true +fn([]) // false +fn('') // false +``` + +#### runWith +```haskell +Pred a ~> a -> Boolean +``` + +As `Pred` wraps a predicate function, it needs a mean to run it with some value +to test against the predicate. `Pred` instances provide a method called +`runWith` that will accept the value to be tested and then runs it through the +predicate returning the result. + +Most of the time this function is not used while working with other predicate +functions in `crocks`, as all functions that take a predicate function also +take a `Pred` instance. It does come in handy though when supplying predicates +to other libraries. + +```javascript +const Pred = require('crocks/Pred') + +const hasProp = require('crocks/predicates/hasProp') +const equals = require('crocks/pointfree/equals') +const flip = require('crocks/combinators/flip') +const runWith = require('crocks/pointfree/runWith') + +// trueBlue :: Pred Object +const trueBlue = + Pred(equals(true)) + .contramap(({ blue }) => blue) + +// isValid :: Pred a +const isValid = + Pred(hasProp('blue')) + .concat(trueBlue) + +// checkValid :: a -> Boolean +const checkValid = + flip(runWith, isValid) + +checkValid(null) //=> false +checkValid([ 1, 2, 3 ]) //=> false +checkValid({ blue: 32 }) //=> false +checkValid({ blue: true }) //=> true +``` + +
diff --git a/docs/src/pages/docs/crocks/Reader.md b/docs/src/pages/docs/crocks/Reader.md new file mode 100644 index 000000000..d1bc109f4 --- /dev/null +++ b/docs/src/pages/docs/crocks/Reader.md @@ -0,0 +1,837 @@ +--- +title: "Reader" +description: "Reader Crock" +layout: "guide" +weight: 110 +--- + +```haskell +Reader e a +``` + +`Reader` is a lazy Product Type that enables the composition of computations +that depend on a shared environment `(e -> a)`. The left portion, the `e` must +be fixed to a type for all related computations. The right portion `a` can vary +in its type. + +As `Reader` is lazy, wrapping a function of the form `(e -> a)`, nothing is +executed until it is run with an environment. `Reader` provides a method on +it's instance that will take an environment called `runWith` that will run +the instance with a given environment. + +Not only is `Reader`'s environment fixed to a type, but it should be immutable +for the "life" computation. If a referential type is used as the environment +great care should be taken to not modify the value of the environment. + +```javascript +const Reader = require('crocks/Reader') +const { ask } = Reader + +const concat = require('crocks/pointfree/concat') + +// greet :: String -> Reader String String +const greet = greeting => + Reader(name => `${greeting}, ${name}`) + +// addFarewell :: String -> Reader String String +const addFarewell = farewell => str => + ask(env => `${str}${farewell} ${env}`) + +// flow :: Reader String String +const flow = + greet('Hola') + .map(concat('...')) + .chain(addFarewell('See Ya')) + +flow + .runWith('Thomas') +// => Hola, Thomas...See Ya Thomas + +flow + .runWith('Jenny') +// => Hola, Jenny...See Ya Jenny +``` + +
+ +## Implements +`Functor`, `Apply`, `Chain`, `Applicative`, `Monad` + +
+ +
+ +## Constructor Methods + +#### ask +```haskell +Reader.ask :: () -> Reader e e +Reader.ask :: (e -> b) -> Reader e b +``` + +A construction helper that returns a `Reader` with the environment on the right +portion of the `Reader`. `ask` can take a function, that can be used to map the +environment to a different type or value. + +```javascript +const Reader = require('crocks/Reader') +const { ask } = Reader + +// add :: Number -> Number -> Number +const add = + x => y => x + y + +// Typical constructor +Reader(add(10)) + .runWith(56) +//=> 66 + +// Using `ask` with no function +// (identity on environment) +ask() + .runWith(56) +//=> 56 + +// Using `ask` with a function +// (map environment before deposit) +ask(add(10)) + .runWith(56) +//=> 66 +``` + +#### of +```haskell +Reader.of :: a -> Reader e a +``` + +`of` is used to construct a `Reader` with the right portion populated with it's +argument. `of` essentially will lift a value of type `a` into a `Reader`, giving +back a `Reader` that is "pointed" to the specific value provided. `of` makes +for a wonderful starting point for some of the more complicated flows. + +```javascript +const Reader = require('crocks/Reader') +const { ask } = Reader + +const objOf = require('crocks/helpers/objOf') +const thrush = require('crocks/combinators/reverseApply') + +// add :: Number -> Number -> Number +const add = + x => y => x + y + +Reader.of(34) + .map(add(6)) + .runWith() +//=> 40 + +Reader.of('Bobby') + .map(objOf('name')) + .runWith() +//=> { name: 'Bobby' } + +Reader.of(57) + .chain(x => ask(add).map(thrush(x))) + .runWith(43) +//=> 100 +``` + +#### type +```haskell +Reader.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 Reader = require('crocks/Reader') +const Identity = require('crocks/Identity') + +const I = require('crocks/combinators/identity') +const isSameType = require('crocks/predicates/isSameType') + +Reader.type() //=> 'Reader' + +isSameType(Reader, Reader.of(76)) //=> true +isSameType(Reader, Reader) //=> true +isSameType(Reader, Identity(0)) //=> false +isSameType(Reader(I), Identity) //=> false +``` + +
+ +
+ +## Instance Methods + +#### map +```haskell +Reader e a ~> (a -> b) -> Reader e b +``` + +While the left side, or the environment, of the `Reader` must always be fixed +to the same type, the right side, or value, of the `Reader` may vary. Using `map` +allows a function to be lifted into the `Reader`, mapping the result into the +result of the lifted function. + +```javascript +const Reader = require('crocks/Reader') +const { ask } = Reader + +const assign = require('crocks/helpers/assign') +const B = require('crocks/combinators/composeB') +const objOf = require('crocks/helpers/objOf') +const option = require('crocks/pointfree/option') +const prop = require('crocks/Maybe/prop') + +// length :: Array -> Number +const length = + x => x.length + +ask() + .map(length) + .runWith([ 1, 2, 3 ]) +//=> 3 + +// propOr :: (String, a) -> b -> a +const propOr = (key, def) => + B(option(def), prop(key)) + +// lengthObj :: Array -> Object +const lengthObj = + B(objOf('length'), length) + +// addLength :: Object -> Redaer Array Object +const addLength = x => + ask(propOr('list', [])) + .map(B(assign(x), lengthObj)) + +Reader.of({ num: 27 }) + .chain(addLength) + .runWith({ list: [ 1, 2, 3 ] }) +//=> { length: 3, num: 27 } +``` + +#### ap +```haskell +Reader e (a -> b) ~> Reader e a -> Reader e b +``` + +`ap` allows for values wrapped in a `Reader` to be applied to functions also +wrapped in a `Reader`. In order to use `ap`, the `Reader` 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 a `Reader`. It is required +that the inner function is curried. + +```javascript +const Reader = require('crocks/Reader') +const { ask } = Reader + +const B = require('crocks/combinators/composeB') +const assign = require('crocks/helpers/assign') +const liftA2 = require('crocks/helpers/liftA2') +const objOf = require('crocks/helpers/objOf') + +// namePart :: Number -> String -> String +const namePart = indx => x => + x.split(' ')[indx] || '' + +// combine :: Object -> Reader Object +const combine = + x => ask(assign(x)) + +// full :: Reader Object +const full = + ask(({ full }) => full) + +// first :: Reader Object +const first = + full + .map(B(objOf('first'), namePart(0))) + +// last :: Reader Object +const last = + full + .map(B(objOf('last'), namePart(1))) + +// fluent style +Reader.of(assign) + .ap(first) + .ap(last) + .chain(combine) + .runWith({ full: 'Mary Jones' }) +//=> { full: 'Mary Jones', first: 'Mary', last: 'Jones' } + +// liftAssign :: Reader Object -> Reader Object -> Reader Object +const liftAssign = + liftA2(assign) + +// using a lift function +liftAssign(first, last) + .chain(combine) + .runWith({ full: 'Tom Jennings' }) +//=> { full: 'Tom Jennings', first: 'Tom', last: 'Jennings' } +``` + +#### chain +```haskell +Reader e a ~> (a -> Reader e b) -> Reader e b +``` + +One of the ways `Monad`s like `Reader` are able to be combined and have their +effects applied, is by using the `chain` method. In the case of `Reader`, the +effect is to read in and make available the shared environment. `chain` expects +a function that will take any `a` and return a new `Reader` with a value of `b`. + +```javascript +const Reader = require('crocks/Reader') +const { ask } = Reader + +const B = require('crocks/combinators/composeB') +const option = require('crocks/pointfree/option') +const prop = require('crocks/Maybe/prop') + +// multiply :: Number -> Number -> Number +const multiply = + x => y => x * y + +// add :: Number -> Number -> Number +const add = + x => y => x + y + +// propOr :: (String, a) -> b -> a +const propOr = (key, def) => + B(option(def), prop(key)) + +// applyScale :: Number -> Reader Object Number +const applyScale = x => + ask(propOr('scale', 1)) + .map(multiply(x)) + +// applyScale :: Number -> Reader Object Number +const applyOffset = x => + ask(propOr('offset', 0)) + .map(add(x)) + +// applyTransforms :: Number -> Reader Object Number +const applyTransform = x => + Reader.of(x) + .chain(applyOffset) + .chain(applyScale) + +applyTransform(45) + .runWith({}) +//=> 45 + +applyTransform(45) + .runWith({ offset: 20 }) +//=> 65 + +applyTransform(45) + .runWith({ scale: 2 }) +//=> 90 + +applyTransform(45) + .runWith({ scale: 2, offset: 20 }) +//=> 130 +``` + +#### runWith +```haskell +Reader e a ~> e -> a +``` + +As `Reader` is a lazy datatype that requires a shared environment to run, it's +instance provides a `runWith` method that takes in an environment and returns +the result of the computation. + +```javascript +const { ask } = require('crocks/Reader') +const Pair = require('crocks/Pair') + +const fst = require('crocks/Pair/fst') +const liftA2 = require('crocks/helpers/liftA2') +const snd = require('crocks/Pair/snd') + +// data :: Pair Number Number +const data = + Pair(20, 45) + +// getCorrect :: Reader (Pair Number Number) Number +const getCorrect = + ask(fst) + +// getTotal :: Reader (Pair Number Number) Number +const getTotal = + ask(snd) + +// divide :: Number -> Number -> Number +const divide = + x => y => x / y + +// formatPercent :: Number -> String +const formatPercent = + x => `${Math.floor(x * 1000) / 10}%` + +// calcPercent :: Reader (Pair Number Number) String +const calcPercent = + liftA2(divide, getCorrect, getTotal) + .map(formatPercent) + +calcPercent + .runWith(data) +//=. '44.4%' +``` + +
+ +Monad Transformer +--- + +
+ +## ReaderT + +```haskell +Monad m => ReaderT e (m a) +``` + +`ReaderT` is a `Monad Transformer` that wraps a given `Monad` with a `Reader`. +This allows the interface of a `Reader` that enables the composition of +computations that depend on a shared environment `(e -> a)`, but provides a way +to abstract a means the `Reader` portion, when combining `ReaderT`s of the same +type. All `ReaderT`s must provide the constructor of the target `Monad` that is +being wrapped. + +
+ +
+ +## Constructor Methods + +#### ask +```haskell +ReaderT.ask :: Monad m => () -> ReaderT e (m e) +ReaderT.ask :: Monad m => (e -> a) -> ReaderT e (m a) +``` + +A construction helper that returns a `ReaderT` with environment on the right +portion of the `Reader`. `ask` can take a function, that can be used to map the +environment to a different type or value. When using the function version, the +function must return the type of the `Monad` the `ReaderT` wraps, which in turn +will be wrapped in another + +```javascript +const ReaderT = require('crocks/Reader/ReaderT') +const Maybe = require('crocks/Maybe') + +const safe = require('crocks/Maybe/safe') +const isNumber = require('crocks/predicates/isNumber') + +const MaybeReader = ReaderT(Maybe) +const { ask } = MaybeReader + +// add :: Number -> Number -> Number +const add = + x => y => x + y + +// Typical Constructor +MaybeReader(safe(isNumber)) + .runWith(76) +//=> Just 76 + +MaybeReader(safe(isNumber)) + .runWith('76') +//=> Nothing + +// Using `ask` with no function +// (identity on environment) +ask() + .runWith(76) +//=> Just 76 + +ask() + .runWith('76') +//=> Just '76' + +// Using `ask` with a function +// (map environment before deposit) +ask(add(10)) + .runWith(76) +//=> Just 86 +``` + +#### lift +```haskell +ReaderT.lift :: Monad m => m a -> ReaderT e (m a) +``` + +Used to promote an instance of a given `Monad` into a `ReaderT` of that `Monad`s +type. This can be used to lift a pointed instance of the underlying `Monad`. +When mixed with composition, `lift` can be used to promote functions that take +the form of `a -> m b` into a function that can be `chain`ed with the `ReaderT`. +Although, [`liftFn`](#liftfn) can be used to remove the composition boilerplate +and promote and `a -> m b` function. + +```javascript +const ReaderT = require('crocks/Reader/ReaderT') +const Async = require('crocks/Async') + +const compose = require('crocks/helpers/compose') +const curry = require('crocks/helpers/curry') +const flip = require('crocks/combinators/flip') +const runWith = require('crocks/pointfree/runWith') +const tap = require('crocks/helpers/tap') + +const AsyncReader = ReaderT(Async) + +const { ask, lift } = AsyncReader +const { Rejected } = Async + +// log :: String -> a -> () +const log = label => + console.log.bind(console, label + ':') + +// forkLog :: Async a b -> Async a b +const forkLog = tap( + m => m.fork(log('rej'), log('res')) +) + +// runAndLog :: e -> ReaderT e (Async a b) -> Async a b +const runAndLog = curry( + x => compose(forkLog, flip(runWith, x)) +) + +// instance :: ReaderT e (Async String a) +const instance = + lift(Rejected('Always Rejected')) + +runAndLog(instance, 'Thomas') +//=> rej: Always Rejected + +// Using in a composition +// rejectWith :: a -> ReaderT e (Async a b) +const rejectWith = + compose(lift, Rejected) + +// envReject :: ReadetT e (Async e b) +const envReject = + ask() + .chain(rejectWith) + +runAndLog(envReject, 'Sammy') +//=> rej: Sammy +``` + +#### liftFn +```haskell +ReaderT.liftFn :: Monad m => (a -> m b) -> a -> ReaderT e (m b) +``` + +Used to transform a given function in the form of `a -> m b` into a lifted +function, where `m` is the underlying `Monad` of a `ReaderT`. This allows for +the removal of composition boilerplate that results from using the +[`lift`](#lift) helper. + +```javascript +const ReaderT = require('crocks/Reader/ReaderT') +const Either = require('crocks/Either') + +const ifElse = require('crocks/logic/ifElse') + +const EitherReader = ReaderT(Either) + +const { ask, liftFn } = EitherReader +const { Left, Right } = Either + +// gte :: Number -> Number -> Either String Number +const gte = x => ifElse( + n => n >= x, + Right, + n => Left(`${n} is not gte to ${x}`) +) + +// gte10 :: Number -> Either String Number +const gte10 = + gte(10) + +// add20 :: ReaderT Number (Either String Number) +const add20 = + ask() + .chain(liftFn(gte10)) + .map(n => n + 20) + +add20 + .runWith(30) +//=> Right 50 + +add20 + .runWith(9) +//=> Left "9 is not gte to 10" +``` + +#### of +```haskell +ReaderT.of :: Monad m => a -> ReaderT e (m a) +``` + +Lifts a value into a `ReaderT` using the `of` method of the underlying `Monad`. +`of` will disregard the environment and points the right portion to the provided +value. + +```javascript +const ReaderT = require('../crocks/src/Reader/ReaderT') + +const Maybe = require('crocks/Maybe') +const Either = require('crocks/Either') +const State = require('crocks/State') + +const MaybeReader = ReaderT(Maybe) +const EitherReader = ReaderT(Either) +const StateReader = ReaderT(State) + +MaybeReader.of('yep') + .map(x => x.toUpperCase()) + .runWith(23) +//=> Just "YEP" + +EitherReader.of(43) + .runWith(23) +//=> Right 43 + +StateReader.of(0) + .runWith(23) + .runWith(42) +//=> Pair(0, 42) +``` + +
+ +
+ +## Instance Methods + +#### map +```haskell +Monad m => ReaderT e (m a) ~> (a -> b) -> ReaderT e (m b) +``` + +Provides a means for lifting a normal javascript function into the underlying +`Monad`, allowing the innermost value of the underlying `Monad` to be mapped. +This method will ignore the outer `ReaderT`, and be applied directly to the +underlying `Monad`. + +```javascript +const ReaderT = require('crocks/Reader/ReaderT') +const Maybe = require('crocks/Maybe') + +const isString = require('crocks/predicates/isString') +const safe = require('crocks/Maybe/safe') + +const MaybeReader = + ReaderT(Maybe) + +const { ask, liftFn } = MaybeReader + +// maybeString :: a -> Maybe String +const maybeString = + safe(isString) + +// toUpper :: String -> String +const toUpper = + x => x.toUpperCase() + +// envToUpper :: ReaderT e (Maybe String) +const envToUpper = + ask() + .chain(liftFn(maybeString)) + .map(toUpper) + +envToUpper + .runWith(4) +//=> Nothing + +envToUpper + .runWith('hola') +//=> Just "HOLA" + +``` + +#### ap +```haskell +Monad m => ReaderT e (m (a -> b)) ~> ReaderT e (m a) -> ReaderT e (m b) +``` + +Applies wrapped functions to the provided value, using the `ap` of the +underlying `Monad`. A `ReaderT` of the underlying `Monad` must be provided, +which allows access to the environment. + +```javascript +const Pair = require('crocks/Pair') +const ReaderT = require('crocks/Reader/ReaderT') +const Result = require('crocks/Result') + +const fst = require('crocks/Pair/fst') +const snd = require('crocks/Pair/snd') + +const ifElse = require('crocks/logic/ifElse') +const isNumber = require('crocks/predicates/isNumber') +const liftA2 = require('crocks/helpers/liftA2') + +const { Err, Ok } = Result + +const ResultReader = + ReaderT(Result) + +const { ask, liftFn } = ResultReader + +// add :: Number -> Number -> Number +const add = + x => y => x + y + +// makeError :: a -> Result [ String ] b +const makeErr = + x => Err([ `${x} is not a Number` ]) + +// isValid :: a -> ReaderT e (Result [ String ] Number) +const isValid = liftFn( + ifElse(isNumber, Ok, makeErr) +) + +// first :: ReaderT (Pair a b) (Result [ String ] Number) +const first = + ask(fst) + .chain(isValid) + +// second :: ReaderT (Pair a b) (Result [ String ] Number) +const second = + ask(snd) + .chain(isValid) + +// Using a fluent style with of +ResultReader.of(add) + .ap(first) + .ap(second) + .runWith(Pair(34, 21)) +//=> Ok 55 + +// Using a fluent style with map +first + .map(add) + .ap(second) + .runWith(Pair(true, 21)) +//=> Err [ "true is not a Number" ] + +// Using liftA2 +liftA2(add, first, second) + .runWith(Pair('Bob', 'Jones')) +//=> Err [ 'Bob is not a Number', 'Jones is not a Number' ] +``` + +#### chain +```haskell +Monad m => ReaderT e (m a) ~> Reader e (a -> ReaderT e (m b)) -> ReaderT e (m b) +``` + +Can be used to apply the effects of the underlying `Monad` with the benefit of +being able to read from the environment. This method only accepts functions +of the form `Monad m => a -> ReaderT e (m b)`. + +```javascript +const ReaderT = require('../crocks/src/Reader/ReaderT') +const Maybe = require('crocks/Maybe') +const prop = require('crocks/Maybe/prop') + +const MaybeReader = + ReaderT(Maybe) + +const { ask, liftFn } = MaybeReader + +// readProp :: String -> b -> ReaderT e (Maybe a) +const readProp = key => + liftFn(prop(key)) + +// getName :: ReaderT e (Maybe a) +const getName = + ask() + .chain(readProp('name')) + +// getFirstName :: ReaderT e (Maybe a) +const getFirstName = + getName + .chain(readProp('first')) + +// getLastName :: ReaderT e (Maybe a) +const getLastName = + getName + .chain(readProp('last')) + +// person :: Object +const person = { + name: { + first: 'Hazel', + middle: 'Anne' + } +} + +getFirstName + .runWith(person) +//=> Just "Hazel" + +getLastName + .runWith(person) +//=> Nothing + +getLastName + .runWith(10) +//=> Nothing +``` + +#### runWith +```haskell +Monad m => ReaderT e (m a) ~> e -> m a +``` + +In order to unwrap the underlying `Monad`, `ReaderT` needs to be ran with a +given environment. A `ReaderT` instance comes equipped with a `runWith` method +that accepts an environment and returns the resulting `Monad`. + +```javascript +const ReaderT = require('../crocks/src/Reader/ReaderT') +const Maybe = require('crocks/Maybe') + +const MaybeReader = ReaderT(Maybe) +const { ask, liftFn } = MaybeReader + +const prop = require('crocks/Maybe/prop') + +// data :: Object +const data = { + animals: [ + 'tiger', 'muskrat', 'mouse' + ] +} + +// length :: Array -> Number +const length = + x => x.length + +// getProp :: String -> ReaderT Object (Maybe []) +const getProp = key => + ask() + .chain(liftFn(prop(key))) + +getProp('animals') + .map(length) + .runWith(data) +//=> Just 3 +``` + +
diff --git a/docs/src/pages/docs/crocks/State.md b/docs/src/pages/docs/crocks/State.md new file mode 100644 index 000000000..9b51535c3 --- /dev/null +++ b/docs/src/pages/docs/crocks/State.md @@ -0,0 +1,695 @@ +--- +title: "State" +description: "State Crock" +layout: "guide" +weight: 130 +--- + +```haskell +State s a +``` + +`State` is an Algebraic Data Type that abstracts away the associated state +management that comes with stateful computations.`State` is parameterized by +two types, a state `s` and a resultant `a`. The resultant portion may vary it's +type, but the state portion must be fixed to a type that is used by all related +stateful computations. + +All `State` instances wrap a function of the form `s -> Pair a s` and can be +constructed by providing a function of this form. In order to get maximum +reuse of existing functions, a few construction helpers are available on the +`State` constructor. + +`State` is lazy and is required to be run at the edge with some initial state. +Three methods are available on the instance for running the `State` with a +given initial state. [`runWith`](#runwith) will return a `Pair a s` with the +state `s` on the right and the resultant `a` on the left. + +The other two are used for extracting either the state or resultant, unwrapping +the values from the `Pair` and discarding the unwanted portion. +[`evalWith`](#evalwith) used when the resultant is wanted, while +[`execWith`](#execwith) is used to pull the state. + +```javascript +const State = require('crocks/State') +const { get, put } = State + +const Pair = require('crocks/Pair') +const constant = require('crocks/combinators/constant') + + +// toUpper :: String -> String +const toUpper = + x => x.toUpperCase() + +// putResultant :: String -> State String String +const putResultant = x => + put(x) + .map(constant(x)) + +// standard construction +// State String String +State(s => Pair(toUpper(s), s)) + .runWith('nice') +//=> Pair('NICE', 'nice') + +// construction helper +// State String String +get(toUpper) + .runWith('nice') +//=> Pair('NICE', 'nice') + +// combine states +get(toUpper) + .chain(putResultant) + .runWith('nice') +//=> Pair('NICE', 'NICE') + +// pull resultant only +get(toUpper) + .evalWith('nice') +//=> 'NICE' + +// pull state only +get(toUpper) + .execWith('nice') +//=> 'nice' +``` + +
+ +## Implements +`Functor`, `Apply`, `Chain`, `Applicative`, `Monad` + +
+ +
+ +## Constructor Methods + +#### get +```haskell +State.get :: () -> State s s +State.get :: (s -> a) -> State s a +``` + +A construction helper that is used to access the state portion of a given +`State` instance. To make the state accessible, `get` will place the state in +the resultant portion, overwriting what was there previously. + +`get` may be called with or without a function as it's argument. When nothing is +provided for the argument, the state will be applied to the resultant as is. The +state will be mapped over any provided function that takes the same type as the +state, with the result deposited in the resultant. + +```javascript +const { get } = require('crocks/State') + +const chain = require('crocks/pointfree/chain') +const compose = require('crocks/helpers/compose') +const isNumber = require('crocks/predicates/isNumber') +const option = require('crocks/pointfree/option') +const prop = require('crocks/Maybe/prop') +const safe = require('crocks/Maybe/safe') + +// propOr :: (String, (b -> Boolean), a) -> Object -> c +const propOr = (key, pred, def) => + compose(option(def), chain(safe(pred)), prop(key)) + +// safeNumber :: Object -> Number +const safeNumber = + propOr('number', isNumber, 0) + +get(safeNumber) + .runWith({ number: 23 }) +//=> Pair(23, { number: 23 }) + +get(safeNumber) + .evalWith({ number: '23' }) +//=> 0 + +get() + .map(safeNumber) + .evalWith({ number: 23 }) +//=> 23 + +get() + .map(safeNumber) + .runWith({ string: '47' }) +//=> Pair(0, { string: '47'}) +``` + +#### modify +```haskell +State.modify :: (s -> s) -> State s () +``` + +A construction helper that can be used to lift an endo-function that matches +the fixed type of the state portion. The lifted function will receive the state +and returns a new `State` instance with the result of the function in the state +portion. Great care should be taken to not use functions that will change the +type of the state as it may not be expected in other stateful computations and +can result in hard to track down bugs. + +```javascript +const { modify } = require('crocks/State') + +const mapProps = require('crocks/helpers/mapProps') + +// add :: Number -> Number -> Number +const add = + x => y => x + y + +// addState :: Number -> State Number () +const addState = x => + modify(add(x)) + +// addValue :: Number -> State Object () +const addValue = x => + modify(mapProps({ value: add(x) })) + +addState(5) + .execWith(45) +//=> 50 + +addValue(5) + .execWith({ value: 45 }) +//=> { value: 50 } + +addValue(5) + .execWith({}) +//=> {} +``` + +#### put +```haskell +State.put :: s -> State s () +``` + +Used to replace the state portion of a given State instance,, `put` can be +employed anytime that the state can change without having to know about it's +previous value. If the previous value is required for a given stateful +computation, [`modify`](#modify) can be used to lift a function that represents +the change. + +As put updates the state, it is important to ensure that the state portion stays +fixed for all related functions. Changing the type of the state portion may +result in hard to debug bugs and destroys the relationship between stateful +computations. + +```javascript +const { put } = require('crocks/State') + +const compose = require('crocks/helpers/compose') +const isString = require('crocks/predicates/isString') +const option = require('crocks/pointfree/option') +const safe = require('crocks/Maybe/safe') + +// safeString :: a -> String +const safeString = + compose(option(''), safe(isString)) + +// reset :: () -> State String () +const reset = () => + put('') + +// update :: a -> State String () +const update = + compose(put, safeString) + +// heckYeah :: State String () +const heckYeah = + update('Oh Heck Yeah') + +heckYeah + .execWith('Gosh') +//=> 'Oh Heck Yeah' + +heckYeah + .chain(reset) + .runWith('Gosh') +// Pair((), '') +``` + +#### of +```haskell +State.of :: a -> State s a +``` + +Used to "blindly" lift any Javascript value into a `State`, `of` will take the +provided value and return back a new `State` instance with the value in +the resultant. There are many uses for `of`, but mostly it is used to set the +resultant in the same way [`put`](#put) is used to replace the state. Many times +`of` is used at the start of a given stateful computation or in conjunction +with [`put`](#put) and [`modify`](#modify) to replace the `Unit` the resultant +is set to for those construction helpers. + +```javascript +const State = require('crocks/State') +const { get, put } = State + +// updatePop :: String -> State String String +const updatePop = x => + get().chain( + old => put(x).chain( + () => State.of(old) + ) + ) + +State.of('hotness') + .chain(updatePop) + .runWith('crusty') +//=> Pair('crusty', 'hotness') +``` + +#### type +```haskell +State.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 State = require('crocks/State') + +const Reader = require('crocks/Reader') +const identity = require('crocks/combinators/identity') +const isSameType = require('crocks/predicates/isSameType') + +State.type() //=> "State" + +isSameType(State, State.of(3)) //=> true +isSameType(State, State) //=> true +isSameType(State, Reader(identity)) //=> false +isSameType(State.of(false), Reader) //=> false +``` + +
+ +
+ +## Instance Methods + +#### map +```haskell +State s a ~> (a -> b) -> State s b +``` + +While the state portion `s` of `State` must remain fixed to a type, the +resultant `a` can vary in it's type as needed. This allows complex stateful +computations to be represented with `State`. The `map` method provides a means +to lift a function into the datatype that will be applied to the resultant and +return a new instance of `State` with the result of the function as the new +resultant. + +While this is similar to the [`modify`](#modify) construction helper, which +lifts an endo-function that acts upon the state, `map` does not require an +endo-function as it can move to any type. + +Due to the composition law associated with `map`, successive `map`s can be +composed together using function composition. This will give the same results +but will only map the value once, instead of once for every mapping. + +```javascript +const { get } = require('crocks/State') + +const compose = require('crocks/helpers/compose') +const objOf = require('crocks/helpers/objOf') +const propOr = require('crocks/helpers/propOr') + +// add :: Number -> Number -> Number +const add = + x => y => x + y + +// getNum :: State Object Number +const getNum = + get(propOr(0, 'num')) + +getNum + .map(add(10)) + .evalWith({ num: 32 }) +//=> 42 + +getNum + .map(add(10)) + .map(objOf('result')) + .evalWith({ val: 32 }) +//=> { result: 10 } + +// comp :: Number -> Object +const comp = compose( + objOf('result'), + add(10) +) + +getNum + .map(comp) + .evalWith({ num: 32 }) +//=> { result: 42 } +``` + +#### ap +```haskell +State s (a -> b) ~> State s a -> State s b +``` + +Short for apply, the `ap` method is used to apply the resultant of a given +`State` instance to a function wrapped in another instance. On a `State` +instance that wraps a function, calling `ap`, providing it another `State` +instance, will return a new `State` instance with the result of the function +in the resultant portion. + +When used with curried, polyadic functions, multiple stateful computations can +be combined using the lifted function as a means to combine each of the +instances' resultants. + +```javascript +const { get, modify } = require('crocks/State') + +const assoc = require('crocks/helpers/assoc') +const propOr = require('crocks/helpers/propOr') + +const data = { + tax: .084, + sub: 34.97 +} + +// add :: Number -> Number -> Number +const add = + x => y => x + y + +// multiply :: Number -> Number -> Number +const multiply = + x => y => x * y + +// round :: Number -> Number +const round = + x => Math.round(x * 100) / 100 + +// getKey :: String -> State Object Number +const getKey = key => + get(propOr(0, key)) + +// updateKey :: String -> a -> State Object () +const updateKey = key => val => + modify(assoc(key, val)) + +// addToSub :: Number -> String Object Number +const addToSub = x => + getKey('sub') + .map(add(x)) + +const calcTax = + getKey('tax') + .map(multiply) + .ap(getKey('sub')) + +// applyTax :: State Object () +const applyTax = + calcTax + .chain(addToSub) + .map(round) + .chain(updateKey('total')) + +applyTax + .execWith(data) +//=> { tax: 0.084, sub: 34.07, total: 37.91 } +``` + +#### chain +```haskell +State s a ~> (a -> State s b) -> State s b +``` + +As a means to combine stateful computations, `chain` is used to sequence +state transactions that either read from or write to the state. `chain` takes +a unary function that must return a new `State` instance. `chain` returns a new +`State` instance that will apply the computation when run. + +```javascript +const { get, modify } = require('crocks/State') + +// add :: Number -> State Number () +const add = x => + modify(y => x + y) + +// multiply :: Number -> State Number () +const multiply = x => + modify(y => x * y) + +// double :: () -> State Number () +const double = () => + get() + .chain(add) + +// square :: () -> State Number () +const square = () => + get() + .chain(multiply) + +add(10) + .execWith(10) +//=> 20 + +add(10) + .chain(double) + .execWith(10) +//=> 40 + +add(10) + .chain(square) + .execWith(10) +//=> 400 + +add(10) + .chain(double) + .chain(square) + .execWith(10) +//=> 1600 +``` + +#### runWith +```haskell +State s a ~> s -> Pair a s +``` + +`State` is a lazy datatype that requires a value for it's state portion to be +run. A given `State` instance provides a `runWith` method that accepts a value +to run the instance with. The value must be a member of the type that the +given `State` instance is fixed to in it's state portion, `s`. + +When called, `runWith` will run the state transition with the given value as the +initial state and will return the resulting `Pair` with the resultant in the +`fst` (first) and the state in the `snd` (second). + + +```javascript +const State = require('crocks/State') +const { get, put } = State + +const K = require('crocks/combinators/constant') + +// swap :: s -> s -> State s s +const swap = x => old => + put(x) + .chain(K(State.of(old))) + +//update :: s -> State s s +const update = x => + get() + .chain(swap(x)) + +update(45) + .runWith(100) +//=> Pair(100, 45) +``` + +#### evalWith +```haskell +State s a ~> s -> a +``` + +`State` is a lazy datatype that requires a value for it's state portion to +be run. A given `State` instance provides an `evalWith` method that accepts a +value to run the instance with. The value must be a member of the type that the +given `State` instance is fixed to in it's state portion, `s`. + +When called, `evalWith` will run the state transition with the given value as +the initial state and will return the resulting resultant discarding the state +portion. + +```javascript +const State = require('crocks/State') +const { get } = State + +const concat = require('crocks/pointfree/concat') +const flip = require('crocks/combinators/flip') +const liftA2 = require('crocks/helpers/liftA2') +const map = require('crocks/pointfree/map') +const propOr = require('crocks/helpers/propOr') + +const name = { + first: 'Franklin', + last: 'Jennings' +} + +// getLast :: State Object String +const getFirst = + get(propOr('', 'first')) + +// getLast :: State Object String +const getLast = + get(propOr('', 'last')) + +// inner :: Functor f => f a -> f [ a ] +const inner = + map(Array.of) + +// combineNames :: State Object [ String ] +const combineNames = liftA2( + flip(concat), + inner(getFirst), + inner(getLast) +) + +combineNames + .evalWith(name) +//=> [ 'Franklin', 'Jennings' ] +``` + +#### execWith +```haskell +State s a ~> s -> s +``` + +`State` is a lazy datatype that requires a value for it's state portion to +be run. A given `State` instance provides an `execWith` method that accepts a +value to run the instance with. The value must be a member of the type that the +given `State` instance is fixed to in it's state portion, `s`. + +When called, `execWith` will run the state transition with the given value as +the initial state and will return the resulting state, discarding the resultant +portion. + +```javascript +const { modify } = require('crocks/State') + +const compose = require('crocks/helpers/compose') +const concat = require('crocks/pointfree/concat') + +// toUpper :: String -> String +const toUpper = + x => x.toUpperCase() + +// exclaim :: String -> String +const exclaim = + concat('!!!') + +// yell :: State String () +const yell = modify( + compose(exclaim, toUpper) +) + +yell + .execWith('nice') +//=> 'NICE!!!' +``` + +
+ +
+ +## Pointfree Functions + +#### evalWith *(pointfree)* +```haskell +evalWith :: s -> State s a -> a +``` + +The `evalWith` pointfree function can be employed to execute the +[`evalWith`](#evalwith) method on a given `State` instance. This function is +typically used at the edge of a program where all the side-effects typically +reside. + +As all this function does is return the result of applying a given initial state +to the [`evalWith`](#evalwith) method to the provided `State` instance, it will +also return the resulting resultant, throwing away the resulting state. + + +```javascript +const { get } = require('crocks/State') + +const evalWith = require('crocks/State/evalWith') + +const compose = require('crocks/helpers/compose') +const curry = require('crocks/helpers/curry') +const flip = require('crocks/combinators/flip') + +// addToState :: Number -> State Number Number +const addToState = + x => get(y => x + y) + +// add :: Number -> Number -> Number +const add = curry( + compose(flip(evalWith), addToState) +) + +// add10 :: Number -> Number +const add10 = + add(10) + +add10(32) +//=> 42 + +add(1295, 42) +// 1337 +``` + +#### execWith *(pointfree)* +```haskell +execWith :: s -> State s a -> s +``` + +The `execWith` pointfree function can be employed to execute the +[`execWith`](#execwith) method on a given `State` instance. This function is +typically used at the edge of a program where all the side-effects typically +reside. + +As all this function does is return the result of applying a given initial state +to the [`execWith`](#execwith) method to the provided `State` instance, it will +also return the resulting state, throwing away the resulting resultant. + +```javascript +const State = require('crocks/State') +const { modify } = State + +const execWith = require('crocks/State/execWith') + +const curry = require('crocks/helpers/curry') +const isSameType = require('crocks/predicates/isSameType') +const mapProps = require('crocks/helpers/mapProps') +const when = require('crocks/logic/when') + +// middleware :: Object -> State Object | Object -> Object +const middleware = curry( + s => when(isSameType(State), execWith(s)) +) + +// incValue :: State Object () +const incValue = + modify(mapProps({ value: x => x + 1 })) + +middleware({ value: 10 }, incValue) +//=> { value: 11 } + +middleware({ value: 10 }, { value: 32 }) +//=> { value: 32 } +``` + +
diff --git a/docs/src/pages/docs/api/crocks.md b/docs/src/pages/docs/crocks/index.md similarity index 98% rename from docs/src/pages/docs/api/crocks.md rename to docs/src/pages/docs/crocks/index.md index 22ec67730..a7ec1f2ca 100644 --- a/docs/src/pages/docs/api/crocks.md +++ b/docs/src/pages/docs/crocks/index.md @@ -1,8 +1,9 @@ --- -description: "Crocks API" -layout: "guide" title: "Crocks" -weight: 1 +description: "Crocks API" +layout: "notopic" +icon: "code-file" +weight: 2 --- ### Crocks diff --git a/docs/src/pages/docs/api/combinators.md b/docs/src/pages/docs/functions/combinators.md similarity index 93% rename from docs/src/pages/docs/api/combinators.md rename to docs/src/pages/docs/functions/combinators.md index b5cb89a31..5820d9120 100644 --- a/docs/src/pages/docs/api/combinators.md +++ b/docs/src/pages/docs/functions/combinators.md @@ -1,13 +1,14 @@ --- description: "Combinators API" -layout: "guide" +layout: "notopic" title: "Combinators" -weight: 3 +weight: 1 --- -### Combinators #### applyTo + `crocks/combinators/applyTo` + ```haskell applyTo :: (a -> b) -> a -> b ``` @@ -16,7 +17,9 @@ and a value and then returns the result of that function with the argument applied. #### composeB + `crocks/combinators/composeB` + ```haskell composeB :: (b -> c) -> (a -> b) -> a -> c ``` @@ -26,10 +29,12 @@ will return a function that will take value `a` and apply it to `g`, passing the result as an argument to `f`, and will finally return the result of `f`. (This allows only two functions, if you want to avoid things like: `composeB(composeB(f, g), composeB(h, i))` then check out -[`compose`](#compose).) +[`compose`](helpers.html#compose).) #### constant + `crocks/combinators/constant` + ```haskell constant :: a -> _ -> a ``` @@ -38,17 +43,21 @@ give you back a function that will return that same value no matter what you pass it. #### flip + `crocks/combinators/flip` + ```haskell flip :: (a -> b -> c) -> b -> a -> c ``` -This little function just takes a function and returns a function that takes the -first two parameters in reverse. One can compose flip calls down the line to +This little function just takes a function and returns a function that takes +the first two parameters in reverse. One can compose flip calls down the line to flip all, or some of the other parameters if there are more than two. Mix and -match to your :heart:'s desire. +match to your heart's desire. #### identity + `crocks/combinators/identity` + ```haskell identity :: a -> a ``` @@ -58,7 +67,9 @@ something, it returns that thing right back to you. So simple, I will leave it as an exercise to reason about why this is so powerful and important. #### reverseApply + `crocks/combinators/reverseApply` + ```haskell reverseApply :: a -> (a -> b) -> b ``` @@ -69,7 +80,9 @@ Once that function is provided, it will return the result of applying your value to that function. #### substitution + `crocks/combinators/substitution` + ```haskell substitution :: (a -> b -> c) -> (a -> b) -> a -> c ``` @@ -81,4 +94,3 @@ of both functions, and the result of the second function to the second parameter of the first function. Finally after all that juggling, it will return the result of that first function. When used with partial application on that first parameter, a whole new world of combinatory madness is presented! - diff --git a/docs/src/pages/docs/api/helpers.md b/docs/src/pages/docs/functions/helpers.md similarity index 75% rename from docs/src/pages/docs/api/helpers.md rename to docs/src/pages/docs/functions/helpers.md index c1a567562..9f30cbf96 100644 --- a/docs/src/pages/docs/api/helpers.md +++ b/docs/src/pages/docs/functions/helpers.md @@ -1,14 +1,14 @@ --- +title: "Helpers" description: "Helper functions" -layout: "guide" -title: "Helper Functions" -weight: 4 +layout: "notopic" +weight: 2 --- -## Helper Functions - #### assign + `crocks/helpers/assign` + ```haskell assign :: Object -> Object -> Object ``` @@ -23,7 +23,9 @@ named [`defaultProps`](#defaultprops) that will only assign values that are `undefined` in the second argument. #### assoc + `crocks/helpers/assoc` + ```haskell assoc :: String -> a -> Object -> Object ``` @@ -32,12 +34,13 @@ want control over how the key and value are applied. That is where `assoc` can come to your aid. Just provide a `String` key and a value of any type to be associated to the key. Finally pass it any `Object` and you will get back a shallow copy with your key-value pair merged in. This will overwrite any exiting -keys with new value specified. Used with [`flip`](#flip), you can do some -interesting things with this function, give it a play! If you just want to -create an `Object` and not concatenate it to another `Object`, [`objOf`](#objof) -may be the function for you. +keys with new value specified. Used with [`flip`](combinators.html#flip), +you can do some interesting things with this function, give it a play! If you +just want to create an `Object` and not concatenate it to another `Object`, +[`objOf`](#objof) may be the function for you. #### binary + `crocks/helpers/binary` ```haskell binary :: (* -> c) -> a -> b -> c @@ -54,7 +57,9 @@ for [`nAry`](#nary). `binary` is basically syntactic sugar for `nAry(2, fn)`. Also related is [`unary`](#unary), which constrains to (1) argument. #### branch + `crocks/Pair/branch` + ```haskell branch :: a -> Pair a a ``` @@ -69,19 +74,23 @@ will reference each other. values into a single value. #### compose + `crocks/helpers/compose` + ```haskell compose :: ((y -> z), (x -> y), ..., (a -> b)) -> a -> z ``` -While the [`composeB`](#composeb) can be used to create a composition of two -functions, there are times when you want to compose an entire flow together. -That is where `compose` is useful. With `compose` you can create a right-to-left -composition of functions. It will return you a function that represents your -flow. Not really sold on writing flows from right-to-left? Well then, I would -recommend reaching for [`pipe`](#pipe). +While the [`composeB`](combinators.html#composeb) can be used to create a +composition of two functions, there are times when you want to compose an entire +flow together. That is where `compose` is useful. With `compose` you can create +a right-to-left composition of functions. It will return you a function that +represents your flow. Not really sold on writing flows from right-to-left? Well +then, I would recommend reaching for [`pipe`](#pipe). #### composeK + `crocks/helpers/composeK` + ```haskell composeK :: Chain m => ((y -> m z), (x -> m y), ..., (a -> m b)) -> a -> m z ``` @@ -147,12 +156,16 @@ functions in `crocks`, a [`pipeK`](#pipek) function is provided for flows that make more sense expressed in a left-to-right style. #### composeP + `crocks/helpers/composeP` + ```haskell composeP :: Promise p => ((y -> p z c), (x -> p y c), ..., (a -> p b c)) -> a -> p z c ``` + When working with `Promise`s, it is common place to create chains on a `Promise`'s `then` function: + ```javascript const promFunc = x => promiseSomething(x) @@ -179,7 +192,9 @@ If you would like to provide your functions in a left-to-right manner, check out [pipeP](#pipep). #### composeS + `crocks/helpers/composeS` + ```haskell composeS :: Semigroupoid s => (s y z, s x y, ..., s a b) -> s a z ``` @@ -219,7 +234,9 @@ composeS(double, avg) ``` #### curry + `crocks/helpers/curry` + ```haskell curry :: ((a, b, ...) -> z) -> a -> b -> ... -> z ``` @@ -231,7 +248,9 @@ that can be called in any combination, such as: `f(x, y, z)`, `f(x)(y)(z)`, on functions for maximum re-usability. #### defaultProps + `crocks/helpers/defaultProps` + ```haskell defaultProps :: Object -> Object -> Object ``` @@ -241,14 +260,17 @@ operation presents itself, `defaultProps` can come to your aid. Just pass it an `Object` that defines your defaults and then the `Object` your want to default those props on. If a key that is present on the defaults `Object` is not defined on your data, then the default value will be used. Otherwise, the value from -your data will be used instead. You could just apply [`flip`](#flip) to the -[`assign`](#assign) function and get the same result, but having a function -named `defaultProps` may be easier to read in code. As with most `Object` -related functions in `crocks`, `defaultProps` will return you a shallow copy of -the result and not include any `undefined` values in either `Object`. +your data will be used instead. You could just apply +[`flip`](combinators.html#flip) to the [`assign`](#assign) function and get the +same result, but having a function named `defaultProps` may be easier to read in +code. As with most `Object` related functions in `crocks`, `defaultProps` will +return you a shallow copy of the result and not include any `undefined` values +in either `Object`. #### defaultTo + `crocks/helpers/defaultTo` + ```haskell defaultTo :: a -> b -> a ``` @@ -264,7 +286,9 @@ anything, it is suggested to stick to the signature and only let `a`s through. As a `b` can be an `a` as well. #### dissoc + `crocks/helpers/dissoc` + ```haskell dissoc :: String -> Object -> Object ``` @@ -275,7 +299,9 @@ new, shallow copy of the `Object` sans your key. As with all the `Object` functions, `dissoc` will remove any `undefined` values from the result. #### fanout + `crocks/helpers/fanout` + ```haskell fanout :: (a -> b) -> (a -> c) -> (a -> Pair b c) fanout :: Arrow a b -> Arrow a c -> Arrow a (Pair b c) @@ -292,7 +318,9 @@ configured to split it's input into a pair and than apply the first Function/ADT to the first portion of the underlying `Pair` and the second on the second. #### fromPairs + `crocks/helpers/fromPairs` + ```haskell fromPairs :: [ (Pair String a) ] | List (Pair String a) -> Object ``` @@ -306,11 +334,18 @@ represented in the resulting `Object`. Also, when if multiple keys share the same name, that last value will be moved over. #### liftA2 -#### liftA3 + `crocks/helpers/liftA2` -`crocks/helpers/liftA3` + ```haskell liftA2 :: Applicative m => (a -> b -> c) -> m a -> m b -> m c +``` + +#### liftA3 + +`crocks/helpers/liftA3` + +```haskell liftA3 :: Applicative m => (a -> b -> c -> d) -> m a -> m b -> m c -> m d ``` Ever see yourself wanting to `map` a binary or trinary function, but `map` only @@ -319,7 +354,9 @@ function as well as the number of `Applicatives` (containers that provide both `of` and `ap` functions) you need to get the mapping you are looking for. #### mapProps + `crocks/helpers/mapProps` + ```haskell mapProps :: { (* -> *) } -> Object -> Object ``` @@ -371,7 +408,9 @@ mapProps(mapping, { ``` #### mapReduce + `crocks/helpers/mapReduce` + ```haskell mapReduce :: Foldable f => (a -> b) -> (c -> b -> c) -> c -> f a ``` @@ -390,7 +429,6 @@ const isNumber = require('crocks/predicates/isNumber') const mapReduce = require('crocks/helpers/mapReduce') const safeLift = require('crocks/Maybe/safeLift') - const data = [ '100', null, 3, true, 1 ] @@ -407,39 +445,56 @@ safeMax(data) ``` #### mconcat -#### mreduce + `crocks/helpers/mconcat` -`crocks/helpers/mreduce` + ```haskell mconcat :: Monoid m => m -> ([ a ] | List a) -> m a +``` + +#### mreduce + +`crocks/helpers/mreduce` + +```haskell mreduce :: Monoid m => m -> ([ a ] | List a) -> a ``` These two functions are very handy for combining an entire `List` or `Array` of -values by providing a [`Monoid`](#monoids) and your collection of values. The -difference between the two is that `mconcat` returns the result inside the -[`Monoid`](#monoids) used to combine them. Where `mreduce` returns the bare -value itself. +values by providing a [`Monoid`](../monoids/index.html) and your collection of +values. The difference between the two is that `mconcat` returns the result +inside the [`Monoid`](../monoids/index.html) used to combine them. Where +`mreduce` returns the bare value itself. #### mconcatMap -#### mreduceMap + `crocks/helpers/mconcatMap` -`crocks/helpers/mreduceMap` + ```haskell mconcatMap :: Monoid m => m -> (b -> a) -> ([ b ] | List b) -> m a +``` + +#### mreduceMap + +`crocks/helpers/mreduceMap` + +```haskell mreduceMap :: Monoid m => m -> (b -> a) -> ([ b ] | List b) -> a ``` There comes a time where the values you have in a `List` or an `Array` are not -in the type that is needed for the [`Monoid`](#monoids) you want to combine -with. These two functions can be used to `map` some transforming function from a -given type into the type needed for the [`Monoid`](#monoids). In essence, this -function will run each value through the function before it lifts the value -into the [`Monoid`](#monoids), before `concat` is applied. The difference +in the type that is needed for the [`Monoid`](../monoids/index.html) you want to +combine with. These two functions can be used to `map` some transforming +function from a given type into the type needed for the +[`Monoid`](../monoids/index.html). In essence, this function will run each value +through the function before it lifts the value into the +[`Monoid`](../monoids/index.html), before `concat` is applied. The difference between the two is that `mconcatMap` returns the result inside the -[`Monoid`](#monoids) used to combine them. Where `mreduceMap` returns the bare -value itself. +[`Monoid`](../monoids/index.html) used to combine them. Where `mreduceMap` +returns the bare value itself. #### nAry + `crocks/helpers/nAry` + ```haskell nAry :: Number -> (* -> a) -> * -> * -> a ``` @@ -456,7 +511,9 @@ arguments to the inner function. Unary and binary functions are so common that [`binary`](#binary). #### objOf + `crocks/helpers/objOf` + ```haskell objOf :: String -> a -> Object ``` @@ -468,7 +525,9 @@ yourself constantly concatenating the result of this function into another `Object`, you may want to use [`assoc`](#assoc) instead. #### omit + `crocks/helpers/omit` + ```haskell omit :: ([ String ] | List String) -> Object -> Object ``` @@ -482,7 +541,9 @@ properties and should only be used with POJOs. If you want to filter or white-list properties rather than reject them, take a look at [`pick`](#pick). #### once + `crocks/helpers/once` + ```haskell once :: ((*) -> a) -> ((*) -> a) ``` @@ -492,7 +553,9 @@ Just pass the function you want guarded to `once` and you will get back a function with the expected guarantees. #### partial + `crocks/helpers/partial` + ```haskell partial :: ((* -> c), *) -> * -> c ``` @@ -518,7 +581,9 @@ map(max10, data) ``` #### pick + `crocks/helpers/pick` + ```haskell pick :: ([ String ] | List String) -> Object -> Object ``` @@ -532,7 +597,9 @@ values will not be copied over, although `null` values are allowed. For black-listing properties, have a look at [`omit`](#omit). #### pipe + `crocks/helpers/pipe` + ```haskell pipe :: ((a -> b), (b -> c), ..., (y -> z)) -> a -> z ``` @@ -542,7 +609,9 @@ This function does the same thing as [`compose`](#compose), the only difference is it allows you define your flows in a left-to-right manner. #### pipeK + `crocks/helpers/pipeK` + ```haskell pipeK :: Chain m => ((a -> m b), (b -> m c), ..., (y -> m z)) -> a -> m z ``` @@ -584,7 +653,9 @@ chainPipe(0).log() ``` #### pipeP + `crocks/helpers/pipeP` + ```haskell pipeP :: Promise p => ((a -> p b d), (b -> p c d), ..., (y -> p z d)) -> a -> p z d ``` @@ -606,7 +677,9 @@ const promPipe = ``` #### pipeS + `crocks/helpers/pipeS` + ```haskell pipeS :: Semigroupoid s => (s a b, s b c, ..., s y z) -> s a z ``` @@ -651,7 +724,9 @@ flow('string', 100).runWith(data) ``` #### prop + `crocks/Maybe/prop` + ```haskell prop :: (String | Integer) -> a -> Maybe b ``` @@ -663,7 +738,9 @@ 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`. #### propOr + `crocks/helpers/propOr` + ```haskell propOr :: a -> (String | Integer) -> b -> c ``` @@ -691,7 +768,9 @@ get() ``` #### propPath + `crocks/Maybe/propPath` + ```haskell propPath :: [ String | Integer ] -> a -> Maybe b ``` @@ -705,7 +784,9 @@ 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`. #### propPathOr + `crocks/helpers/propPathOr` + ```haskell propPathOr :: a -> [ String | Integer ] -> b -> c ``` @@ -734,7 +815,9 @@ get() ``` #### safe + `crocks/Maybe/safe` + ```haskell safe :: ((a -> Boolean) | Pred) -> a -> Maybe a ``` @@ -746,7 +829,9 @@ value will be evaluated against the predicate, and will lift it into a `Just` if true and a `Nothing` if false. #### safeLift + `crocks/Maybe/safeLift` + ```haskell safeLift :: ((a -> Boolean) | Pred) -> (a -> b) -> a -> Maybe b ``` @@ -759,7 +844,9 @@ that will first lift its argument into a `Maybe` and then maps your original function over the result. #### tap + `crocks/helpers/tap` + ```haskell tap :: (a -> b) -> a -> a ``` @@ -772,7 +859,9 @@ immutability for reference types (`Objects`, `Arrays`, etc), you will need to exercise some discipline here to not mutate. #### toPairs + `crocks/Pair/toPairs` + ```haskell toPairs :: Object -> List (Pair String a) ``` @@ -786,7 +875,9 @@ references. Like most of the `Object` functions in `crocks`, any keys with to this function named [`fromPairs`](#frompairs). #### tryCatch + `crocks/Result/tryCatch` + ```haskell tryCatch :: (a -> b) -> a -> Result e b ``` @@ -798,7 +889,9 @@ either return the result in a `Result.Ok` if everything was good, or an error wrapped in an `Result.Err` if it fails. #### unary + `crocks/helpers/unary` + ```haskell unary :: (* -> b) -> a -> b ``` @@ -811,7 +904,9 @@ Another common case is [`binary`](#binary) which, as the name implies, only applies (2) arguments to a given function. #### unit + `crocks/helpers/unit` + ```haskell unit :: () -> undefined ``` @@ -820,131 +915,3 @@ things. A common use for it is as a default `noop` as it is a function that does nothing and returns `undefined`. You can also use it in a pointed fashion to represent some special value for a given type. This pointed use is the heart and soul of the infamous `Maybe` type. - -### Logic Functions -The functions in this section are used to represent logical branching in a -declarative manner. Each of these functions require either `Pred`s or predicate -functions in their input. Since these functions work with `Pred`s and predicate -functions, rather than values, this allows for composeable, "lazy" evaluation. -All logic functions can be referenced from `crocks/logic` - -#### and -```haskell -and :: ((a -> Boolean) | Pred) -> ((a -> Boolean) | Pred) -> a -> Boolean -``` -Say you have two predicate functions or `Pred`s and would like to combine them -into one predicate over conjunction, well you came to the right place, `and` -accepts either predicate functions or `Pred`s and will return you a function -ready to take a value. Once that value is passed, it will run it through both of -the predicates and return the result of combining it over a `logical and`. This -is super helpful combined with `or` for putting together reusable, complex -predicates. As they follow the general form of `(a -> Boolean)` they are easily -combined with other logic functions. - -#### ifElse -```haskell -ifElse :: ((a -> Boolean) | Pred) -> (* -> a) -> (* -> a) -> * -> a -``` -Whenever you need to modify a value based some condition and want a functional -way to do it without some imperative `if` statement, then reach for `ifElse`. -This function take a predicate (some function that returns a Boolean) and two -functions. The first is what is executed when the predicate is true, the second -on a false condition. This will return a function ready to take a value to run -through the predicate. After the value is evaluated, it will be ran through it's -corresponding function, returning the result as the final result. This function -comes in really handy when creating lifting functions for Sum Types (like -`Either` or `Maybe`). - -#### not -```haskell -not :: ((a -> Boolean) | Pred) -> a -> Boolean -``` -When you need to negate a predicate function or a `Pred`, but want a new -predicate function that does the negation, then `not` is going to get you what -you need. Using `not` will allow you to stay as declarative as possible. Just -pass `not` your predicate function or a `Pred`, and it will give you back a -predicate function ready for insertion into your flow. All predicate based -functions in `crocks` take either a `Pred` or predicate function, so it should -be easy to swap between the two. - -#### or -```haskell -or :: ((a -> Boolean) | Pred) -> ((a -> Boolean) | Pred) -> a -> Boolean -``` -Say you have two predicate functions or `Pred`s and would like to combine them -into one predicate over disjunction, look no further, `or` accepts either -predicate functions or `Pred`s and will return you a function ready to take a -value. Once that value is passed, it will run it through both of the predicates -and return the result of combining it over a `logical or`. This is super helpful -combined with `and` for putting together reusable, complex predicates. As they -follow the general form of `(a -> Boolean)` they are easily combined with other -logic functions. - -#### unless -```haskell -unless :: ((a -> Boolean) | Pred) -> (a -> a) -> a -> a -``` -There may come a time when you need to adjust a value when a condition is false, -that is where `unless` can come into play. Just provide a predicate function (a -function that returns a Boolean) and a function to apply your desired -modification. This will get you back a function that when you pass it a value, -it will evaluate it and if false, will run your value through the provided -function. Either the original or modified value will be returned depending on -the result of the predicate. Check out [`when`](#when) for a negated version of -this function. - -#### when -```haskell -when :: ((a -> Boolean) | Pred) -> (a -> a) -> a -> a -``` -There may come a time when you need to adjust a value when a condition is true, -that is where `when` can come into play. Just provide a predicate function (a -function that returns a Boolean) and a function to apply your desired -modification. This will get you back a function that when you pass it a value, -it will evaluate it and if true, will run your value through the provided -function. Either the original or modified value will be returned depending on -the result of the predicate. Check out [`unless`](#unless) for a negated version -of this function. - -### Predicate Functions -All functions in this group have a signature of `* -> Boolean` and are used with -the many predicate based functions that ship with `crocks`, like -[`safe`](#safe), [`ifElse`](#ifelse) and `filter` to name a few. They also fit -naturally with the `Pred` ADT. All predicate functions can be referenced from -`crocks/predicates` Below is a list of all the current predicates that are -included with a description of their truth: - -* `hasProp :: (String | Number) -> a -> Boolean`: An Array or Object that contains the provided index or key -* `isAlt :: a -> Boolean`: an ADT that provides `map` and `alt` functions -* `isAlternative :: a -> Boolean`: an ADT that provides `alt`, `zero`, `map`, `ap`, `chain` and `of` functions -* `isApplicative :: a -> Boolean`: an ADT that provides `map`, `ap` and `of` functions -* `isApply :: a -> Boolean`: an ADT that provides `map` and `ap` functions -* `isArray :: a -> Boolean`: Array -* `isBifunctor :: a -> Boolean`: an ADT that provides `map` and `bimap` functions -* `isBoolean :: a -> Boolean`: Boolean -* `isCategory :: a -> Boolean`: an ADT that provides `id` and `compose` functions -* `isChain :: a -> Boolean`: an ADT that provides `map`, `ap` and `chain` functions -* `isContravariant : a -> Boolean`: an ADT that provides `contramap` function -* `isDefined :: a -> Boolean`: Every value that is not `undefined`, `null` included -* `isEmpty :: a -> Boolean`: Empty Object, Array or String -* `isExtend :: a -> Boolean`: an ADT that provides `map` and `extend` functions -* `isFoldable :: a -> Boolean`: Array, List or any structure with a `reduce` function -* `isFunction :: a -> Boolean`: Function -* `isFunctor :: a -> Boolean`: an ADT that provides a `map` function -* `isInteger :: a -> Boolean`: Integer -* `isMonad :: a -> Boolean`: an ADT that provides `map`, `ap`, `chain` and `of` functions -* `isMonoid :: a -> Boolean`: an ADT that provides `concat` and `empty` functions -* `isNil :: a -> Boolean`: `undefined` or `null` or `NaN` -* `isNumber :: a -> Boolean`: `Number` that is not a `NaN` value, `Infinity` included -* `isObject :: a -> Boolean`: Plain Old Javascript Object (POJO) -* `isPlus :: a -> Boolean`: an ADT that provides `map`, `alt` and `zero` functions -* `isProfunctor : a -> Boolean`: an ADT that provides `map`, `contramap` and `promap` functions -* `isPromise :: a -> Boolean`: an object implementing `then` and `catch` -* `isSame :: a -> b -> Boolean`: same value or reference, use `equals` for value equality -* `isSameType :: a -> b -> Boolean`: Constructor matches a values type, or two values types match -* `isSemigroup :: a -> Boolean`: an ADT that provides a `concat` function -* `isSemigroupoid :: a -> Boolean`: an ADT that provides a `compose` function -* `isSetoid :: a -> Boolean`: an ADT that provides an `equals` function -* `isString :: a -> Boolean`: String -* `isTraversable :: a -> Boolean`: an ADT that provides `map` and `traverse` functions - diff --git a/docs/src/pages/docs/api/index.md b/docs/src/pages/docs/functions/index.md similarity index 68% rename from docs/src/pages/docs/api/index.md rename to docs/src/pages/docs/functions/index.md index 093adfb3c..191010bc1 100644 --- a/docs/src/pages/docs/api/index.md +++ b/docs/src/pages/docs/functions/index.md @@ -1,23 +1,12 @@ --- -title: "API" -description: "Crocks API" -layout: "guide" +title: "Functions" +description: "functions" +layout: "notopic" icon: "code-file" -weight: 2 +weight: 4 --- -There are (8) classifications of "things" included in this library: - -* [Crocks](crocks.html): These are the ADTs that this library is centered around. -They are all `Functor` based Data Types that provide different computational -contexts for working in a more declarative, functional flow. For the most part, -a majority of the other bits in `crocks` exist to serve these ADTs. - -* [Monoids](monoids.html): These helpful ADTs are in a class of their own, not -really `Functor`s in their own right (although some can be), they are still very -useful in our everyday programming needs. Ever need to Sum a list of Numbers or -mix a mess of objects together? This is were you will find the ADTs you need to -do that. +There are (6) function classifications included in this library: * [Combinators](combinators.html): A collection of functions that are used for working with other functions. These do things like compose (2) functions diff --git a/docs/src/pages/docs/api/logic-functions.md b/docs/src/pages/docs/functions/logic-functions.md similarity index 98% rename from docs/src/pages/docs/api/logic-functions.md rename to docs/src/pages/docs/functions/logic-functions.md index 2853bd4a6..efdc47572 100644 --- a/docs/src/pages/docs/api/logic-functions.md +++ b/docs/src/pages/docs/functions/logic-functions.md @@ -1,11 +1,10 @@ --- description: "Logic Functions API" -layout: "guide" +layout: "notopic" title: "Logic Functions" -weight: 5 +weight: 3 --- -### Logic Functions The functions in this section are used to represent logical branching in a declarative manner. Each of these functions require either `Pred`s or predicate functions in their input. Since these functions work with `Pred`s and predicate diff --git a/docs/src/pages/docs/api/pointfree-functions.md b/docs/src/pages/docs/functions/pointfree-functions.md similarity index 99% rename from docs/src/pages/docs/api/pointfree-functions.md rename to docs/src/pages/docs/functions/pointfree-functions.md index b49d322ec..ac56965cb 100644 --- a/docs/src/pages/docs/api/pointfree-functions.md +++ b/docs/src/pages/docs/functions/pointfree-functions.md @@ -1,11 +1,10 @@ --- -description: "Point-free Functions API" -layout: "guide" title: "Point-free Functions" -weight: 7 +description: "Point-free Functions API" +layout: "notopic" +weight: 5 --- -### Point-free Functions While it can seem natural to work with all these containers in a fluent fashion, it can get cumbersome and hard to get a lot of reuse out of. A way to really get the most out of reusability in Javascript is to take what is called a point-free diff --git a/docs/src/pages/docs/api/predicate-functions.md b/docs/src/pages/docs/functions/predicate-functions.md similarity index 74% rename from docs/src/pages/docs/api/predicate-functions.md rename to docs/src/pages/docs/functions/predicate-functions.md index 2fb8f5950..07c248edf 100644 --- a/docs/src/pages/docs/api/predicate-functions.md +++ b/docs/src/pages/docs/functions/predicate-functions.md @@ -1,48 +1,49 @@ --- description: "Predicate Functions API" -layout: "guide" +layout: "notopic" title: "Predicate Functions" -weight: 6 +weight: 4 --- -### Predicate Functions All functions in this group have a signature of `* -> Boolean` and are used with the many predicate based functions that ship with `crocks`, like [`safe`](#safe), [`ifElse`](#ifelse) and `filter` to name a few. They also fit naturally with the `Pred` ADT. All predicate functions can be referenced from -`crocks/predicates` Below is a list of all the current predicates that are -included with a description of their truth: +`crocks/predicates`. -* `hasProp :: (String | Number) -> a -> Boolean`: An Array or Object that contains the provided index or key -* `isAlt :: a -> Boolean`: an ADT that provides `map` and `alt` functions -* `isAlternative :: a -> Boolean`: an ADT that provides `alt`, `zero`, `map`, `ap`, `chain` and `of` functions -* `isApplicative :: a -> Boolean`: an ADT that provides `map`, `ap` and `of` functions -* `isApply :: a -> Boolean`: an ADT that provides `map` and `ap` functions +Below is a list of all the current predicates that are included with a +description of their truth: + +* `hasProp :: (String | Number) -> a -> Boolean`: An `Array` or `Object` that contains the provided index or key +* `isAlt :: a -> Boolean`: an ADT that provides `map` and `alt` methods +* `isAlternative :: a -> Boolean`: an ADT that provides `alt`, `zero`, `map`, `ap`, `chain` and `of`methods +* `isApplicative :: a -> Boolean`: an ADT that provides `map`, `ap` and `of` methods +* `isApply :: a -> Boolean`: an ADT that provides `map` and `ap` methods * `isArray :: a -> Boolean`: Array -* `isBifunctor :: a -> Boolean`: an ADT that provides `map` and `bimap` functions +* `isBifunctor :: a -> Boolean`: an ADT that provides `map` and `bimap` methods * `isBoolean :: a -> Boolean`: Boolean -* `isCategory :: a -> Boolean`: an ADT that provides `id` and `compose` functions -* `isChain :: a -> Boolean`: an ADT that provides `map`, `ap` and `chain` functions -* `isContravariant : a -> Boolean`: an ADT that provides `contramap` function +* `isCategory :: a -> Boolean`: an ADT that provides `id` and `compose` methods +* `isChain :: a -> Boolean`: an ADT that provides `map`, `ap` and `chain` methods +* `isContravariant : a -> Boolean`: an ADT that provides `contramap` method * `isDefined :: a -> Boolean`: Every value that is not `undefined`, `null` included * `isEmpty :: a -> Boolean`: Empty Object, Array or String -* `isExtend :: a -> Boolean`: an ADT that provides `map` and `extend` functions -* `isFoldable :: a -> Boolean`: Array, List or any structure with a `reduce` function +* `isExtend :: a -> Boolean`: an ADT that provides `map` and `extend` methods +* `isFoldable :: a -> Boolean`: Array, List or any structure with a `reduce` method * `isFunction :: a -> Boolean`: Function -* `isFunctor :: a -> Boolean`: an ADT that provides a `map` function +* `isFunctor :: a -> Boolean`: an ADT that provides a `map` method * `isInteger :: a -> Boolean`: Integer -* `isMonad :: a -> Boolean`: an ADT that provides `map`, `ap`, `chain` and `of` functions -* `isMonoid :: a -> Boolean`: an ADT that provides `concat` and `empty` functions +* `isMonad :: a -> Boolean`: an ADT that provides `map`, `ap`, `chain` and `of` methods +* `isMonoid :: a -> Boolean`: an ADT that provides `concat` and `empty` methods * `isNil :: a -> Boolean`: `undefined` or `null` or `NaN` * `isNumber :: a -> Boolean`: `Number` that is not a `NaN` value, `Infinity` included * `isObject :: a -> Boolean`: Plain Old Javascript Object (POJO) -* `isPlus :: a -> Boolean`: an ADT that provides `map`, `alt` and `zero` functions -* `isProfunctor : a -> Boolean`: an ADT that provides `map`, `contramap` and `promap` functions +* `isPlus :: a -> Boolean`: an ADT that provides `map`, `alt` and `zero` methods +* `isProfunctor : a -> Boolean`: an ADT that provides `map`, `contramap` and `promap` methods * `isPromise :: a -> Boolean`: an object implementing `then` and `catch` * `isSame :: a -> b -> Boolean`: same value or reference, use `equals` for value equality * `isSameType :: a -> b -> Boolean`: Constructor matches a values type, or two values types match -* `isSemigroup :: a -> Boolean`: an ADT that provides a `concat` function -* `isSemigroupoid :: a -> Boolean`: an ADT that provides a `compose` function -* `isSetoid :: a -> Boolean`: an ADT that provides an `equals` function +* `isSemigroup :: a -> Boolean`: an ADT that provides a `concat` method +* `isSemigroupoid :: a -> Boolean`: an ADT that provides a `compose` method +* `isSetoid :: a -> Boolean`: an ADT that provides an `equals` method * `isString :: a -> Boolean`: String -* `isTraversable :: a -> Boolean`: an ADT that provides `map` and `traverse` functions +* `isTraversable :: a -> Boolean`: an ADT that provides `map` and `traverse` methods diff --git a/docs/src/pages/docs/api/transformation-functions.md b/docs/src/pages/docs/functions/transformation-functions.md similarity index 99% rename from docs/src/pages/docs/api/transformation-functions.md rename to docs/src/pages/docs/functions/transformation-functions.md index b33a14d20..111c9ef2e 100644 --- a/docs/src/pages/docs/api/transformation-functions.md +++ b/docs/src/pages/docs/functions/transformation-functions.md @@ -1,11 +1,10 @@ --- -description: "Transformation Functions API" -layout: "guide" title: "Transformation Functions" -weight: 8 +description: "Transformation Functions API" +layout: "notopic" +weight: 6 --- -### Transformation Functions Transformation functions are mostly used to reduce unwanted nesting of similar types. Take for example the following structure: diff --git a/docs/src/pages/docs/getting-started.md b/docs/src/pages/docs/getting-started.md index 947fea2df..7ebf69f32 100644 --- a/docs/src/pages/docs/getting-started.md +++ b/docs/src/pages/docs/getting-started.md @@ -1,8 +1,8 @@ --- +title: "Getting Started" description: "Getting started with a Crocks." icon: "arrow-right-rod" layout: "guide" -title: "Getting Started" weight: 1 --- @@ -32,11 +32,11 @@ import crocks from 'crocks'
-## Import only what you need +## Import only what you need This lib *should* work, with no additional compilation in all current browsers (Edge, Safari, Chrome, Firefox), if it does not, please file an issue as I -really, really want it to. :smile_cat:. +really, really want it to. There is a lot to this library, and as such it may not be desired to bring in the entire library when bundling for a library or a frontend application. If diff --git a/docs/src/pages/docs/monoids/All.md b/docs/src/pages/docs/monoids/All.md new file mode 100644 index 000000000..be534ff60 --- /dev/null +++ b/docs/src/pages/docs/monoids/All.md @@ -0,0 +1,146 @@ +--- +title: "All" +description: "All Monoid" +layout: "guide" +weight: 10 +--- + +```haskell +All Boolean +``` + +`All` is a `Monoid` that will combine two values of any type using logical +conjunction (AND) on their coerced `Boolean` values, mapping truth-y values to +`true` and false-y values to `false`. + +```javascript +const All = require('crocks/All') + +const mconcat = require('crocks/helpers/mconcat') + +const trueNum = All(13) +const falseNum = All(0) +const trueString = All('So true') + +trueNum.concat(falseNum) +//=> All false + +trueNum.concat(trueString) +//=> All true + +const allGood = + mconcat(All) + +allGood([ 1, 5, 89 ]) +//=> All true + +allGood([ 'nice', '00', null ]) +//=> All false +``` + +
+ +## Implements +`Semigroup`, `Monoid` + +
+ +
+ +## Constructor Methods + +#### empty +```haskell +All.empty :: () -> All +``` + +`empty` provides the identity for the `Monoid` in that when the value it +provides is `concat`ed to any other value, it will return the other value. In +the case of `All` the result of `empty` is `true`. `empty` is available on both +the Constructor and the Instance for convenience. + +```javascript +const All = require('crocks/All') + +All.empty() //=> All true + +All(true).concat(All.empty()) //=> All true +All(false).concat(All.empty()) //=> All false +``` + +#### type +```haskell +All.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 All = require('crocks/All') + +const Maybe = require('crocks/Maybe') +const isSameType = require('crocks/predicates/isSameType') + +All.type() //=> "All" + +isSameType(All, All(3)) //=> true +isSameType(All, All) //=> true +isSameType(All, Maybe.Just('3')) //=> false +isSameType(All(false), Maybe) //=> false +``` + +
+ +
+ +## Instance Methods + +#### concat +```haskell +All ~> All -> All +``` + +`concat` is used to combine (2) `Semigroup`s of the same type under an operation +specified by the `Semigroup`. In the case of `All`, it will combine the two +using logical AND (conjunction). + +```javascript +const All = require('crocks/All') + +All(true).concat(All(true)) //=> All true +All(true).concat(All(false)) //=> All false +All(false).concat(All(true)) //=> All false +All(false).concat(All(false)) //=> All false +``` + +#### valueOf + +```haskell +All ~> () -> Boolean +``` + +`valueOf` is used on all `crocks` `Monoid`s as a means of extraction. While the +extraction is available, types that implement `valueOf` are not necessarily a +`Comonad`. This function is used primarily for convenience for some of the +helper functions that ship with `crocks`. Calling `valueOf` on an `All` instance +will result in the underlying `Boolean` value. + +```javascript +const All = require('crocks/All') + +All(0).value() //=> false +All('string').valueOf() //=> true + +//=> false +All(true) + .concat('') + .valueOf() +``` + +
diff --git a/docs/src/pages/docs/monoids/Any.md b/docs/src/pages/docs/monoids/Any.md new file mode 100644 index 000000000..e2b3b7f16 --- /dev/null +++ b/docs/src/pages/docs/monoids/Any.md @@ -0,0 +1,149 @@ +--- +title: "Any" +description: "Any Monoid" +layout: "guide" +weight: 20 +--- + +```haskell +Any Boolean +``` + +`Any` is a `Monoid` that will combine two values of any type using logical +disjunction (OR) on their coerced `Boolean` values, mapping truth-y values to +`true` and false-y values to `false`. + +```javascript +const Any = require('crocks/Any') + +const isNumber = require('crocks/predicates/isNumber') +const mconcatMap = require('crocks/helpers/mconcat') + +const trueString = Any('string') +const falseString = Any('') +const object = Any({ nice: true }) + +trueString.concat(falseString) +//=> Any false + +trueString.concat(object) +//=> Any true + +const anyNumber = + mconcatMap(Any, isNumber) + +anyNumber([ 'string', 3 ]) +//=> Any true + +anyNumber([ true, 'string' ]) +//=> Any false +``` + +
+ +## Implements +`Semigroup`, `Monoid` + +
+ +
+ +## Constructor Methods + +#### empty +```haskell +Any.empty :: () -> Any +``` + +`empty` provides the identity for the `Monoid` in that when the value it +provides is `concat`ed to any other value, it will return the other value. In +the case of `Any` the result of `empty` is `false`. `empty` is available on both +the Constructor and the Instance for convenience. + +```javascript +const Any = require('crocks/Any') + +Any.empty() //=> Any false + +Any(true).concat(Any.empty()) //=> Any true +Any(false).concat(Any.empty()) //=> Any false +``` + + +#### type +```haskell +Any.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 Any = require('crocks/Any') + +const Assign = require('crocks/Assign') +const isSameType = require('crocks/predicates/isSameType') + +Any.type() //=> "Any" + +isSameType(Any, Any(3)) //=> true +isSameType(Any, Any) //=> true +isSameType(Any(false), Assign) //=> false + +isSameType(Any, Assign({ food: 'always' })) +//=> false +``` + +
+ +
+ +## Instance Methods + +#### concat +```haskell +Any ~> Any -> Any +``` + +`concat` is used to combine (2) `Semigroup`s of the same type under an operation +specified by the `Semigroup`. In the case of `Any`, it will combine the two +using logical OR (disjunction). + +```javascript +const Any = require('crocks/Any') + +Any(true).concat(Any(true)) //=> Any true +Any(true).concat(Any(false)) //=> Any true +Any(false).concat(Any(true)) //=> Any true +Any(false).concat(Any(false)) //=> Any false +``` + +#### valueOf +```haskell +Any ~> () -> Boolean +``` + +`valueOf` is used on all `crocks` `Monoid`s as a means of extraction. While the +extraction is available, types that implement `valueOf` are not necessarily a +`Comonad`. This function is used primarily for convenience for some of the +helper functions that ship with `crocks`. Calling `value` on an `Any` instance +will result in the underlying `Boolean` value. + +```javascript +const Any = require('crocks/Any') + +Any(0).valueOf() //=> false +Any('string').valueOf() //=> true + +//=> true +Any(45) + .concat('') + .valueOf() +``` + +
diff --git a/docs/src/pages/docs/api/monoids.md b/docs/src/pages/docs/monoids/index.md similarity index 97% rename from docs/src/pages/docs/api/monoids.md rename to docs/src/pages/docs/monoids/index.md index 802c86f7e..07b629037 100644 --- a/docs/src/pages/docs/api/monoids.md +++ b/docs/src/pages/docs/monoids/index.md @@ -1,8 +1,9 @@ --- +title: "Monoids" description: "Monoids API" layout: "guide" -title: "Monoids" -weight: 2 +icon: "code-file" +weight: 3 --- ### Monoids diff --git a/docs/src/pages/index.soy b/docs/src/pages/index.soy index 568d3676f..c1ef82f53 100644 --- a/docs/src/pages/index.soy +++ b/docs/src/pages/index.soy @@ -88,22 +88,6 @@ description: "A collection of well known Monadic Containers for your utter enjoy {/template} -/** - * - */ -{template .how} -
-
-
-
-

It's easier than you think

-

Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Nullam id dolor id nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros praesent commodo ultricies vehicula ut.

-
-
-
-
-{/template} - /** * */