From 9b195a7f357ef7403959011fd79e721d8fd527c1 Mon Sep 17 00:00:00 2001 From: Kyle Simpson Date: Tue, 19 Jan 2021 16:10:44 -0600 Subject: [PATCH] fixed a few quirks with some helpers (boolean truthiness) and added 'isMonad(..)' helper --- README.md | 2 +- package.json | 2 +- src/async-either.js | 4 ++-- src/either.js | 3 ++- src/io-event-stream.js | 4 ++-- src/io-helpers.js | 16 ++++++++-------- src/io.js | 18 +++++++----------- src/just.js | 8 +++++--- src/lib/util.js | 25 +++++++++++++++++++++++-- src/maybe.js | 3 ++- src/nothing.js | 4 +++- 11 files changed, 56 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 18c54c4..6c6fc5f 100644 --- a/README.md +++ b/README.md @@ -158,4 +158,4 @@ Then open up `coverage/lcov-report/index.html` in a browser to view the report. ## License -All code and documentation are (c) 2020 Kyle Simpson and released under the [MIT License](http://getify.mit-license.org/). A copy of the MIT License [is also included](LICENSE.txt). +All code and documentation are (c) 2021 Kyle Simpson and released under the [MIT License](http://getify.mit-license.org/). A copy of the MIT License [is also included](LICENSE.txt). diff --git a/package.json b/package.json index 7e50f6c..f090c9c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "monio", "description": "Async-capable IO monad for JS", - "version": "0.13.1", + "version": "0.14.0", "main": "./src/index.js", "scripts": { "build": "node scripts/", diff --git a/src/async-either.js b/src/async-either.js index 52dc64f..97e289f 100644 --- a/src/async-either.js +++ b/src/async-either.js @@ -1,6 +1,6 @@ "use strict"; -var { isPromise, } = require("./lib/util.js"); +var { isFunction, isPromise, } = require("./lib/util.js"); var Either = require("./either.js"); var brand = {}; @@ -111,7 +111,7 @@ function fromPromise(pr) { } function is(val) { - return val && typeof val._is == "function" && val._is(brand); + return val && isFunction(val._is) && val._is(brand); } function fromFoldable(m) { diff --git a/src/either.js b/src/either.js index 261ba81..2f34d34 100644 --- a/src/either.js +++ b/src/either.js @@ -1,5 +1,6 @@ "use strict"; +var { isFunction, } = require("./lib/util.js"); var Just = require("./just.js"); var brand = {}; @@ -83,7 +84,7 @@ function LeftOrRight(val,isRight = true) { } function is(val) { - return val && typeof val._is == "function" && val._is(brand); + return val && isFunction(val._is) && val._is(brand); } function fromFoldable(m) { diff --git a/src/io-event-stream.js b/src/io-event-stream.js index e17c08d..fbf9d13 100644 --- a/src/io-event-stream.js +++ b/src/io-event-stream.js @@ -244,7 +244,7 @@ function pullFromStreams(streams) { .map(function callIter(v,idx){ // stream (iterator) waiting for its // next() call? - if (v && typeof v.next == "function") { + if (v && isFunction(v.next)) { streams[idx] = (async function getNext(){ var pr = v.next(); try { @@ -274,7 +274,7 @@ function pullFromStreams(streams) { function close(...streams) { return IO(() => ( Promise.all(streams.map(async function closeStream(stream){ - if (stream && typeof stream.return == "function") { + if (stream && isFunction(stream.return)) { try { return await stream.return(); } diff --git a/src/io-helpers.js b/src/io-helpers.js index f986711..6e02f56 100644 --- a/src/io-helpers.js +++ b/src/io-helpers.js @@ -1,6 +1,6 @@ "use strict"; -var { isPromise, } = require("./lib/util.js"); +var { isFunction, isPromise, isMonad, } = require("./lib/util.js"); var IO = require("./io.js"); var Maybe = require("./maybe.js"); var Either = require("./either.js"); @@ -156,10 +156,10 @@ function els(thens) { function iReturn(val) { return IO(env => ( liftIO(env,val) - .chain(v => { + .map(v => { var ret = { returned: v, }; returnedValues.add(ret); - return IO.of(ret); + return ret; }) .run(env) )); @@ -168,7 +168,7 @@ function iReturn(val) { function iNot(val) { return IO(env => ( liftIO(env,val) - .chain(v => IO.of(!v)) + .map(not) .run(env) )); } @@ -179,7 +179,7 @@ function wasReturned(v) { function liftIO(env,v) { // monad? - if (v && typeof v == "object" && typeof v.chain == "function") { + if (isMonad(v)) { // already an IO? if (IO.is(v)) { return v; @@ -205,12 +205,12 @@ function liftIO(env,v) { } } } - // function? - else if (typeof v == "function") { + // non-monad function? + else if (isFunction(v)) { return liftIO(env,v(env)); } // iterator? - else if (v && typeof v == "object" && typeof v.next == "function") { + else if (v && typeof v == "object" && isFunction(v.next)) { return IO.do(v); } // fallback: wrap whatever value this is in an IO diff --git a/src/io.js b/src/io.js index bfa9e3e..ff0f4bb 100644 --- a/src/io.js +++ b/src/io.js @@ -1,6 +1,6 @@ "use strict"; -var { isPromise, } = require("./lib/util.js"); +var { isFunction, isPromise, } = require("./lib/util.js"); var Either = require("./either.js"); var brand = {}; @@ -64,8 +64,8 @@ function IO(effect) { function _inspect() { return `${publicAPI[Symbol.toStringTag]}(${ - typeof effect == "function" ? (effect.name || "anonymous function") : - (effect && typeof effect._inspect == "function") ? effect._inspect() : + isFunction(effect) ? (effect.name || "anonymous function") : + (effect && isFunction(effect._inspect)) ? effect._inspect() : val })`; } @@ -81,7 +81,7 @@ function of(v) { } function is(v) { - return v && typeof v._is == "function" && v._is(brand); + return v && isFunction(v._is) && v._is(brand); } function processNext(next,respVal,outerV,throwEither = false) { @@ -171,16 +171,12 @@ function doEither(block) { function getIterator(block,v) { return ( - typeof block == "function" ? block(v) : - (block && typeof block == "object" && typeof block.next == "function") ? block : + isFunction(block) ? block(v) : + (block && typeof block == "object" && isFunction(block.next)) ? block : undefined ); } function monadFlatMap(m,fn) { - return m[ - "flatMap" in m ? "flatMap" : - "chain" in m ? "chain" : - "bind" - ](fn); + return getMonadFlatMap(m).call(m,fn); } diff --git a/src/just.js b/src/just.js index 4e20d42..5192a70 100644 --- a/src/just.js +++ b/src/just.js @@ -1,5 +1,7 @@ "use strict"; +var { isFunction, } = require("./lib/util.js"); + var brand = {}; module.exports = Object.assign(Just,{ @@ -44,8 +46,8 @@ function Just(val) { return ( typeof val == "string" ? `"${ val }"` : typeof val == "undefined" ? "" : - typeof val == "function" ? (val.name || "anonymous function") : - val && typeof val._inspect == "function" ? val._inspect() : + isFunction(val) ? (val.name || "anonymous function") : + val && isFunction(val._inspect) ? val._inspect() : Array.isArray(val) ? `[${ val.map(v => v == null ? String(v) : _serialize(v)) }]` : String(val) ); @@ -58,5 +60,5 @@ function Just(val) { } function is(val) { - return val && typeof val._is == "function" && val._is(brand); + return val && isFunction(val._is) && val._is(brand); } diff --git a/src/lib/util.js b/src/lib/util.js index 86059f8..cce3b1f 100644 --- a/src/lib/util.js +++ b/src/lib/util.js @@ -1,23 +1,44 @@ "use strict"; module.exports = { + getMonadFlatMap, isFunction, isPromise, + isMonad, curry, }; +module.exports.getMonadFlatMap = getMonadFlatMap; module.exports.isFunction = isFunction; module.exports.isPromise = isPromise; +module.exports.isMonad = isMonad; module.exports.curry = curry; // ************************** +function getMonadFlatMap(m) { + return m[ + "flatMap" in m ? "flatMap" : + "chain" in m ? "chain" : + "bind" + ]; +} + function isFunction(v) { - return v && typeof v == "function"; + return !!(v && typeof v == "function"); } function isPromise(v) { - return v && typeof v.then == "function"; + return !!(v && isFunction(v.then)); +} + +// duck-type check for monad'ness +function isMonad(v) { + return !!( + v && + (typeof v == "object" || isFunction(v)) && + isFunction(getMonadFlatMap(v)) + ); } function curry(fn,arity = fn.length) { diff --git a/src/maybe.js b/src/maybe.js index 6b59acf..b7fa801 100644 --- a/src/maybe.js +++ b/src/maybe.js @@ -1,5 +1,6 @@ "use strict"; +var { isFunction, } = require("./lib/util.js"); var Just = require("./just.js"); var Nothing = require("./nothing.js"); @@ -86,7 +87,7 @@ function Maybe(val) { } function is(val) { - return val && typeof val._is == "function" && val._is(brand); + return val && isFunction(val._is) && val._is(brand); } function from(val) { diff --git a/src/nothing.js b/src/nothing.js index 5b6ca00..af2ba96 100644 --- a/src/nothing.js +++ b/src/nothing.js @@ -1,5 +1,7 @@ "use strict"; +var { isFunction, } = require("./lib/util.js"); + var brand = {}; module.exports = Object.assign(Nothing,{ @@ -35,7 +37,7 @@ function Nothing() { } function is(val) { - return val && typeof val._is == "function" && val._is(brand); + return val && isFunction(val._is) && val._is(brand); } // default isEmpty(), can be overidden