diff --git a/README.md b/README.md index b2a6561dd..15442a715 100644 --- a/README.md +++ b/README.md @@ -85,12 +85,12 @@ All `Crocks` are Constructor functions of the given type, with `Writer` being an | `IO` | `of` | `ap`, `chain`, `map`, `of`, `run` | | `List` | `empty`, `fromArray`, `of` | `ap`, `chain`, `concat`, `cons`, `empty`, `equals`, `filter`, `head`, `map`, `of`, `reduce`, `reject`, `sequence`, `tail`, `toArray`, `traverse`, `value` | | `Maybe` | `Nothing`, `Just`, `of`, `zero` | `alt`, `ap`, `chain`, `coalesce`, `concat`, `equals`, `either`, `map`, `of`, `option`, `sequence`, `traverse`, `zero` | -| `Pair` | --- | `ap`, `bimap`, `chain`, `concat`, `equals`, `extend`, `fst`, `map`, `merge`, `of`, `snd`, `swap` | +| `Pair` | --- | `ap`, `bimap`, `chain`, `concat`, `equals`, `extend`, `fst`, `map`, `merge`, `of`, `snd`, `swap`, `toArray` | | `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`, `compose`, `contramap`, `map`, `promap`, `runWith` | -| `State` | `get`, `gets`, `modify` `of`, `put`| `ap`, `chain`, `evalWith`, `execWith`, `map`, `of`, `runWith` | +| `Star` | `id` | `both`, `compose`, `contramap`, `map`, `promap`, `runWith` | +| `State` | `get`, `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` | @@ -937,3 +937,4 @@ bad | `resultToFirst` | `Result e a -> First a` | `(a -> Result e b) -> a -> First b` | `crocks/First` | | `resultToLast` | `Result e a -> Last a` | `(a -> Result e b) -> a -> Last b` | `crocks/Last` | | `resultToMaybe` | `Result e a -> Maybe a` | `(a -> Result e b) -> a -> Maybe b` | `crocks/Maybe` | +| `writerToPair` | `Writer m a -> Pair m a` | `(a -> Writer m b) -> a -> Pair m b` | `crocks/Pair` | diff --git a/src/Pair/writerToPair.js b/src/Pair/writerToPair.js new file mode 100644 index 000000000..391da60a7 --- /dev/null +++ b/src/Pair/writerToPair.js @@ -0,0 +1,36 @@ +/** @license ISC License (c) copyright 2017 original and current authors */ +/** @author Ian Hofmann-Hicks (evil) */ + +const Pair = require('../core/Pair') +const curry = require('../core/curry') +const isFunction = require('../core/isFunction') + +const isWriter = + x => !!x && isFunction(x.read) + +const applyTransform = w => + Pair(w.log(), w.value()) + +// writerToPair : Monoid m => Writer m a -> Pair m a +// writerToPair : Monoid m => (a -> Writer m a) -> Pair m b +function writerToPair(writer) { + if(isFunction(writer)) { + return function(x) { + const m = writer(x) + + if(!isWriter(m)) { + throw new TypeError('writerToPair: Writer returing function required') + } + + return applyTransform(m) + } + } + + if(isWriter(writer)) { + return applyTransform(writer) + } + + throw new TypeError('writerToPair: Writer or Writer returing function required') +} + +module.exports = curry(writerToPair) diff --git a/src/Pair/writerToPair.spec.js b/src/Pair/writerToPair.spec.js new file mode 100644 index 000000000..140f17859 --- /dev/null +++ b/src/Pair/writerToPair.spec.js @@ -0,0 +1,84 @@ +const test = require('tape') +const helpers = require('../../test/helpers') +const Last = require('../../test/LastMonoid') + +const bindFunc = helpers.bindFunc + +const Pair = require('.') +const Writer = require('../Writer')(Last) + +const constant = require('../core/constant') +const identity = require('../core/identity') +const isFunction = require('../core/isFunction') +const isSameType = require('../core/isSameType') + +const writerToPair = require('./writerToPair') + +test('writerToPair transform', t => { + const f = bindFunc(writerToPair) + + t.ok(isFunction(writerToPair), 'is a function') + + const err = /writerToPair: Writer or Writer returing function required/ + t.throws(f(undefined), err, 'throws if arg is undefined') + t.throws(f(null), err, 'throws if arg is null') + t.throws(f(0), err, 'throws if arg is a falsey number') + t.throws(f(1), err, 'throws if arg is a truthy number') + t.throws(f(''), err, 'throws if arg is a falsey string') + t.throws(f('string'), err, 'throws if arg is a truthy string') + t.throws(f(false), err, 'throws if arg is false') + t.throws(f(true), err, 'throws if arg is true') + t.throws(f([]), err, 'throws if arg is an array') + t.throws(f({}), err, 'throws if arg is an object') + + t.end() +}) + +test('writerToPair with Writer', t => { + const value = 'something' + const log = 'log' + + const p = writerToPair(Writer(log, value)) + + t.ok(isSameType(Pair, p), 'returns a Pair') + + t.equals(p.snd(), value, 'second contains the value') + t.ok(isSameType(Last, p.fst()), 'first contains the Writers Monoid') + t.equals(p.fst().value(), log, 'first Monoid wraps the same log value from Writer') + + t.end() +}) + +test('writerToPair with Writer returning function', t => { + const value = 'something' + const log = 'log' + + t.ok(isFunction(constant(writerToPair(Writer(log, value)))), 'returns a function') + + const f = bindFunc(writerToPair(identity)) + + const err = /writerToPair: Writer returing function required/ + t.throws(f(undefined), err, 'throws if function returns undefined') + t.throws(f(null), err, 'throws if function returns null') + t.throws(f(0), err, 'throws if function returns a falsey number') + t.throws(f(1), err, 'throws if function returns a truthy number') + t.throws(f(''), err, 'throws if function returns a falsey string') + t.throws(f('string'), err, 'throws if function returns a truthy string') + t.throws(f(false), err, 'throws if function returns false') + t.throws(f(true), err, 'throws if function returns true') + t.throws(f([]), err, 'throws if function returns an array') + t.throws(f({}), err, 'throws if function returns an object') + + const lift = + x => Writer(log, x) + + const p = writerToPair(lift, value) + + t.ok(isSameType(Pair, p), 'returns a Pair') + + t.equals(p.snd(), value, 'second contains the value') + t.ok(isSameType(Last, p.fst()), 'first contains the Writers Monoid') + t.equals(p.fst().value(), log, 'first Monoid wraps the same log value from Writer') + + t.end() +}) diff --git a/src/core/Pair.js b/src/core/Pair.js index e996ee6a0..3328f55e2 100644 --- a/src/core/Pair.js +++ b/src/core/Pair.js @@ -23,6 +23,9 @@ function Pair(l, r) { const inspect = () => `Pair(${_inspect(l)},${_inspect(r)} )` + const toArray = + () => [ l, r ] + function merge(fn) { if(!isFunction(fn)) { throw new TypeError('Pair.merge: Binary function required') @@ -144,9 +147,10 @@ function Pair(l, r) { } return { - inspect, fst, snd, type, - merge, equals, concat, swap, - map, bimap, ap, chain, extend + inspect, fst, snd, toArray, + type, merge, equals, concat, + swap, map, bimap, ap, chain, + extend } } diff --git a/src/core/Pair.spec.js b/src/core/Pair.spec.js index 67abb0795..6a0094839 100644 --- a/src/core/Pair.spec.js +++ b/src/core/Pair.spec.js @@ -82,6 +82,15 @@ test('Pair snd', t => { t.end() }) +test('Pair toArray', t => { + const p = Pair(34, 'string') + + t.ok(isFunction(p.toArray), 'provides a toArray function') + t.same(p.toArray(), [ 34, 'string' ], 'returns an array with the Pairs values') + + t.end() +}) + test('Pair merge', t => { const p = Pair(1, 20) diff --git a/src/index.js b/src/index.js index 1b3c9b1bf..dc7cc19b7 100644 --- a/src/index.js +++ b/src/index.js @@ -195,7 +195,8 @@ const transforms = { resultToEither: require('./Either/resultToEither'), resultToFirst: require('./First/resultToFirst'), resultToLast: require('./Last/resultToLast'), - resultToMaybe: require('./Maybe/resultToMaybe') + resultToMaybe: require('./Maybe/resultToMaybe'), + writerToPair: require('./Pair/writerToPair') } module.exports = Object.assign( diff --git a/src/index.spec.js b/src/index.spec.js index 6caff2db7..f336cb79f 100644 --- a/src/index.spec.js +++ b/src/index.spec.js @@ -189,6 +189,7 @@ const resultToEither = require('./Either/resultToEither') const resultToFirst = require('./First/resultToFirst') const resultToLast = require('./Last/resultToLast') const resultToMaybe = require('./Maybe/resultToMaybe') +const writerToPair = require('./Pair/writerToPair') test('entry', t => { t.equal(crocks.toString(), '[object Object]', 'is an object') @@ -381,6 +382,7 @@ test('entry', t => { t.equal(crocks.resultToFirst, resultToFirst, 'provides the resultToFirst transform') t.equal(crocks.resultToLast, resultToLast, 'provides the resultToLast transform') t.equal(crocks.resultToMaybe, resultToMaybe, 'provides the resultToMaybe transform') + t.equal(crocks.writerToPair, writerToPair, 'provides the writerToPair transform') t.end() })