Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Pred crock #16

Merged
merged 5 commits into from
Dec 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,26 @@ There are (5) classifications of "things" included in this library:
### Crocks
The `Crocks` are the heart and soul of this library. This is where you will find all your favorite ADT's you have grown to :heart:. They include gems such as: `Maybe`, `Either` and `IO`, to name a few. The are usually just a simple constructor that takes either a function or value (depending on the type) and will return you a "container" that wraps whatever you passed it. Each container provides a variety of functions that act as the operations you can do on the contained value. There are many types that share the same function names, but what they do from type to type may vary. Every `Crock` provides `type` function on the Constructor and both `inspect` and `type` functions on their Instances.

All `Crocks` are Constructor functions of the given type, with `Writer` being an exception. The `Writer` function takes a `Monoid` that will represent the `log`. Once you provide the `Monoid`, the function will return the `Writer` Constructor for your `Writer` for that specific `Monoid`.
All `Crocks` are Constructor functions of the given type, with `Writer` being an exception. The `Writer` function takes a `Monoid` that will represent the `log`. Once you provide the `Monoid`, the function will return the `Writer` Constructor for your `Writer` using that specific `Monoid`.

| Crock | Constructor | Instance |
|---|---|---|
| `Arrow` | `empty` | `value`, `runWith`, `concat`, `empty`, `map`, `contramap`, `promap`, `first`, `second` |
| `Const` | -- | `equals`, `value`, `concat`, `map`, `ap`, `chain` |
| `Either` | `Left`, `Right`, `of`| `equals`, `value`, `either`, `swap`, `coalesce`, `map`, `bimap`, `ap`, `of`, `chain`, `sequence`, `traverse` |
| `Identity` | `of` | `equals`, `value`, `map`, `ap`, `of`, `chain`, `sequence`, `traverse` |
| `IO` | `of` | `run`, `map`, `ap`, `of`, `chain` |
| `List` | `empty`, `of` | `equals`, `value`, `head`, `tail`, `cons`, `concat`, `empty`, `reduce`, `filter`, `map`, `ap`, `of`, `chain`, `sequence`, `traverse` |
| `Maybe` | `Nothing`, `Just`, `of` | `equals`, `maybe`, `either`, `option`, `coalesce`, `map`, `ap`, `of`, `chain`, `sequence`, `traverse` |
| `Pair` | `of` | `equals`, `value`, `fst`, `snd`, `merge`, `concat`, `swap`, `map`, `bimap`, `ap`, `of`, `chain` |
| `Reader` | `ask`, `of`| `runWith`, `map`, `ap`, `of`, `chain` |
| `Star` | -- | `runWith`, `map`, `contramap`, `promap` |
| `State` | `get`, `gets`, `put`, `modify` `of`| `runWith`, `execWith`, `evalWith`, `map`, `ap`, `of`, `chain` |
| `Unit` | `empty`, `of` | `equals`, `value`, `concat`, `empty`, `map`, `ap`, `of`, `chain` |
| `Writer`| `of` | `equals`, `value`, `log`, `read`, `map`, `ap`, `of`, `chain` |
| `Arrow` | `empty` | `concat`, `contramap`, `empty`, `first`, `map`, `promap`, `runWith`, `second`, `value` |
| `Const` | -- | `ap`, `chain`, `concat`, `equals`, `map`, `value` |
| `Either` | `Left`, `Right`, `of`| `ap`, `bimap`, `chain`, `coalesce`, `either`, `equals`, `map`, `of`, `sequence`, `swap`, `traverse`, `value` |
| `Identity` | `of` | `ap`, `chain`, `equals`, `map`, `of`, `sequence`, `traverse`, `value` |
| `IO` | `of` | `ap`, `chain`, `map`, `of`, `run` |
| `List` | `empty`, `of` | `ap`, `chain`, `concat`, `cons`, `empty`, `equals`, `filter`, `head`, `map`, `of`, `reduce`, `sequence`, `tail`, `traverse`, `value` |
| `Maybe` | `Nothing`, `Just`, `of` | `ap`, `chain`, `coalesce`, `equals`, `either`, `map`, `maybe`, `of`, `option`, `sequence`, `traverse` |
| `Pair` | `of` | `ap`, `bimap`, `chain`, `concat`, `equals`, `fst`, `map`, `merge`, `of`, `snd`, `swap`, `value` |
| `Pred`[*] | `empty` | `concat`, `contramap`, `empty`, `runWith`, `value` |
| `Reader` | `ask`, `of`| `ap`, `chain`, `map`, `of`, `runWith` |
| `Star` | -- | `contramap`, `map`, `promap`, `runWith` |
| `State` | `get`, `gets`, `modify` `of`, `put`| `ap`, `chain`, `evalWith`, `execWith`, `map`, `of`, `runWith` |
| `Unit` | `empty`, `of` | `ap`, `chain`, `concat`, `empty`, `equals`, `map`, `of`, `value` |
| `Writer`| `of` | `ap`, `chain`, `equals`, `log`, `map`, `of`, `read`, `value` |

[*] based on [this article](https://medium.com/@drboolean/monoidal-contravariant-functors-are-actually-useful-1032211045c4#.polugsx2a)

### Monoids
Each `Monoid` provides a means to represent a binary operation and is usually locked down to a specific type. These are great when you need to combine a list of values down to one value. In this library, any ADT that provides both an `empty` and `concat` function can be used as a `Monoid`. There are a few of the `Crocks` that are also monoidial, so be on the look out for those as well. All `Monoids` work with the point-free functions `mconcat`, `mreduce`, `mconcatMap` and `mreduceMap`.
Expand Down Expand Up @@ -229,9 +232,9 @@ These functions provide a very clean way to build out very simple functions and
| `bimap` | `Either`, `Pair` |
| `chain` | `Const`, `Either`, `Identity`, `IO`, `List`, `Maybe`, `Pair`, `Reader`, `State`, `Unit`, `Writer` |
| `coalesce` | `Maybe`, `Either` |
| `concat` | `Array`, `String`, `Arrow`, `Const`, `List`, `Pair`, `Unit`, `All`, `Any`, `Assign`, `Max`, `Min`, `Prod`, `Sum` |
| `concat` | `All`, `Any`, `Array`, `Arrow`, `Assign`, `Const`, `List`, `Max`, `Min`, `Pair`, `Pred`, `Prod`, `String`, `Sum`, `Unit` |
| `cons` | `Array`, `List` |
| `contramap` | `Arrow`, `Star` |
| `contramap` | `Arrow`, `Pred`, `Star` |
| `either` | `Either`, `Maybe` |
| `evalWith` | `State` |
| `execWith` | `State` |
Expand All @@ -240,19 +243,19 @@ These functions provide a very clean way to build out very simple functions and
| `fst` | `Pair` |
| `head` | `Array, List` |
| `log` | `Writer` |
| `map` | `Array`, `Function`, `Arrow`, `Const`, `Either`, `Identity`, `IO`, `List`, `Maybe`, `Pair`, `Reader`, `Star`, `State`, `Unit`, `Writer` |
| `map` | `Array`, `Arrow`, `Const`, `Either`, `Function`, `Identity`, `IO`, `List`, `Maybe`, `Pair`, `Reader`, `Star`, `State`, `Unit`, `Writer` |
| `maybe` | `Maybe` |
| `merge` | `Pair` |
| `option` | `Either`, `Maybe` |
| `promap` | `Arrow`, `Star` |
| `read` | `Writer` |
| `reduce` | `Array`, `List` |
| `run` | `IO` |
| `runWith` | `Arrow`, `Reader`, `Star`, `State` |
| `runWith` | `Arrow`, `Pred`, `Reader`, `Star`, `State` |
| `second` | `Arrow` |
| `sequence` | `Either`, `Identity`, `List`, `Maybe` |
| `snd` | `Pair` |
| `swap` | `Pair` |
| `tail` | `Array`, `String`, `List` |
| `tail` | `Array`, `List`, `String` |
| `traverse` | `Either`, `Identity`, `List`, `Maybe` |
| `value` | `Arrow`, `Const`, `Either`, `Identity`, `List`, `Pair`, `Unit`, `Writer` |
| `value` | `Arrow`, `Const`, `Either`, `Identity`, `List`, `Pair`, `Pred`, `Unit`, `Writer` |
1 change: 1 addition & 0 deletions crocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const crocks = {
List: require('./crocks/List'),
Maybe: require('./crocks/Maybe'),
Pair: require('./crocks/Pair'),
Pred: require('./crocks/Pred'),
Reader: require('./crocks/Reader'),
Star: require('./crocks/Star'),
State: require('./crocks/State'),
Expand Down
2 changes: 2 additions & 0 deletions crocks.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const IO = require('./crocks/IO')
const List = require('./crocks/List')
const Maybe = require('./crocks/Maybe')
const Pair = require('./crocks/Pair')
const Pred = require('./crocks/Pred')
const Reader = require('./crocks/Reader')
const Star = require('./crocks/Star')
const State = require('./crocks/State')
Expand Down Expand Up @@ -145,6 +146,7 @@ test('entry', t => {
t.equal(crocks.List, List, 'provides the List function')
t.equal(crocks.Maybe, Maybe, 'provides the Maybe function')
t.equal(crocks.Pair, Pair, 'provides the Pair function')
t.equal(crocks.Pred, Pred, 'provides the Pred function')
t.equal(crocks.Reader, Reader, 'provides the Reader function')
t.equal(crocks.Star, Star, 'provides the Star function')
t.equal(crocks.State, State, 'provides the State function')
Expand Down
52 changes: 26 additions & 26 deletions crocks/Arrow.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,23 +85,6 @@ test('Arrow runWith', t => {
t.end()
})

test('Arrow concat properties (Semigroup)', t => {
const a = Arrow(x => x + 1)
const b = Arrow(x => x * 10)
const c = Arrow(x => x - 5)

t.ok(isFunction(Arrow(identity).concat), 'is a function')

const left = a.concat(b).concat(c).runWith
const right = a.concat(b.concat(c)).runWith
const x = 20

t.same(left(x), right(x), 'associativity')
t.same(a.concat(b).type(), a.type(), 'returns Semigroup of same type')

t.end()
})

test('Arrow concat functionality', t => {
const f = x => x + 1
const g = x => x * 0
Expand Down Expand Up @@ -133,18 +116,19 @@ test('Arrow concat functionality', t => {
t.end()
})

test('Arrow empty properties (Monoid)', t => {
const m = Arrow(x => x + 45)
const x = 32
test('Arrow concat properties (Semigroup)', t => {
const a = Arrow(x => x + 1)
const b = Arrow(x => x * 10)
const c = Arrow(x => x - 5)

t.ok(isFunction(m.concat), 'provides a concat function')
t.ok(isFunction(m.empty), 'provides a empty function')
t.ok(isFunction(Arrow(identity).concat), 'is a function')

const right = m.concat(m.empty()).runWith
const left = m.empty().concat(m).runWith
const left = a.concat(b).concat(c).runWith
const right = a.concat(b.concat(c)).runWith
const x = 20

t.same(right(x), m.runWith(x), 'right identity')
t.same(left(x), m.runWith(x), 'left identity')
t.same(left(x), right(x), 'associativity')
t.same(a.concat(b).type(), a.type(), 'returns Semigroup of same type')

t.end()
})
Expand All @@ -160,6 +144,22 @@ test('Arrow empty functionality', t => {
t.end()
})

test('Arrow empty properties (Monoid)', t => {
const m = Arrow(x => x + 45)
const x = 32

t.ok(isFunction(m.concat), 'provides a concat function')
t.ok(isFunction(m.empty), 'provides a empty function')

const right = m.concat(m.empty()).runWith
const left = m.empty().concat(m).runWith

t.same(right(x), m.runWith(x), 'right identity')
t.same(left(x), m.runWith(x), 'left identity')

t.end()
})

test('Arrow map errors', t => {
const map = bindFunc(Arrow(noop).map)

Expand Down
2 changes: 1 addition & 1 deletion crocks/IO.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const _of =
x => IO(constant(x))

function IO(run) {
if(!arguments.length || !isFunction(run)) {
if(!isFunction(run)) {
throw new TypeError('IO: Must wrap a function')
}

Expand Down
61 changes: 61 additions & 0 deletions crocks/Pred.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/** @license ISC License (c) copyright 2016 original and current authors */
/** @author Ian Hofmann-Hicks (evil) */

const isFunction = require('../internal/isFunction')
const isType = require('../internal/isType')

const _inspect = require('../funcs/inspect')

const constant = require('../combinators/constant')
const composeB = require('../combinators/composeB')

const _type =
constant('Pred')

const _empty =
() => Pred(constant(true))

function Pred(runWith) {
if(!isFunction(runWith)) {
throw new TypeError('Pred: Predicate function required')
}

const type =
_type

const inspect =
constant(`Pred${_inspect(runWith)}`)

const empty =
_empty

const value =
constant(runWith)

function concat(m) {
if(!(m && isType(type(), m))) {
throw new TypeError('Pred.concat: Pred required')
}

return Pred(x => runWith(x) && m.runWith(x))
}

function contramap(fn) {
if(!isFunction(fn)) {
throw new TypeError('Pred.contramap: Function required')
}

return Pred(composeB(runWith, fn))
}

return {
runWith, inspect, type,
value, empty, concat,
contramap
}
}

Pred.empty = _empty
Pred.type = _type

module.exports = Pred
Loading