Skip to content

Commit

Permalink
move Star and Arrow from Semigroups to Semigroupoids
Browse files Browse the repository at this point in the history
  • Loading branch information
evilsoft committed May 21, 2017
1 parent af7e578 commit 6f6a86e
Show file tree
Hide file tree
Showing 20 changed files with 440 additions and 62 deletions.
86 changes: 79 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ All `Crocks` are Constructor functions of the given type, with `Writer` being an

| Crock | Constructor | Instance |
|---|:---|:---|
| `Arrow` | `empty` | `both`, `concat`, `contramap`, `empty`, `first`, `map`, `promap`, `runWith`, `second`, `value` |
| `Arrow` | `id` | `both`, `compose`, `contramap`, `empty`, `first`, `map`, `promap`, `runWith`, `second`, `value` |
| `Async` | `Rejected`, `Resolved`, `all`, `fromNode`, `fromPromise`, `of` | `alt`, `ap`, `bimap`, `chain`, `coalesce`, `fork`, `map`, `of`, `swap`, `toPromise` |
| `Const` | -- | `ap`, `chain`, `concat`, `equals`, `map`, `value` |
| `Either` | `Left`, `Right`, `of`| `alt`, `ap`, `bimap`, `chain`, `coalesce`, `concat`, `either`, `equals`, `map`, `of`, `sequence`, `swap`, `traverse` |
Expand All @@ -85,7 +85,7 @@ All `Crocks` are Constructor functions of the given type, with `Writer` being an
| `Pred` * | `empty` | `concat`, `contramap`, `empty`, `runWith`, `value` |
| `Reader` | `ask`, `of`| `ap`, `chain`, `map`, `of`, `runWith` |
| `Result` | `Err`, `Ok`, `of`| `alt`, `ap`, `bimap`, `chain`, `coalesce`, `concat`, `either`, `equals`, `map`, `of`, `sequence`, `swap`, `traverse` |
| `Star` | -- | `both`, `concat`, `contramap`, `map`, `promap`, `runWith` |
| `Star` | -- | `both`, `compose`, `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` |
Expand Down Expand Up @@ -264,6 +264,38 @@ const promFunc =
```
Due to the nature of the `then` function, only the head of your composition needs to return a `Promise`. This will create a function that takes a value, which is passed through your chain, returning a `Promise` which can be extended. This is only a `then` chain, it does not do anything with the `catch` function. If you would like to provide your functions in a left-to-right manner, check out [pipeP](#pipep).

#### composeS
```haskell
composeS : Semigroupoid s => (s y z, s x y, ..., s a b) -> s a z
```
When working with things like `Arrow` and `Star` there will come a point when you would like to compose them like you would any `Function`. That is where `composeS` comes in handy. Just pass it the `Semigroupoid`s you want to compose and it will give you back a new `Semigroupoid` of the same type with all of the underlying functions composed and ready to be run. Like [`compose`](#compose), `composeS` composes the functions in a right-to-left fashion. If you would like to represent your flow in a more left-to-right manner, then [`pipeS`](#pipeS) is provided for such things.

```js
const {
Arrow, bimap, branch, composeS, merge, mreduce, Sum
} = require('crocks')

const length =
xs => xs.length

const divide =
(x, y) => x / y

const avg =
Arrow(bimap(mreduce(Sum), length))
.promap(branch, merge(divide))

const double =
Arrow(x => x * 2)

const data =
[ 34, 198, 3, 43, 92 ]

composeS(double, avg)
.runWith(data)
// => 148
```

#### curry
```haskell
curry : ((a, b, ...) -> z) -> a -> b -> ... -> z
Expand Down Expand Up @@ -366,7 +398,7 @@ const data =
[ 13, 5, 13 ]

map(max10, data)
// [ 10, 5, 10]
// => [ 10, 5, 10]
```

#### pick
Expand All @@ -383,7 +415,7 @@ If you find yourself not able to come to terms with doing the typical right-to-l

#### pipeK
```haskell
pipe : Chain m => ((a -> m b), (b -> m c), ..., (y -> m z)) -> a -> m z
pipeK : Chain m => ((a -> m b), (b -> m c), ..., (y -> m z)) -> a -> m z
```
Like [`composeK`](#composek), you can remove much of the boilerplate when chaining together a series of functions with the signature: `Chain m => a -> m b`. The difference between the two functions is, while [`composeK`](composek) is right-to-left, `pipeK` is the opposite, taking its functions left-to-right.

Expand Down Expand Up @@ -436,6 +468,44 @@ const promPipe =
pipeP(proimse, doSomething, doAnother)
```

#### pipeS
```haskell
pipeS : Semigroupoid s => (s a b, s b c, ..., s y z) -> s a z
```
While `Star`s and `Arrow`s come in very handy at times, the only thing that could make them better is to compose them . With `pipeS` you can do just that with any `Semigroupoid`. Just like with [`composeS`](#composes), you just pass it `Semigroupoid`s of the same type and you will get back another `Semigroupoid` with them all composed together. The only difference between the two, is that `pipeS` composes in a left-to-right fashion, while [`composeS`](#composes) does the opposite.

```js
const {
curry, isNumber, pipeS, prop, safeLift, Star
} = require('../crocks')

const add = curry(
(x, y) => x + y
)

const pull =
x => Star(prop(x))

const safeAdd =
x => Star(safeLift(isNumber, add(x)))

const data = {
num: 56,
string: '56'
}

const flow = (key, num) => pipeS(
pull(key),
safeAdd(num)
)

flow('num', 10).runWith(data)
// => Just 66

flow('string', 100).runWith(data)
// => Nothing
```

#### prop
```haskell
prop : (String | Number) -> (Object | Array) -> Maybe a
Expand Down Expand Up @@ -538,8 +608,9 @@ All functions in this group have a signature of `* -> Boolean` and are used with
* `isApply : a -> Boolean`: an ADT that provides `map` and `ap` functions
* `isArray : a -> Boolean`: Array
* `isBoolean : a -> Boolean`: Boolean
* `isDefined : a -> Boolean`: Every value that is not `undefined`, `null` included
* `isCategory : a -> Boolean`: an ADT that provides `id` and `compose` functions
* `isChain : a -> Boolean`: an ADT that provides `map`, `ap` and `chain` functions
* `isDefined : a -> Boolean`: Every value that is not `undefined`, `null` included
* `isEmpty : a -> Boolean`: Empty Object, Array or String
* `isFoldable : a -> Boolean`: Array, List or any structure with a `reduce` function
* `isFunction : a -> Boolean`: Function
Expand All @@ -553,6 +624,7 @@ All functions in this group have a signature of `* -> Boolean` and are used with
* `isPromise : a -> Boolean`: An object implementing `then` and `catch`
* `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
Expand Down Expand Up @@ -635,11 +707,11 @@ These functions provide a very clean way to build out very simple functions and
| `both` | `Arrow`, `Function`, `Star` |
| `chain` | `Array`, `Async`, `Const`, `Either`, `Identity`, `IO`, `List`, `Maybe`, `Pair`, `Reader`, `Result`, `State`, `Unit`, `Writer` |
| `coalesce` | `Async`, `Either`, `Maybe`, `Result` |
| `concat` | `All`, `Any`, `Array`, `Arrow`, `Assign`, `Const`, `Either`, `Endo`, `Identity`, `List`, `Max`, `Maybe`, `Min`, `Pair`, `Pred`, `Prod`, `Result`, `Star`, `String`, `Sum`, `Unit` |
| `concat` | `All`, `Any`, `Array`, `Assign`, `Const`, `Either`, `Endo`, `Identity`, `List`, `Max`, `Maybe`, `Min`, `Pair`, `Pred`, `Prod`, `Result`, `String`, `Sum`, `Unit` |
| `cons` | `Array`, `List` |
| `contramap` | `Arrow`, `Pred`, `Star` |
| `either` | `Either`, `Maybe`, `Result` |
| `empty` | `All`, `Any`, `Array`, `Assign`,, `Endo`, `List`, `Max`, `Min`, `Object`, `Pred`, `Prod`, `String`, `Sum`, `Unit` |
| `empty` | `All`, `Any`, `Array`, `Assign`, `Endo`, `List`, `Max`, `Min`, `Object`, `Pred`, `Prod`, `String`, `Sum`, `Unit` |
| `evalWith` | `State` |
| `execWith` | `State` |
| `filter` | `Array`, `List`, `Object` |
Expand Down
4 changes: 4 additions & 0 deletions crocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const helpers = {
compose: require('./helpers/compose'),
composeK: require('./helpers/composeK'),
composeP: require('./helpers/composeP'),
composeS: require('./helpers/composeS'),
curry: require('./helpers/curry'),
defaultProps: require('./helpers/defaultProps'),
defaultTo: require('./helpers/defaultTo'),
Expand All @@ -59,6 +60,7 @@ const helpers = {
pipe: require('./helpers/pipe'),
pipeK: require('./helpers/pipeK'),
pipeP: require('./helpers/pipeP'),
pipeS: require('./helpers/pipeS'),
prop: require('./helpers/prop'),
propPath: require('./helpers/propPath'),
safe: require('./helpers/safe'),
Expand Down Expand Up @@ -134,6 +136,7 @@ const predicates = {
isApply: require('./predicates/isApply'),
isArray: require('./predicates/isArray'),
isBoolean: require('./predicates/isBoolean'),
isCategory: require('./predicates/isCategory'),
isChain: require('./predicates/isChain'),
isDefined: require('./predicates/isDefined'),
isEmpty: require('./predicates/isEmpty'),
Expand All @@ -150,6 +153,7 @@ const predicates = {
isSameType: require('./predicates/isSameType'),
isSetoid: require('./predicates/isSetoid'),
isSemigroup: require('./predicates/isSemigroup'),
isSemigroupoid: require('./predicates/isSemigroupoid'),
isString: require('./predicates/isString'),
isTraversable: require('./predicates/isTraversable')
}
Expand Down
8 changes: 8 additions & 0 deletions crocks.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const branch = require('./helpers/branch')
const compose = require('./helpers/compose')
const composeK = require('./helpers/composeK')
const composeP = require('./helpers/composeP')
const composeS = require('./helpers/composeS')
const curry = require('./helpers/curry')
const defaultProps = require('./helpers/defaultProps')
const defaultTo = require('./helpers/defaultTo')
Expand All @@ -38,6 +39,7 @@ const pick = require('./helpers/pick')
const pipe = require('./helpers/pipe')
const pipeK = require('./helpers/pipeK')
const pipeP = require('./helpers/pipeP')
const pipeS = require('./helpers/pipeS')
const prop = require('./helpers/prop')
const propPath = require('./helpers/propPath')
const safe = require('./helpers/safe')
Expand Down Expand Up @@ -123,6 +125,7 @@ const isApplicative = require('./predicates/isApplicative')
const isApply = require('./predicates/isApply')
const isArray = require('./predicates/isArray')
const isBoolean = require('./predicates/isBoolean')
const isCategory = require('./predicates/isCategory')
const isChain = require('./predicates/isChain')
const isDefined = require('./predicates/isDefined')
const isEmpty = require('./predicates/isEmpty')
Expand All @@ -138,6 +141,7 @@ const isObject = require('./predicates/isObject')
const isPromise = require('./predicates/isPromise')
const isSameType = require('./predicates/isSameType')
const isSemigroup = require('./predicates/isSemigroup')
const isSemigroupoid = require('./predicates/isSemigroupoid')
const isSetoid = require('./predicates/isSetoid')
const isString = require('./predicates/isString')
const isTraversable = require('./predicates/isTraversable')
Expand Down Expand Up @@ -172,6 +176,7 @@ test('entry', t => {
t.equal(crocks.compose, compose, 'provides the compose function')
t.equal(crocks.composeK, composeK, 'provides the composeK function')
t.equal(crocks.composeP, composeP, 'provides the composeP function')
t.equal(crocks.composeS, composeS, 'provides the composeS function')
t.equal(crocks.curry, curry, 'provides the curry function')
t.equal(crocks.defaultProps, defaultProps, 'provides the defaultProps function')
t.equal(crocks.defaultTo, defaultTo, 'provides the defaultTo function')
Expand All @@ -193,6 +198,7 @@ test('entry', t => {
t.equal(crocks.pipe, pipe, 'provides the pipe function')
t.equal(crocks.pipeK, pipeK, 'provides the pipeK function')
t.equal(crocks.pipeP, pipeP, 'provides the pipeP function')
t.equal(crocks.pipeS, pipeS, 'provides the pipeS function')
t.equal(crocks.prop, prop, 'provides the prop function')
t.equal(crocks.propPath, propPath, 'provides the propPath function')
t.equal(crocks.safe, safe, 'provides the safe function')
Expand Down Expand Up @@ -278,6 +284,7 @@ test('entry', t => {
t.equal(crocks.isApply, isApply, 'provides the isApply function')
t.equal(crocks.isArray, isArray, 'provides the isArray function')
t.equal(crocks.isBoolean, isBoolean, 'provides the isBoolean function')
t.equal(crocks.isCategory, isCategory, 'provides the isCategory function')
t.equal(crocks.isChain, isChain, 'provides the isChain function')
t.equal(crocks.isDefined, isDefined, 'provides the isDefined function')
t.equal(crocks.isEmpty, isEmpty, 'provides the isEmpty function')
Expand All @@ -293,6 +300,7 @@ test('entry', t => {
t.equal(crocks.isPromise, isPromise, 'provides the isPromise function')
t.equal(crocks.isSameType, isSameType, 'provides the isSameType function')
t.equal(crocks.isSemigroup, isSemigroup, 'provides the isSemigroup function')
t.equal(crocks.isSemigroupoid, isSemigroupoid, 'provides the isSemigroupoid function')
t.equal(crocks.isSetoid, isSetoid, 'provides the isSetoid function')
t.equal(crocks.isString, isString, 'provides the isString function')
t.equal(crocks.isTraversable, isTraversable, 'provides the isTraversable function')
Expand Down
33 changes: 15 additions & 18 deletions crocks/Arrow.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
/** @license ISC License (c) copyright 2016 original and current authors */
/** @author Ian Hofmann-Hicks (evil) */

const isFunction = require('../predicates/isFunction')

const isSameType = require('../predicates/isSameType')
const _inspect = require('../internal/inspect')

const compose = require('../helpers/compose')

const identity = require('../combinators/identity')
const composeB = require('../combinators/composeB')
const constant = require('../combinators/constant')
const identity = require('../combinators/identity')
const isFunction = require('../predicates/isFunction')
const isSameType = require('../predicates/isSameType')

const Pair = require('./Pair')

const _type =
constant('Arrow')

const _empty =
const _id =
() => Arrow(identity)

function Arrow(runWith) {
Expand All @@ -27,18 +24,18 @@ function Arrow(runWith) {
const type =
_type

const empty =
_empty

const value =
constant(runWith)

const inspect =
constant(`Arrow${_inspect(value())}`)

function concat(m) {
const id =
_id

function compose(m) {
if(!(isSameType(Arrow, m))) {
throw new TypeError('Arrow.concat: Arrow required')
throw new TypeError('Arrow.compose: Arrow required')
}

return map(m.runWith)
Expand All @@ -49,23 +46,23 @@ function Arrow(runWith) {
throw new TypeError('Arrow.map: Function required')
}

return Arrow(compose(fn, runWith))
return Arrow(composeB(fn, runWith))
}

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

return Arrow(compose(runWith, fn))
return Arrow(composeB(runWith, fn))
}

function promap(l, r) {
if(!isFunction(l) || !isFunction(r)) {
throw new TypeError('Arrow.promap: Functions required for both arguments')
}

return Arrow(compose(r, runWith, l))
return Arrow(composeB(r, composeB(runWith, l)))
}

function first() {
Expand Down Expand Up @@ -98,12 +95,12 @@ function Arrow(runWith) {

return {
inspect, type, value, runWith,
concat, empty, map, contramap,
id, compose, map, contramap,
promap, first, second, both
}
}

Arrow.empty = _empty
Arrow.id = _id
Arrow.type = _type

module.exports = Arrow
Loading

0 comments on commit 6f6a86e

Please sign in to comment.