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

Move Star and Arrow from Semigroups to Semigroupoids #127

Merged
merged 1 commit into from
May 21, 2017
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
102 changes: 87 additions & 15 deletions README.md

Large diffs are not rendered by default.

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
36 changes: 18 additions & 18 deletions crocks/Arrow.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ test('Arrow', t => {

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

t.ok(isFunction(Arrow.empty), 'provides an empty function')
t.ok(isFunction(Arrow.type), 'provides a type function')
t.ok(isFunction(Arrow.id), 'provides an id function')

t.ok(isObject(Arrow(unit)), 'returns an object')

Expand Down Expand Up @@ -85,7 +85,7 @@ test('Arrow runWith', t => {
t.end()
})

test('Arrow concat functionality', t => {
test('Arrow compose functionality', t => {
const f = x => x + 1
const g = x => x * 0

Expand All @@ -97,7 +97,7 @@ test('Arrow concat functionality', t => {

const notArrow = { type: constant('Arrow...Not') }

const cat = bindFunc(a.concat)
const cat = bindFunc(a.compose)

t.throws(cat(undefined), TypeError, 'throws with undefined')
t.throws(cat(null), TypeError, 'throws with null')
Expand All @@ -111,48 +111,48 @@ test('Arrow concat functionality', t => {
t.throws(cat({}), TypeError, 'throws with an object')
t.throws(cat(notArrow), TypeError, 'throws with non-Arrow')

t.same(a.concat(b).runWith(x), result, 'builds composition as expected')
t.same(a.compose(b).runWith(x), result, 'builds composition as expected')

t.end()
})

test('Arrow concat properties (Semigroup)', t => {
test('Arrow compose properties (Semigroupoid)', 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')
t.ok(isFunction(Arrow(identity).compose), 'is a function')

const left = a.concat(b).concat(c).runWith
const right = a.concat(b.concat(c)).runWith
const left = a.compose(b).compose(c).runWith
const right = a.compose(b.compose(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.same(a.compose(b).type(), a.type(), 'returns Semigroupoid of same type')

t.end()
})

test('Arrow empty functionality', t => {
const m = Arrow(unit).empty()
test('Arrow id functionality', t => {
const m = Arrow(unit).id()

t.equal(m.empty, Arrow.empty, 'static and instance versions are the same')
t.equal(m.id, Arrow.id, 'static and instance versions are the same')

t.equal(m.type(), 'Arrow', 'provides an Arrow')
t.equal(m.type(), Arrow.type(), 'provides an Arrow')
t.same(m.runWith(13), 13, 'wraps an identity function')

t.end()
})

test('Arrow empty properties (Monoid)', t => {
test('Arrow id properties (Category)', 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')
t.ok(isFunction(m.compose), 'provides a compose function')
t.ok(isFunction(m.id), 'provides an id function')

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

t.same(right(x), m.runWith(x), 'right identity')
t.same(left(x), m.runWith(x), 'left identity')
Expand Down
8 changes: 4 additions & 4 deletions crocks/Star.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const isSameType = require('../predicates/isSameType')

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

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

const merge = require('../pointfree/merge')
Expand All @@ -30,7 +30,7 @@ function Star(runWith) {
const inspect =
constant(`Star${_inspect(runWith)}`)

function concat(s) {
function compose(s) {
if(!isSameType(Star, s)) {
throw new TypeError('Star.concat: Star required')
}
Expand Down Expand Up @@ -75,7 +75,7 @@ function Star(runWith) {
throw new TypeError('Star.contramap: Function required')
}

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

function promap(l, r) {
Expand Down Expand Up @@ -144,7 +144,7 @@ function Star(runWith) {
}

return {
inspect, type, runWith, concat, map,
inspect, type, runWith, compose, map,
contramap, promap, first, second, both
}
}
Expand Down
20 changes: 10 additions & 10 deletions crocks/Star.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ test('Star runWith', t => {
t.end()
})

test('Star concat functionality', t => {
test('Star compose functionality', t => {
const f = x => MockCrock(x + 1)

const a = Star(f)

const notStar = { type: constant('Star...Not') }
const notMock = { type: constant('Mock...Not') }

const cat = bindFunc(a.concat)
const cat = bindFunc(a.compose)

t.throws(cat(undefined), TypeError, 'throws with undefined')
t.throws(cat(null), TypeError, 'throws with null')
Expand All @@ -98,7 +98,7 @@ test('Star concat functionality', t => {
t.throws(cat({}), TypeError, 'throws with an object')
t.throws(cat(notStar), TypeError, 'throws with non-Star')

const noMonadFst = bindFunc(Star(identity).concat(a).runWith)
const noMonadFst = bindFunc(Star(identity).compose(a).runWith)

t.throws(noMonadFst(undefined), TypeError, 'throws when first computation returns undefined')
t.throws(noMonadFst(null), TypeError, 'throws when first computation returns null')
Expand All @@ -111,7 +111,7 @@ test('Star concat functionality', t => {
t.throws(noMonadFst({}), TypeError, 'throws when first computation returns false')
t.throws(noMonadFst([]), TypeError, 'throws when first computation returns true')

const noMonadSnd = bindFunc(x => a.concat(Star(constant(x))).runWith(10))
const noMonadSnd = bindFunc(x => a.compose(Star(constant(x))).runWith(10))

t.throws(noMonadSnd(undefined), TypeError, 'throws when second computation returns undefined')
t.throws(noMonadSnd(null), TypeError, 'throws when second computation returns null')
Expand All @@ -129,26 +129,26 @@ test('Star concat functionality', t => {
const g = x => MockCrock(x * 10)

const chained = f(x).chain(g).value()
const star = a.concat(Star(g)).runWith(x).value()
const star = a.compose(Star(g)).runWith(x).value()

t.equal(chained, star, 'builds composition as expected')

t.end()
})

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

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

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

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

t.end()
})
Expand Down
1 change: 1 addition & 0 deletions helpers/compose.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function applyPipe(f, g) {
return g.call(null, f.apply(null, argsArray(arguments)))
}
}

// compose : ((y -> z), (x -> y), ..., (a -> b)) -> a -> z
function compose() {
if(!arguments.length) {
Expand Down
Loading