├── .gitignore ├── .jscsrc ├── .jshintrc ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── bower.json ├── dist ├── ramda-fantasy.js └── ramda-fantasy.min.js ├── docs ├── Either.md ├── Future.md ├── IO.md ├── Identity.md ├── Maybe.md ├── Reader.md ├── State.md └── Tuple.md ├── index.js ├── package.json ├── src ├── Either.js ├── Future.js ├── IO.js ├── Identity.js ├── Maybe.js ├── Reader.js ├── State.js ├── Tuple.js ├── internal │ └── util.js ├── lift2.js └── lift3.js └── test ├── either.test.js ├── future.test.js ├── identity.test.js ├── io.test.js ├── lift2.test.js ├── lift3.test.js ├── maybe.test.js ├── reader.test.js ├── state.test.js ├── tuple.test.js ├── types.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /npm-debug.log 3 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "disallowKeywordsOnNewLine": ["else", "catch", "finally"], 3 | "disallowMixedSpacesAndTabs": true, 4 | "disallowMultipleLineStrings": true, 5 | "disallowQuotedKeysInObjects": "allButReserved", 6 | "disallowSpaceAfterObjectKeys": true, 7 | "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], 8 | "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], 9 | "disallowSpacesInCallExpression": true, 10 | "disallowSpacesInFunction": {"beforeOpeningRoundBrace": true}, 11 | "disallowSpacesInsideArrayBrackets": true, 12 | "disallowSpacesInsideObjectBrackets": true, 13 | "disallowSpacesInsideParentheses": true, 14 | "disallowTrailingWhitespace": true, 15 | "disallowYodaConditions": true, 16 | "requireCapitalizedConstructors": true, 17 | "requireCommaBeforeLineBreak": true, 18 | "requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch", "finally"], 19 | "requireDotNotation": true, 20 | "requireLineFeedAtFileEnd": true, 21 | "requireParenthesesAroundIIFE": true, 22 | "requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<=", ",", ":"], 23 | "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch", "finally"], 24 | "requireSpaceAfterLineComment": true, 25 | "requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="], 26 | "requireSpaceBeforeBlockStatements": true, 27 | "requireSpaceBeforeKeywords": ["else", "catch", "finally"], 28 | "requireSpaceBetweenArguments": true, 29 | "requireSpacesInConditionalExpression": true, 30 | "requireSpacesInFunction": {"beforeOpeningCurlyBrace": true}, 31 | "validateIndentation": 2, 32 | "validateQuoteMarks": {"escape": true, "mark": "'"} 33 | } 34 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "eqnull": true, 3 | "globals": { 4 | "Symbol": true 5 | }, 6 | "newcap": false, 7 | "predef": ["beforeEach", "console", "define", "describe", "it", "module", "process", "require"], 8 | "undef": true, 9 | "unused": true 10 | } 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /npm-debug.log 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Michael Hurley 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ramda-fantasy 2 | ============= 3 | 4 | [Fantasy Land][1] compatible types for easy integration with [Ramda][2]. 5 | 6 | ## Project status 7 | Ramda-Fantasy is no longer developed. There are a number of excellent libraries providing algebraic datatypes in JavaScript. The existing npm releases of Ramda-Fantasy will remain available indefinitely. 8 | 9 | We recommend a number of alternative libraries such as [Sanctuary](https://github.com/sanctuary-js), [Folktale](http://folktale.origamitower.com/), [Fluture](https://github.com/fluture-js), and [Fantasy-Land](https://github.com/fantasyland). 10 | 11 | Specifically, we suggest these replacements: 12 | 13 | * Maybe: [sanctuary-js/sanctuary-maybe](https://github.com/sanctuary-js/sanctuary-maybe) 14 | * Either: [sanctuary-js/sanctuary-either](https://github.com/sanctuary-js/sanctuary-either) 15 | * Future: [fluture-js/Fluture](https://github.com/fluture-js/Fluture) 16 | * State: [fantasyland/fantasy-states](https://github.com/fantasyland/fantasy-states) 17 | * Tuple: [fantasyland/fantasy-tuples](https://github.com/fantasyland/fantasy-tuples) 18 | * Reader: [fantasyland/fantasy-readers](https://github.com/fantasyland/fantasy-readers) 19 | * IO: [fantasyland/fantasy-io](https://github.com/fantasyland/fantasy-io) 20 | * Identity: [sanctuary-js/sanctuary-identity](https://github.com/sanctuary-js/sanctuary-identity) 21 | 22 | ## Available types 23 | 24 | | Name | [Setoid][3] | [Semigroup][4] | [Functor][5] | [Applicative][6] | [Monad][7] | [Foldable][8] | [ChainRec][16] | 25 | | --------------- | :----------: | :------------: | :----------: | :--------------: | :--------: | :-----------: | :------------: | 26 | | [Either][9] | **✔︎** | | **✔︎** | **✔︎** | **✔︎** | | **✔︎** | 27 | | [Future][10] | | | **✔︎** | **✔︎** | **✔︎** | | **✔︎** | 28 | | [Identity][11] | **✔︎** | | **✔︎** | **✔︎** | **✔︎** | | **✔︎** | 29 | | [IO][12] | | | **✔︎** | **✔︎** | **✔︎** | | **✔︎** | 30 | | [Maybe][13] | **✔︎** | **✔︎** | **✔︎** | **✔︎** | **✔︎** | **✔︎** | **✔︎** | 31 | | [Reader][14] | | | **✔︎** | **✔︎** | **✔︎** | | | 32 | | [Tuple][15] | **✔︎** | **✔︎** | **✔︎** | | | | | 33 | | [State][17] | | | **✔︎** | **✔︎** | **✔︎** | | **✔︎** | 34 | 35 | 36 | Access like so: 37 | ``` 38 | var Either = require('ramda-fantasy').Either; 39 | ``` 40 | ## available translations 41 | [Spanish](https://github.com/idcmardelplata/ramda-fantasy) 42 | 43 | [1]: https://github.com/fantasyland/fantasy-land 44 | [2]: https://github.com/ramda/ramda 45 | [3]: https://github.com/fantasyland/fantasy-land#setoid 46 | [4]: https://github.com/fantasyland/fantasy-land#semigroup 47 | [5]: https://github.com/fantasyland/fantasy-land#functor 48 | [6]: https://github.com/fantasyland/fantasy-land#applicative 49 | [7]: https://github.com/fantasyland/fantasy-land#monad 50 | [8]: https://github.com/fantasyland/fantasy-land#foldable 51 | [9]: docs/Either.md 52 | [10]: docs/Future.md 53 | [11]: docs/Identity.md 54 | [12]: docs/IO.md 55 | [13]: docs/Maybe.md 56 | [14]: docs/Reader.md 57 | [15]: docs/Tuple.md 58 | [16]: https://github.com/fantasyland/fantasy-land#chainrec 59 | [17]: docs/State.md 60 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ramda-fantasy", 3 | "description": "Fantasy Land compatible types for easy integration with Ramda", 4 | "version": "0.8.1", 5 | "authors": [ 6 | { 7 | "name": "Michael Hurley", 8 | "email": "mh@buzzdecafe.com", 9 | "homepage": "buzzdecafe.com" 10 | }, 11 | { 12 | "name": "Ludwig Magnusson" 13 | } 14 | ], 15 | "homepage": "https://www.github.com/ramda/ramda-fantasy", 16 | "license": "MIT", 17 | "moduleType": [ 18 | "node" 19 | ], 20 | "repository": { 21 | "type": "git", 22 | "url": "git://github.com/ramda/ramda-fantasy.git" 23 | }, 24 | "keywords": [ 25 | "abstract", 26 | "adt", 27 | "datatype", 28 | "functional", 29 | "type" 30 | ], 31 | "dependencies": { 32 | "ramda": ">=0.15.0 <=0.22.x" 33 | }, 34 | "devDependencies": { 35 | "jshint": "~2.7.0", 36 | "jsverify": "^0.5.1", 37 | "mocha": "^2.1.0", 38 | "uglify-js": "2.4.x", 39 | "xyz": "0.5.x" 40 | }, 41 | "ignore": [ 42 | "node_modules" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /dist/ramda-fantasy.min.js: -------------------------------------------------------------------------------- 1 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n;n="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,n.RF=t()}}(function(){var t,n,r;return function e(t,n,r){function u(i,a){if(!n[i]){if(!t[i]){var c="function"==typeof require&&require;if(!a&&c)return c(i,!0);if(o)return o(i,!0);var f=new Error("Cannot find module '"+i+"'");throw f.code="MODULE_NOT_FOUND",f}var s=n[i]={exports:{}};t[i][0].call(s.exports,function(n){var r=t[i][1][n];return u(r?r:n)},s,s.exports,e,t,n,r)}return n[i].exports}for(var o="function"==typeof require&&require,i=0;ie;)t(n[e]),e+=1;return n}))},{"./internal/_checkForMethod":13,"./internal/_curry2":17}],9:[function(t,n,r){var e=t("./internal/_curry2");n.exports=e(function u(t,n){return t===n?0!==t||1/t===1/n:t!==t&&n!==n})},{"./internal/_curry2":17}],10:[function(t,n,r){var e=t("./internal/_curry1"),u=t("./internal/_identity");n.exports=e(u)},{"./internal/_curry1":16,"./internal/_identity":24}],11:[function(t,n,r){n.exports=function e(t,n){switch(t){case 0:return function(){return n.apply(this,arguments)};case 1:return function(t){return n.apply(this,arguments)};case 2:return function(t,r){return n.apply(this,arguments)};case 3:return function(t,r,e){return n.apply(this,arguments)};case 4:return function(t,r,e,u){return n.apply(this,arguments)};case 5:return function(t,r,e,u,o){return n.apply(this,arguments)};case 6:return function(t,r,e,u,o,i){return n.apply(this,arguments)};case 7:return function(t,r,e,u,o,i,a){return n.apply(this,arguments)};case 8:return function(t,r,e,u,o,i,a,c){return n.apply(this,arguments)};case 9:return function(t,r,e,u,o,i,a,c,f){return n.apply(this,arguments)};case 10:return function(t,r,e,u,o,i,a,c,f,s){return n.apply(this,arguments)};default:throw new Error("First argument to _arity must be a non-negative integer no greater than ten")}}},{}],12:[function(t,n,r){n.exports=function e(t){for(var n=[],r;!(r=t.next()).done;)n.push(r.value);return n}},{}],13:[function(t,n,r){var e=t("./_isArray"),u=t("./_slice");n.exports=function o(t,n){return function(){var r=arguments.length;if(0===r)return n();var o=arguments[r-1];return e(o)||"function"!=typeof o[t]?n.apply(this,arguments):o[t].apply(o,u(arguments,0,r-1))}}},{"./_isArray":26,"./_slice":33}],14:[function(t,n,r){n.exports=function e(t){return function(){return!t.apply(this,arguments)}}},{}],15:[function(t,n,r){var e=t("./_indexOf");n.exports=function u(t,n){return e(n,t,0)>=0}},{"./_indexOf":25}],16:[function(t,n,r){n.exports=function e(t){return function n(r){return 0===arguments.length?n:null!=r&&r["@@functional/placeholder"]===!0?n:t.apply(this,arguments)}}},{}],17:[function(t,n,r){var e=t("./_curry1");n.exports=function u(t){return function n(r,u){var o=arguments.length;return 0===o?n:1===o&&null!=r&&r["@@functional/placeholder"]===!0?n:1===o?e(function(n){return t(r,n)}):2===o&&null!=r&&r["@@functional/placeholder"]===!0&&null!=u&&u["@@functional/placeholder"]===!0?n:2===o&&null!=r&&r["@@functional/placeholder"]===!0?e(function(n){return t(n,u)}):2===o&&null!=u&&u["@@functional/placeholder"]===!0?e(function(n){return t(r,n)}):t(r,u)}}},{"./_curry1":16}],18:[function(t,n,r){var e=t("./_curry1"),u=t("./_curry2");n.exports=function o(t){return function n(r,o,i){var a=arguments.length;return 0===a?n:1===a&&null!=r&&r["@@functional/placeholder"]===!0?n:1===a?u(function(n,e){return t(r,n,e)}):2===a&&null!=r&&r["@@functional/placeholder"]===!0&&null!=o&&o["@@functional/placeholder"]===!0?n:2===a&&null!=r&&r["@@functional/placeholder"]===!0?u(function(n,r){return t(n,o,r)}):2===a&&null!=o&&o["@@functional/placeholder"]===!0?u(function(n,e){return t(r,n,e)}):2===a?e(function(n){return t(r,o,n)}):3===a&&null!=r&&r["@@functional/placeholder"]===!0&&null!=o&&o["@@functional/placeholder"]===!0&&null!=i&&i["@@functional/placeholder"]===!0?n:3===a&&null!=r&&r["@@functional/placeholder"]===!0&&null!=o&&o["@@functional/placeholder"]===!0?u(function(n,r){return t(n,r,i)}):3===a&&null!=r&&r["@@functional/placeholder"]===!0&&null!=i&&i["@@functional/placeholder"]===!0?u(function(n,r){return t(n,o,r)}):3===a&&null!=o&&o["@@functional/placeholder"]===!0&&null!=i&&i["@@functional/placeholder"]===!0?u(function(n,e){return t(r,n,e)}):3===a&&null!=r&&r["@@functional/placeholder"]===!0?e(function(n){return t(n,o,i)}):3===a&&null!=o&&o["@@functional/placeholder"]===!0?e(function(n){return t(r,n,i)}):3===a&&null!=i&&i["@@functional/placeholder"]===!0?e(function(n){return t(r,o,n)}):t(r,o,i)}}},{"./_curry1":16,"./_curry2":17}],19:[function(t,n,r){var e=t("./_arity");n.exports=function u(t,n,r){return function(){for(var o=[],i=0,a=t,c=0;c=arguments.length)?f=n[c]:(f=arguments[i],i+=1),o[c]=f,(null==f||f["@@functional/placeholder"]!==!0)&&(a-=1),c+=1}return 0>=a?r.apply(this,o):e(a,u(t,o,r))}}},{"./_arity":11}],20:[function(t,n,r){var e=t("./_isArray"),u=t("./_isTransformer"),o=t("./_slice");n.exports=function i(t,n,r){return function(){var i=arguments.length;if(0===i)return r();var a=arguments[i-1];if(!e(a)){var c=o(arguments,0,i-1);if("function"==typeof a[t])return a[t].apply(a,c);if(u(a)){var f=n.apply(null,c);return f(a)}}return r.apply(this,arguments)}}},{"./_isArray":26,"./_isTransformer":28,"./_slice":33}],21:[function(t,n,r){var e=t("./_arrayFromIterator"),u=t("./_has"),o=t("../identical"),i=t("../keys"),a=t("../type");n.exports=function c(t,n,r,f){if(o(t,n))return!0;if(a(t)!==a(n))return!1;if(null==t||null==n)return!1;if("function"==typeof t.equals||"function"==typeof n.equals)return"function"==typeof t.equals&&t.equals(n)&&"function"==typeof n.equals&&n.equals(t);switch(a(t)){case"Arguments":case"Array":case"Object":break;case"Boolean":case"Number":case"String":if(typeof t!=typeof n||!o(t.valueOf(),n.valueOf()))return!1;break;case"Date":if(!o(t.valueOf(),n.valueOf()))return!1;break;case"RegExp":if(t.source!==n.source||t.global!==n.global||t.ignoreCase!==n.ignoreCase||t.multiline!==n.multiline||t.sticky!==n.sticky||t.unicode!==n.unicode)return!1;break;case"Map":case"Set":if(!c(e(t.entries()),e(n.entries()),r,f))return!1;break;case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":break;case"ArrayBuffer":break;default:return!1}var s=i(t);if(s.length!==i(n).length)return!1;for(var l=r.length-1;l>=0;){if(r[l]===t)return f[l]===n;l-=1}for(r.push(t),f.push(n),l=s.length-1;l>=0;){var p=s[l];if(!u(p,n)||!c(n[p],t[p],r,f))return!1;l-=1}return r.pop(),f.pop(),!0}},{"../identical":9,"../keys":40,"../type":49,"./_arrayFromIterator":12,"./_has":23}],22:[function(t,n,r){n.exports=function e(t,n){for(var r=0,e=n.length,u=[];e>r;)t(n[r])&&(u[u.length]=n[r]),r+=1;return u}},{}],23:[function(t,n,r){n.exports=function e(t,n){return Object.prototype.hasOwnProperty.call(n,t)}},{}],24:[function(t,n,r){n.exports=function e(t){return t}},{}],25:[function(t,n,r){var e=t("../equals");n.exports=function u(t,n,r){for(var u=r;u=0&&"[object Array]"===Object.prototype.toString.call(t)}},{}],27:[function(t,n,r){n.exports=function e(t){return"[object String]"===Object.prototype.toString.call(t)}},{}],28:[function(t,n,r){n.exports=function e(t){return"function"==typeof t["@@transducer/step"]}},{}],29:[function(t,n,r){n.exports=function e(t,n){for(var r=0,e=n.length,u=Array(e);e>r;)u[r]=t(n[r]),r+=1;return u}},{}],30:[function(t,n,r){n.exports=function e(t,n){return function(){return n.call(this,t.apply(this,arguments))}}},{}],31:[function(t,n,r){n.exports=function e(t){var n=t.replace(/\\/g,"\\\\").replace(/[\b]/g,"\\b").replace(/\f/g,"\\f").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t").replace(/\v/g,"\\v").replace(/\0/g,"\\0");return'"'+n.replace(/"/g,'\\"')+'"'}},{}],32:[function(t,n,r){var e=t("./_xwrap"),u=t("../bind"),o=t("../isArrayLike");n.exports=function(){function t(t,n,r){for(var e=0,u=r.length;u>e;){if(n=t["@@transducer/step"](n,r[e]),n&&n["@@transducer/reduced"]){n=n["@@transducer/value"];break}e+=1}return t["@@transducer/result"](n)}function n(t,n,r){for(var e=r.next();!e.done;){if(n=t["@@transducer/step"](n,e.value),n&&n["@@transducer/reduced"]){n=n["@@transducer/value"];break}e=r.next()}return t["@@transducer/result"](n)}function r(t,n,r){return t["@@transducer/result"](r.reduce(u(t["@@transducer/step"],t),n))}var i="undefined"!=typeof Symbol?Symbol.iterator:"@@iterator";return function a(u,c,f){if("function"==typeof u&&(u=e(u)),o(f))return t(u,c,f);if("function"==typeof f.reduce)return r(u,c,f);if(null!=f[i])return n(u,c,f[i]());if("function"==typeof f.next)return n(u,c,f);throw new TypeError("reduce: list must be array or iterable")}}()},{"../bind":2,"../isArrayLike":39,"./_xwrap":38}],33:[function(t,n,r){n.exports=function e(t,n,r){switch(arguments.length){case 1:return e(t,0,t.length);case 2:return e(t,n,t.length);default:for(var u=[],o=0,i=Math.max(0,Math.min(t.length,r)-n);i>o;)u[o]=t[n+o],o+=1;return u}}},{}],34:[function(t,n,r){n.exports=function(){var t=function n(t){return(10>t?"0":"")+t};return"function"==typeof Date.prototype.toISOString?function r(t){return t.toISOString()}:function e(n){return n.getUTCFullYear()+"-"+t(n.getUTCMonth()+1)+"-"+t(n.getUTCDate())+"T"+t(n.getUTCHours())+":"+t(n.getUTCMinutes())+":"+t(n.getUTCSeconds())+"."+(n.getUTCMilliseconds()/1e3).toFixed(3).slice(2,5)+"Z"}}()},{}],35:[function(t,n,r){var e=t("./_contains"),u=t("./_map"),o=t("./_quote"),i=t("./_toISOString"),a=t("../keys"),c=t("../reject");n.exports=function f(t,n){var r=function l(r){var u=n.concat([t]);return e(r,u)?"":f(r,u)},s=function(t,n){return u(function(n){return o(n)+": "+r(t[n])},n.slice().sort())};switch(Object.prototype.toString.call(t)){case"[object Arguments]":return"(function() { return arguments; }("+u(r,t).join(", ")+"))";case"[object Array]":return"["+u(r,t).concat(s(t,c(function(t){return/^\d+$/.test(t)},a(t)))).join(", ")+"]";case"[object Boolean]":return"object"==typeof t?"new Boolean("+r(t.valueOf())+")":t.toString();case"[object Date]":return"new Date("+o(i(t))+")";case"[object Null]":return"null";case"[object Number]":return"object"==typeof t?"new Number("+r(t.valueOf())+")":1/t===-(1/0)?"-0":t.toString(10);case"[object String]":return"object"==typeof t?"new String("+r(t.valueOf())+")":o(t);case"[object Undefined]":return"undefined";default:return"function"==typeof t.constructor&&"Object"!==t.constructor.name&&"function"==typeof t.toString&&"[object Object]"!==t.toString()?t.toString():"{"+s(t,a(t)).join(", ")+"}"}}},{"../keys":40,"../reject":44,"./_contains":15,"./_map":29,"./_quote":31,"./_toISOString":34}],36:[function(t,n,r){n.exports={init:function(){return this.xf["@@transducer/init"]()},result:function(t){return this.xf["@@transducer/result"](t)}}},{}],37:[function(t,n,r){var e=t("./_curry2"),u=t("./_xfBase");n.exports=function(){function t(t,n){this.xf=n,this.f=t}return t.prototype["@@transducer/init"]=u.init,t.prototype["@@transducer/result"]=u.result,t.prototype["@@transducer/step"]=function(t,n){return this.f(n)?this.xf["@@transducer/step"](t,n):t},e(function n(r,e){return new t(r,e)})}()},{"./_curry2":17,"./_xfBase":36}],38:[function(t,n,r){n.exports=function(){function t(t){this.f=t}return t.prototype["@@transducer/init"]=function(){throw new Error("init not implemented on XWrap")},t.prototype["@@transducer/result"]=function(t){return t},t.prototype["@@transducer/step"]=function(t,n){return this.f(t,n)},function n(r){return new t(r)}}()},{}],39:[function(t,n,r){var e=t("./internal/_curry1"),u=t("./internal/_isArray");n.exports=e(function o(t){return u(t)?!0:t?"object"!=typeof t?!1:t instanceof String?!1:1===t.nodeType?!!t.length:0===t.length?!0:t.length>0?t.hasOwnProperty(0)&&t.hasOwnProperty(t.length-1):!1:!1})},{"./internal/_curry1":16,"./internal/_isArray":26}],40:[function(t,n,r){var e=t("./internal/_curry1"),u=t("./internal/_has");n.exports=function(){var t=!{toString:null}.propertyIsEnumerable("toString"),n=["constructor","valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"],r=function o(t,n){for(var r=0;r=0;)o=n[a],u(o,e)&&!r(i,o)&&(i[i.length]=o),a-=1;return i})}()},{"./internal/_curry1":16,"./internal/_has":23}],41:[function(t,n,r){var e=t("./internal/_curry1");n.exports=e(function u(t){var n=!1,r;return function(){return n?r:(n=!0,r=t.apply(this,arguments))}})},{"./internal/_curry1":16}],42:[function(t,n,r){var e=t("./internal/_arity"),u=t("./internal/_pipe"),o=t("./reduce"),i=t("./tail");n.exports=function a(){if(0===arguments.length)throw new Error("pipe requires at least one argument");return e(arguments[0].length,o(u,arguments[0],i(arguments)))}},{"./internal/_arity":11,"./internal/_pipe":30,"./reduce":43,"./tail":47}],43:[function(t,n,r){var e=t("./internal/_curry3"),u=t("./internal/_reduce");n.exports=e(u)},{"./internal/_curry3":18,"./internal/_reduce":32}],44:[function(t,n,r){var e=t("./internal/_complement"),u=t("./internal/_curry2"),o=t("./filter");n.exports=u(function i(t,n){return o(e(t),n)})},{"./filter":7,"./internal/_complement":14,"./internal/_curry2":17}],45:[function(t,n,r){var e=t("./internal/_curry1"),u=t("./internal/_isString"),o=t("./internal/_slice");n.exports=e(function i(t){return u(t)?t.split("").reverse().join(""):o(t).reverse()})},{"./internal/_curry1":16,"./internal/_isString":27,"./internal/_slice":33}],46:[function(t,n,r){var e=t("./internal/_checkForMethod"),u=t("./internal/_curry3");n.exports=u(e("slice",function o(t,n,r){return Array.prototype.slice.call(r,t,n)}))},{"./internal/_checkForMethod":13,"./internal/_curry3":18}],47:[function(t,n,r){var e=t("./internal/_checkForMethod"),u=t("./slice");n.exports=e("tail",u(1,1/0))},{"./internal/_checkForMethod":13,"./slice":46}],48:[function(t,n,r){var e=t("./internal/_curry1"),u=t("./internal/_toString");n.exports=e(function o(t){return u(t,[])})},{"./internal/_curry1":16,"./internal/_toString":35}],49:[function(t,n,r){var e=t("./internal/_curry1");n.exports=e(function u(t){return null===t?"Null":void 0===t?"Undefined":Object.prototype.toString.call(t).slice(8,-1)})},{"./internal/_curry1":16}],50:[function(t,n,r){function e(t,n){switch(arguments.length){case 0:throw new TypeError("no arguments to Either");case 1:return function(n){return null==n?e.Left(t):e.Right(n)};default:return null==n?e.Left(t):e.Right(n)}}function u(t){this.value=t}function o(t){this.value=t}var i=t("ramda/src/curry"),a=t("ramda/src/toString"),c=t("./internal/util");e.prototype["@@type"]="ramda-fantasy/Either",e.prototype.map=c.returnThis,e.of=e.prototype.of=function(t){return e.Right(t)},e.prototype.chain=c.returnThis,e.either=i(function f(t,n,r){if(r instanceof o)return t(r.value);if(r instanceof u)return n(r.value);throw new TypeError("invalid type given to Either.either")}),e.isLeft=function(t){return t.isLeft},e.isRight=function(t){return t.isRight},c.extend(u,e),u.prototype.isRight=!0,u.prototype.isLeft=!1,u.prototype.map=function(t){return new u(t(this.value))},u.prototype.ap=function(t){return t.map(this.value)},u.prototype.chain=function(t){return t(this.value)},e.chainRec=e.prototype.chainRec=function(t,n){for(var r,u=c.chainRecNext(n);u.isNext;){if(r=t(c.chainRecNext,c.chainRecDone,u.value),e.isLeft(r))return r;u=r.value}return e.Right(u.value)},u.prototype.bimap=function(t,n){return new u(n(this.value))},u.prototype.extend=function(t){return new u(t(this))},u.prototype.toString=function(){return"Either.Right("+a(this.value)+")"},u.prototype.equals=c.getEquals(u),e.Right=function(t){return new u(t)},c.extend(o,e),o.prototype.isLeft=!0,o.prototype.isRight=!1,o.prototype.ap=c.returnThis,o.prototype.bimap=function(t){return new o(t(this.value))},o.prototype.extend=c.returnThis,o.prototype.toString=function(){return"Either.Left("+a(this.value)+")"},o.prototype.equals=c.getEquals(o),e.Left=function(t){return new o(t)},e.prototype.either=function s(t,n){return this.isLeft?t(this.value):n(this.value)},n.exports=e},{"./internal/util":58,"ramda/src/curry":4,"ramda/src/toString":48}],51:[function(t,n,r){function e(t,n){return function(r){try{return n(r)}catch(e){t(e)}}}function u(t){return this instanceof u?void(this._fork=t):new u(t)}var o=t("ramda/src/once"),i=t("ramda/src/forEach"),a=t("ramda/src/toString"),c=t("ramda/src/curry"),f=t("./internal/util");u.prototype["@@type"]="ramda-fantasy/Future",u.prototype.fork=function(t,n){this._fork(t,e(t,n))},u.prototype.map=function(t){return this.chain(function(n){return u.of(t(n))})},u.prototype.ap=function(t){var n=this;return new u(function(r,u){var i,a,c=o(r),f=e(c,function(){return null!=i&&null!=a?u(i(a)):void 0});n._fork(c,function(t){i=t,f()}),t._fork(c,function(t){a=t,f()})})},u.of=function(t){return new u(function(n,r){return r(t)})},u.prototype.of=u.of,u.prototype.chain=function(t){return new u(function(n,r){return this._fork(function(t){return n(t)},e(n,function(e){return t(e)._fork(n,r)}))}.bind(this))},u.chainRec=u.prototype.chainRec=function(t,n){return u(function(r,e){return function u(n){for(var o=null,i=f.chainRecNext(n),a=function(t){null===o?(o=!0,i=t):(t.isNext?u:e)(t.value)};i.isNext;)if(o=null,t(f.chainRecNext,f.chainRecDone,i.value).fork(r,a),o!==!0)return void(o=!1);e(i.value)}(n)})},u.prototype.chainReject=function(t){return new u(function(n,r){return this._fork(e(n,function(e){return t(e)._fork(n,r)}),function(t){return r(t)})}.bind(this))},u.prototype.bimap=function(t,n){var r=this;return new u(function(u,o){r._fork(e(u,function(n){u(t(n))}),e(u,function(t){o(n(t))}))})},u.reject=function(t){return new u(function(n){n(t)})},u.prototype.toString=function(){return"Future("+a(this._fork)+")"},u.cache=function(t){function n(t,n){o.push({REJECTED:t,RESOLVED:n})}function r(n,r){return e="PENDING",t._fork(f("REJECTED",n),f("RESOLVED",r))}var e="IDLE",o=[],a,f=c(function(t,n,r){e=t,a=r,n(r),i(function(t){t[e](a)},o)});return new u(function(t,u){switch(e){case"IDLE":r(t,u);break;case"PENDING":n(t,u);break;case"REJECTED":t(a);break;case"RESOLVED":u(a)}})},n.exports=u},{"./internal/util":58,"ramda/src/curry":4,"ramda/src/forEach":8,"ramda/src/once":41,"ramda/src/toString":48}],52:[function(t,n,r){function e(t){return this instanceof e?void(this.fn=t):new e(t)}var u=t("ramda/src/compose"),o=t("ramda/src/toString"),i=t("./internal/util");n.exports=e,e.prototype["@@type"]="ramda-fantasy/IO",e.prototype.chain=function(t){var n=this;return new e(function(){var r=t(n.fn.apply(n,arguments));return r.fn.apply(r,arguments)})},e.chainRec=e.prototype.chainRec=function(t,n){return new e(function(){for(var r=i.chainRecNext(n);r.isNext;)r=t(i.chainRecNext,i.chainRecDone,r.value).fn();return r.value})},e.prototype.map=function(t){var n=this;return new e(u(t,n.fn))},e.prototype.ap=function(t){return this.chain(function(n){return t.map(n)})},e.runIO=function(t){return t.runIO.apply(t,[].slice.call(arguments,1))},e.prototype.runIO=function(){return this.fn.apply(this,arguments)},e.prototype.of=function(t){return new e(function(){return t})},e.of=e.prototype.of,e.prototype.toString=function(){return"IO("+o(this.fn)+")"}},{"./internal/util":58,"ramda/src/compose":3,"ramda/src/toString":48}],53:[function(t,n,r){function e(t){return this instanceof e?void(this.value=t):new e(t)}var u=t("ramda/src/toString"),o=t("./internal/util");e.prototype["@@type"]="ramda-fantasy/Identity",e.of=function(t){return new e(t)},e.prototype.of=e.of,e.prototype.map=function(t){return new e(t(this.value))},e.prototype.ap=function(t){return t.map(this.value)},e.prototype.chain=function(t){return t(this.value)},e.chainRec=e.prototype.chainRec=function(t,n){for(var r=o.chainRecNext(n);r.isNext;)r=t(o.chainRecNext,o.chainRecDone,r.value).get();return e(r.value)},e.prototype.get=function(){return this.value},e.prototype.equals=o.getEquals(e),e.prototype.toString=function(){return"Identity("+u(this.value)+")"},n.exports=e},{"./internal/util":58,"ramda/src/toString":48}],54:[function(t,n,r){function e(t){return null==t?f:e.Just(t)}function u(t){this.value=t}function o(){}var i=t("ramda/src/toString"),a=t("ramda/src/curry"),c=t("./internal/util.js");e.prototype["@@type"]="ramda-fantasy/Maybe",c.extend(u,e),u.prototype.isJust=!0,u.prototype.isNothing=!1,c.extend(o,e),o.prototype.isNothing=!0,o.prototype.isJust=!1;var f=new o;e.Nothing=function(){return f},e.Just=function(t){return new u(t)},e.of=e.Just,e.prototype.of=e.Just,e.isJust=function(t){return t.isJust},e.isNothing=function(t){return t.isNothing},e.maybe=a(function(t,n,r){return r.reduce(function(t,r){return n(r)},t)}),e.toMaybe=e,u.prototype.concat=function(t){return t.isNothing?this:this.of(this.value.concat(t.value))},o.prototype.concat=c.identity,u.prototype.map=function(t){return this.of(t(this.value))},o.prototype.map=c.returnThis,u.prototype.ap=function(t){return t.map(this.value)},o.prototype.ap=c.returnThis,u.prototype.chain=c.baseMap,o.prototype.chain=c.returnThis,e.chainRec=e.prototype.chainRec=function(t,n){for(var r,u=c.chainRecNext(n);u.isNext;){if(r=t(c.chainRecNext,c.chainRecDone,u.value),e.isNothing(r))return r;u=r.value}return e.Just(u.value)},u.prototype.datatype=u,o.prototype.datatype=o,u.prototype.equals=c.getEquals(u),o.prototype.equals=function(t){return t===f},e.prototype.isNothing=function(){return this===f},e.prototype.isJust=function(){return this instanceof u},u.prototype.getOrElse=function(){return this.value},o.prototype.getOrElse=function(t){return t},u.prototype.reduce=function(t,n){return t(n,this.value)},o.prototype.reduce=function(t,n){return n},u.prototype.toString=function(){return"Maybe.Just("+i(this.value)+")"},o.prototype.toString=function(){return"Maybe.Nothing()"},n.exports=e},{"./internal/util.js":58,"ramda/src/curry":4,"ramda/src/toString":48}],55:[function(t,n,r){function e(t){return this instanceof e?void(this.run=t):new e(t)}var u=t("ramda/src/compose"),o=t("ramda/src/identity"),i=t("ramda/src/toString"),a=t("ramda/src/always");e.run=function(t){return t.run.apply(t,[].slice.call(arguments,1))},e.prototype["@@type"]="ramda-fantasy/Reader",e.prototype.chain=function(t){var n=this;return new e(function(r){return t(n.run(r)).run(r)})},e.prototype.ap=function(t){return this.chain(function(n){return t.map(n)})},e.prototype.map=function(t){return this.chain(function(n){return e.of(t(n))})},e.prototype.of=function(t){return new e(function(){return t})},e.of=e.prototype.of,e.ask=e(o),e.prototype.toString=function(){return"Reader("+i(this.run)+")"},e.T=function(t){var n=function r(t){return this instanceof r?void(this.run=t):new r(t)};return n.lift=u(n,a),n.ask=n(t.of),n.prototype.of=n.of=function(r){return n(function(){return t.of(r)})},n.prototype.chain=function(t){var r=this;return n(function(n){var e=r.run(n);return e.chain(function(r){return t(r).run(n)})})},n.prototype.map=function(t){return this.chain(function(r){return n.of(t(r))})},n.prototype.ap=function(t){var r=this;return n(function(n){return r.run(n).ap(t.run(n))})},n.prototype.toString=function(){return"ReaderT["+t.name+"]("+i(this.run)+")"},n},n.exports=e},{"ramda/src/always":1,"ramda/src/compose":3,"ramda/src/identity":10,"ramda/src/toString":48}],56:[function(t,n,r){function e(t){function n(t){return this instanceof n?void(this._run=t):new n(t)}return n.prototype.run=function(t){return this._run(t)},n.prototype.eval=function(t){return i.fst(this.run(t))},n.prototype.exec=function(t){return i.snd(this.run(t))},n.prototype.chain=function(t){var r=this;return n(function(n){return r._run(n).chain(function(n){return t(i.fst(n))._run(i.snd(n))})})},n.of=n.prototype.of=function(r){return n(function(n){return t.of(i(r,n))})},n.prototype.ap=a.deriveAp(n),n.prototype.map=a.deriveMap(n),n.tailRec=u(function(r,e){return n(function(n){return t.tailRec(function(n){return r(i.fst(n))._run(i.snd(n)).chain(function(n){return t.of(i.fst(n).bimap(function(t){return i(t,i.snd(n))},function(t){return i(t,i.snd(n))}))})},i(e,n))})}),n.lift=function(r){return n(function(n){return r.chain(function(r){return t.of(i(r,n))})})},n.get=n(function(n){return t.of(i(n,n))}),n.gets=function(r){return n(function(n){return t.of(i(r(n),n))})},n.put=function(r){return n(function(n){return t.of(i(void n,r))})},n.modify=function(r){return n(function(n){return t.of(i(void 0,r(n)))})},n}var u=t("ramda/src/curry"),o=t("./Identity"),i=t("./Tuple"),a=t("./internal/util"),c=e(o);c.T=e,c.prototype.run=function(t){return this._run(t).value},n.exports=c},{"./Identity":53,"./Tuple":57,"./internal/util":58,"ramda/src/curry":4}],57:[function(t,n,r){function e(t,n){switch(arguments.length){case 0:throw new TypeError("no arguments to Tuple");case 1:return function(n){return new u(t,n)};default:return new u(t,n)}}function u(t,n){this[0]=t,this[1]=n,this.length=2}function o(t){t.forEach(function(t){if("function"!=typeof t.concat)throw new TypeError(i(t)+" must be a semigroup to perform this operation")})}var i=t("ramda/src/toString"),a=t("ramda/src/equals");e.fst=function(t){return t[0]},e.snd=function(t){return t[1]},u.prototype["@@type"]="ramda-fantasy/Tuple",u.prototype.concat=function(t){return o([this[0],this[1]]),e(this[0].concat(t[0]),this[1].concat(t[1]))},u.prototype.map=function(t){return e(this[0],t(this[1]))},u.prototype.ap=function(t){return o([this[0]]),e(this[0].concat(t[0]),this[1](t[1]))},u.prototype.equals=function(t){return t instanceof u&&a(this[0],t[0])&&a(this[1],t[1])},u.prototype.toString=function(){return"Tuple("+i(this[0])+", "+i(this[1])+")"},n.exports=e},{"ramda/src/equals":6,"ramda/src/toString":48}],58:[function(t,n,r){var e=t("ramda/src/equals");n.exports={baseMap:function(t){return t(this.value)},getEquals:function(t){return function n(r){return r instanceof t&&e(this.value,r.value)}},extend:function(t,n){function r(){this.constructor=t}r.prototype=n.prototype,t.prototype=new r,t.super_=n.prototype},identity:function(t){return t},notImplemented:function(t){return function(){throw new Error(t+" is not implemented")}},notCallable:function(t){return function(){throw new Error(t+" cannot be called directly")}},returnThis:function(){return this},chainRecNext:function(t){return{isNext:!0,value:t}},chainRecDone:function(t){return{isNext:!1,value:t}},deriveAp:function(t){return function(n){return this.chain(function(r){return n.chain(function(n){return t.of(r(n))})})}},deriveMap:function(t){return function(n){return this.chain(function(r){return t.of(n(r))})}}}},{"ramda/src/equals":6}],59:[function(t,n,r){var e=t("ramda/src/curryN");n.exports=e(3,function u(t,n,r){return n.map(t).ap(r)})},{"ramda/src/curryN":5}],60:[function(t,n,r){var e=t("ramda/src/curryN");n.exports=e(4,function u(t,n,r,e){return n.map(t).ap(r).ap(e)})},{"ramda/src/curryN":5}],"/index.js":[function(t,n,r){n.exports={Either:t("./src/Either"),Future:t("./src/Future"),Identity:t("./src/Identity"),IO:t("./src/IO"),lift2:t("./src/lift2"),lift3:t("./src/lift3"),Maybe:t("./src/Maybe"),Reader:t("./src/Reader"),State:t("./src/State"),Tuple:t("./src/Tuple")}},{"./src/Either":50,"./src/Future":51,"./src/IO":52,"./src/Identity":53,"./src/Maybe":54,"./src/Reader":55,"./src/State":56,"./src/Tuple":57,"./src/lift2":59,"./src/lift3":60}]},{},[])("/index.js")}); -------------------------------------------------------------------------------- /docs/Either.md: -------------------------------------------------------------------------------- 1 | # Either 2 | 3 | The `Either` type is very similar to the `Maybe` type, in that it is often used 4 | to represent the notion of failure in some way. The difference between the two 5 | is that an error represented with an `Either` can hold some value (perhaps an 6 | exception or error message), while `Maybe` can only indicate that absence of 7 | some value. 8 | 9 | While the `Either` type is often used to represent potential errors, there is 10 | nothing restricting it to this purpose. It is therefore perhaps more appropriate 11 | to simply think of `Either` as a representation of two possible types of values, 12 | sometimes referred to as the _disjoint union_, or _coproduct_ of two types. 13 | 14 | ## Construction 15 | 16 | The `Either` type consists of two constructors, `Left :: a -> Either a b` and 17 | `Right :: b -> Either a b`. When an `Either` type is used to represent the 18 | possibility of an error, `Left` is typically used to hold the error value and 19 | `Right` holds the "successful" value (as a mnemonic, you can think of `Right` as 20 | being _the right_ value). 21 | 22 | It is worth highlighting that the types of the value stored in an `Left` does 23 | not have to be the same type as that in the `Right` of the same `Either`. This 24 | is the reason why it is documented as having two type parameters `Either a b`, 25 | where `a` represents the type contained within the `Left` and `b` for the value 26 | contained in the `Right`. 27 | 28 | ## Interaction 29 | 30 | In order to satisfy a number of laws relating to the implemented interfaces, 31 | only a single type parameter can be used as the argument of the various 32 | functions. For this reason, `Either` is _right-biased_, meaning methods such as 33 | `map`, `ap` and `chain` will only call the given function for a `Right` instance 34 | while a `Left` will simply pass the instance straight through, ignoring the 35 | provided function. 36 | 37 | Aside from the transformation methods mentioned above, `Either.either` can also 38 | be used to extract the value from an `Either` type, which takes a function to 39 | handle the type of value within the `Left` and another function to handle the 40 | type contained in the `Right`. Only one of these functions will be called 41 | depending on the constructor containing the value. 42 | 43 | ## Reference 44 | 45 | ### Constructors 46 | 47 | #### `Either.Left` 48 | ```hs 49 | :: a -> Either a b 50 | ``` 51 | Constructs a `Left` instance of `Either`. This is often used to represent a 52 | failure. 53 | 54 | #### `Either.Right` 55 | ```hs 56 | :: b -> Either a b 57 | ``` 58 | Constructs a `Right` instance of `Either`. This is often used to represent a 59 | success where there is a possibility of failure. 60 | 61 | ### Static functions 62 | 63 | #### `Either.either` 64 | ```hs 65 | :: (a -> c) -> (b -> c) -> Either a b -> c 66 | ``` 67 | Used to extract the value out of an `Either` by providing a function to handle 68 | the types of values contained in both `Left` and `Right`. 69 | 70 | #### `Either.of` 71 | ```hs 72 | :: b -> Either a b 73 | ``` 74 | Creates a pure instance of an `Either`. This is effectively a `Right` 75 | constructor. 76 | 77 | #### `Either.isLeft` 78 | ```hs 79 | :: Either a b -> Boolean 80 | ``` 81 | Returns `true` if the given `Either` instance is a `Left`, otherwise `false`. 82 | 83 | #### `Either.isRight` 84 | ```hs 85 | :: Either a b -> Boolean 86 | ``` 87 | Returns `true` if the given `Either` instance is a `Right`, otherwise `false`. 88 | 89 | ### Instance methods 90 | 91 | #### `either.map` 92 | ```hs 93 | :: Either a b ~> (b -> c) -> Either a c 94 | ``` 95 | Modifies the value contained in a `Right` with the provided function. If the 96 | instance is a `Left`, the value is left unmodified. 97 | 98 | #### `either.ap` 99 | ```hs 100 | :: Either a (b -> c) ~> Either a b -> Either a c 101 | ``` 102 | Applies the function contained in the instance of a `Right` to the value 103 | contained in the provided `Right`, producing a `Right` containing the result. If 104 | the instance is `Left`, the result will be the `Left` instance. If the 105 | instance is `Right` and the provided either is `Left`, the result will be the 106 | provided `Left`. 107 | 108 | #### `either.chain` 109 | ```hs 110 | :: Either a b ~> (b -> Either a c) -> Either a c 111 | ``` 112 | Applies the provided function to the value contained in a `Right` instance, 113 | resulting in the return value of that function. If the instance is a `Left`, the 114 | function is ignored and the `Left` instance is returned unmodified. 115 | 116 | #### `either.extend` 117 | ```hs 118 | :: Either a b ~> (Either a b -> c) -> Either a c 119 | ``` 120 | Applies the provided function to the `Right` instance, returning a `Right` 121 | containing the result of the function application. If the instance is a `Left`, 122 | the function is ignored and the `Left` instance is returned unmodified. 123 | 124 | #### `either.bimap` 125 | ```hs 126 | :: Either a b ~> (a -> c) -> (b -> d) -> Either c d 127 | ``` 128 | Transforms an `Either` by applying the first function to the contained value if 129 | the instance is a `Left`, or the second function if the instance is a `Right`. 130 | 131 | #### `either.equals` 132 | ```hs 133 | :: Either a b ~> * -> Boolean 134 | ``` 135 | Determines whether the provided value is equal to this instance, ensuring both 136 | are of the same constructor and both contain equal values. 137 | 138 | #### `either.toString` 139 | ```hs 140 | :: Either a b ~> () -> String 141 | ``` 142 | Returns a string representation of the `Either` instance. 143 | 144 | #### `either.either` 145 | ```hs 146 | :: Either a b ~> (a -> c) -> (b -> c) -> c 147 | ``` 148 | Used to extract the value out of the `Either` by providing a function to handle 149 | the types of values contained in both `Left` and `Right`. 150 | -------------------------------------------------------------------------------- /docs/Future.md: -------------------------------------------------------------------------------- 1 | # Future 2 | 3 | The `Future` type is used to represent some future, often asynchronous, 4 | action that may potentially fail. It is similar to the native JS `Promise` type, 5 | however the computation of a `Promise` is executed immediately, while the 6 | execution of a `Future` instance is delayed until explicitly requested. 7 | 8 | ## Construction 9 | 10 | The `Future` type consists of a single constructor that accepts a function which 11 | must accept the two continuation functions used to resolve or reject the 12 | `Future` instance: `Future :: ((e -> (), a -> ()) -> ()) -> Future e a`. 13 | 14 | ```js 15 | delayed = (ms, val) => Future((reject, resolve) => 16 | ms > 1000 ? reject(Error('Delay too long')) 17 | : setTimeout(() => resolve(val), ms)); 18 | ``` 19 | 20 | ## Interaction 21 | 22 | Once a `Future` instance has been created, the various methods attached to the 23 | instance can be used to instruct further transformations to take place after 24 | the `Future` has been resolved or rejected. It is important to note that nothing 25 | is actually executed until the `fork` method is eventually called. 26 | 27 | The `map`, `ap` and `chain` functions can be used to transform resolved values 28 | of a `Future` instance, while `chainReject` can be used to transform or recover 29 | from a rejected value. 30 | 31 | ```js 32 | //:: String -> Future Error [String] 33 | ls = path => Future((reject, resolve) => 34 | fs.readdir(path, (err, files) => err ? reject(err) : resolve(files))); 35 | 36 | //:: String -> Future Error String 37 | cat = file => Future((reject, resolve) => 38 | fs.readFile(file, 'utf8', (err, data) => err ? reject(err) : resolve(data))); 39 | 40 | //:: String -> Future Error String 41 | catDir = dir => ls(dir).chain(R.traverse(Future.of, cat)).map(R.join('\n')); 42 | ``` 43 | 44 | To execute a `Future` instance, the `fork` method must be called with an 45 | `onRejected` and `onResolved` handler functions. If `fork` is called multiple 46 | times, the action described by the `Future` instance will be invoked multiple 47 | times. If desired, a `Future` instance can be given to `Future.cache` which 48 | returns a new instance that ensures the action will only ever be invoked once, 49 | with the cached resolved or rejected value being used for subsequent calls to 50 | `fork`. 51 | 52 | ```js 53 | catDir('./src').fork(err => console.error(err), data => console.log(data)); 54 | ``` 55 | 56 | ## Reference 57 | 58 | ### Constructors 59 | 60 | #### `Future` 61 | ```hs 62 | :: ((e -> (), a -> ()) -> ()) -> Future e a 63 | ``` 64 | Constructs a `Future` instance that represents some action that may possibly 65 | fail. 66 | 67 | ### Static methods 68 | 69 | #### `Future.of` 70 | ```hs 71 | :: a -> Future e a 72 | ``` 73 | Creates a `Future` instance that resolves to the provided value. 74 | 75 | #### `Future.reject` 76 | ```hs 77 | :: e -> Future e a 78 | ``` 79 | Creates a `Future` instance that rejects with the provided value. 80 | 81 | #### `Future.cache` 82 | ```hs 83 | :: Future e a -> Future e a 84 | ``` 85 | Creates a new `Future` instance from the provided `Future` instance, where the 86 | resolved or rejected value is cached for subsequent calls to `fork`. 87 | 88 | ### Instance methods 89 | 90 | #### `future.fork` 91 | ```hs 92 | :: Future e a ~> (e -> ()) -> (a -> ()) -> () 93 | ``` 94 | Executes the actions described by the `Future` instance, calling the first 95 | argument with the rejected value if the instance is rejected or the second 96 | argument with the resolved value if the instance is resolved. 97 | 98 | #### `future.map` 99 | ```hs 100 | :: Future e a ~> (a -> b) -> Future e b 101 | ``` 102 | Transforms the resolved value of this `Future` instance with the given function. 103 | If the instance is rejected, the provided function is not called. 104 | 105 | #### `future.ap` 106 | ```hs 107 | :: Future e (a -> b) ~> Future e a -> Future e b 108 | ``` 109 | Applies the resolved function of this `Future` instance to the resolved value of 110 | the provided `Future` instance, producing a resolved `Future` instance of the 111 | result. If either `Future` is rejected, then the returned `Future` instance will 112 | be rejected with that value. When `fork` is called on the returned `Future` 113 | instance, the actions of this `Future` instance and the provided `Future` 114 | instance will be executed in parallel. 115 | 116 | #### `future.chain` 117 | ```hs 118 | :: Future e a ~> (a -> Future e b) -> Future e b 119 | ``` 120 | Calls the provided function with the resolved value of this `Future` instance, 121 | returning the new `Future` instance. If either `Future` instance is rejected, 122 | the returned `Future` instance will be rejected with that value. 123 | 124 | #### `future.chainReject` 125 | ```hs 126 | :: Future e a ~> (e -> Future e b) -> Future e b 127 | ``` 128 | If this `Future` instance is rejected, the provided function will be called with 129 | the rejected value, where a new `Future` instance must be returned. This can 130 | be used to recover from a rejected `Future` instance. If this `Future` instance 131 | is resolved, the provided function will be ignored. 132 | 133 | #### `future.bimap` 134 | ```hs 135 | :: Future e a ~> (e -> f) -> (a -> b) -> Future f b 136 | ``` 137 | Uses the provided functions to transform this `Future` instance when it becomes 138 | rejected or resolved, respectively. 139 | -------------------------------------------------------------------------------- /docs/IO.md: -------------------------------------------------------------------------------- 1 | # IO 2 | 3 | The `IO` type is used to store a function that describes some computation with 4 | side effects, such as reading some data from a file, printing logging 5 | information to the console, or mutating the elements in the DOM. Describing 6 | actions in this way allows for `IO` instances to be composed and passed around 7 | while keeping functions pure and maintaining referential transparency. 8 | 9 | ## Construction 10 | 11 | The `IO` type consists of a single constructor which accepts a nullary function 12 | that describes the action to be performed, resulting in an `IO` of the value 13 | eventually returned from the action `IO :: (() -> a) -> IO a`. 14 | 15 | ```js 16 | //:: IO [String] 17 | argsIO = IO(() => R.tail(process.argv)); 18 | 19 | //:: String -> IO String 20 | readFile = filename => IO(() => fs.readFileSync(filename, 'utf8')); 21 | 22 | //:: String -> IO () 23 | stdoutWrite = data => IO(() => process.stdout.write(data)); 24 | ``` 25 | 26 | It is important to note that upon construction of an `IO` instance, the action 27 | will not be executed until the `runIO` method is called. 28 | 29 | ## Interaction 30 | 31 | An `IO` instance implements the monad specification, allowing the results of the 32 | actions to be transformed or chained together. The `runIO` method can then be 33 | called to finally execute the action. Execution of an `IO` instance is 34 | recommended to be delegated to the outer edges of an application, to the point 35 | where an application will consist of a single `IO` instance at the entry point. 36 | 37 | ```js 38 | // Read each file provided in the arguments of the running application, echoing 39 | // their contents to stdout after transforming the text to uppercase characters 40 | //:: String -> IO () 41 | loudCat = argsIO.chain(R.traverse(IO.of, readFile)) 42 | .map(R.join('\n')) 43 | .map(R.toUpper) 44 | .chain(stdoutWrite); 45 | 46 | loudCat.runIO(); 47 | ``` 48 | 49 | ## Reference 50 | 51 | ### Constructors 52 | 53 | #### `IO` 54 | ```hs 55 | :: (() -> a) -> IO a 56 | ``` 57 | Constructs an `IO` instance that represents some action that may possibly have 58 | side effects. 59 | 60 | ### Static methods 61 | 62 | #### `IO.runIO` 63 | ```hs 64 | :: IO a -> a 65 | ``` 66 | Executes the action described by the given `IO` instance. This is also available 67 | as an instance method. 68 | 69 | #### `IO.of` 70 | ```hs 71 | :: a -> IO a 72 | ``` 73 | Produces an `IO` instance that results in the given value. 74 | 75 | ### Instance methods 76 | 77 | #### `io.runIO` 78 | ```hs 79 | :: IO a ~> () -> a 80 | ``` 81 | Executes the action described by this `IO` instance. This is also available as a 82 | static method. 83 | 84 | #### `io.map` 85 | ```hs 86 | :: IO a ~> (a -> b) -> IO b 87 | ``` 88 | Transforms the result of this `IO` instance with the provided function. 89 | 90 | #### `io.ap` 91 | ```hs 92 | :: IO (a -> b) ~> IO a -> IO b 93 | ``` 94 | Produces a new `IO` instance that when executed, applies the function resulting 95 | from the action in this `IO` instance to the value resulting from the action in 96 | the given `IO` instance. 97 | 98 | #### `io.chain` 99 | ```hs 100 | :: IO a ~> (a -> IO b) -> IO b 101 | ``` 102 | Produces an `IO` instance that when executed, will apply the given function to 103 | the result of the action in this `IO` instance and then execute the resulting 104 | `IO` action. 105 | -------------------------------------------------------------------------------- /docs/Identity.md: -------------------------------------------------------------------------------- 1 | # Identity 2 | 3 | The `Identity` type is a very simple type that has no interesting side effects 4 | and is effectively just a container of some value. So why does it exist? The 5 | `Identity` type is often used as the base monad of a monad transformer when no 6 | other behaviour is required. For example, `Reader.T(Identity)` is effectively 7 | the same as an ordinary `Reader`. 8 | 9 | ## Construction 10 | 11 | The `Identity` type consists of a single constructor. 12 | 13 | ```js 14 | const Identity = require('ramda-fantasy').Identity; 15 | const five = Identity(5); 16 | ``` 17 | 18 | Alternatively, as `Identity` implements the `Applicative` interface, 19 | `Identity.of` could also be used. 20 | 21 | ## Interaction 22 | 23 | An `Identity` instance can be transformed via the methods necessary to implement 24 | `Functor`, `Ap` and `Chain` (see their corresponding reference below). 25 | 26 | The value contained within an `Identity` instance can be accessed by calling the 27 | `get` method of the instance. 28 | 29 | ## Reference 30 | 31 | ### Constructors 32 | 33 | #### `Identity` 34 | ```hs 35 | :: a -> Identity a 36 | ``` 37 | 38 | ### Static functions 39 | 40 | #### `Identity.of` 41 | ```hs 42 | :: a -> Identity a 43 | ``` 44 | 45 | ### Instance methods 46 | 47 | #### `identity.map` 48 | ```hs 49 | :: Identity a ~> (a -> b) -> Identity b 50 | ``` 51 | Transforms the value contained within the `Identity` instance with the provided 52 | function. 53 | 54 | #### `identity.ap` 55 | ```hs 56 | :: Identity (a -> b) ~> Identity a -> Identity b 57 | ``` 58 | Transforms the value within the provided `Identity` instance using the function 59 | contained withing the instance of this `Identity`. 60 | 61 | #### `identity.chain` 62 | ```hs 63 | :: Identity a ~> (a -> Identity b) -> Identity b 64 | ``` 65 | Produces a new `Identity` instance by applying the value of this `Identity` to 66 | the provided function. 67 | -------------------------------------------------------------------------------- /docs/Maybe.md: -------------------------------------------------------------------------------- 1 | # Maybe 2 | 3 | The `Maybe` type represents the possibility of some value or nothing. It is 4 | often used where `null` traditionally would to represent the absence of a value. 5 | The advantage of using a `Maybe` type over `null` is that it is both composable 6 | and requires the developer to explicitly acknowledge the potential absence of a 7 | value, helping to avoid the existence of null pointer exceptions. 8 | 9 | ## Construction 10 | 11 | The `Maybe` type consists of two constructors, `Just :: a -> Maybe a` and 12 | `Nothing :: () -> Maybe a`, representing the existence and absence of some type 13 | `a` respectively. 14 | 15 | ```js 16 | const M = require('ramda-fantasy').Maybe; 17 | const Just = M.Just; 18 | const Nothing = M.Nothing; 19 | 20 | const safeDiv = R.curry((n, d) => d === 0 ? Nothing() : Just(n / d)); 21 | const lookup = R.curry((k, obj) => k in obj ? Just(obj[k]) : Nothing()); 22 | ``` 23 | 24 | ## Interaction 25 | 26 | Once an instance of a `Maybe` type is obtained, there are a number of ways the 27 | contained value can be accessed if it exists, while ensuring the `Nothing` case 28 | is handled. 29 | 30 | ```js 31 | // getOrElse :: Maybe a ~> a -> a -- provide a default value if Nothing 32 | lookup('foo', { foo: 'bar' }).getOrElse('baz'); // 'bar' 33 | lookup('foo', { abc: 'bar' }).getOrElse('baz'); // 'baz' 34 | 35 | // maybe :: b -> (a -> b) -> Maybe a -> b -- transform the value if it exists 36 | // -- with the provided function, 37 | // -- otherwise return the default value 38 | plus1or0 = M.maybe(0, R.inc); 39 | plus1or0(safeDiv(42, 2)); // 22 40 | plus1or0(safeDiv(42, 0)); // 0 41 | ``` 42 | 43 | It is quite often useful to transform the potential value of a `Maybe` while 44 | deferring the extraction until later in the program. The two functions, `map` 45 | and `chain`, exist to support this behaviour. Both of these functions are 46 | somewhat similar in that neither can transform a `Nothing` into a `Just`, though 47 | `chain` is considered more powerful as it allows a function to transform a 48 | `Just` into a `Nothing`, while `map` can only transform the value contained 49 | within a `Just`. 50 | 51 | ```js 52 | // map :: Maybe a ~> (a -> b) -> Maybe b 53 | safeDiv(42, 2).map(R.inc); // Maybe(22) 54 | safeDiv(42, 0).map(R.inc); // Nothing 55 | 56 | // chain :: Maybe a ~> (a -> Maybe b) -> Maybe b 57 | lookup('a', { a: { b: 'foo' }}).chain(lookup('b')); // Just('foo') 58 | lookup('a', { a: {}}).chain(lookup('b')); // Nothing 59 | lookup('a', {}).chain(lookup('b')); // Nothing 60 | ``` 61 | 62 | ## Reference 63 | 64 | ### Constructors 65 | 66 | #### `Maybe.Just` 67 | ```hs 68 | :: a -> Maybe a 69 | ``` 70 | Constructs a `Maybe` instance that represents the existence of some value. 71 | 72 | #### `Maybe.Nothing` 73 | ```hs 74 | :: () -> Maybe a 75 | ``` 76 | Constructs a `Maybe` instance that represents the absence of a value. 77 | 78 | ### Static functions 79 | 80 | #### `Maybe.maybe` 81 | ```hs 82 | :: b -> (a -> b) -> Maybe a -> b 83 | ``` 84 | Transforms the value of a `Just` with the provided function, or returns the 85 | default value if a `Nothing` is received. 86 | 87 | #### `Maybe.of` 88 | ```hs 89 | :: a -> Maybe a 90 | ``` 91 | Produces a pure `Maybe` instance of a given value. Effectively the `Just` 92 | constructor. 93 | 94 | #### `Maybe.isJust` 95 | ```hs 96 | :: Maybe a -> Boolean 97 | ``` 98 | Returns `true` if the given `Maybe` instance is a `Just`, otherwise `false`. 99 | 100 | #### `Maybe.isNothing` 101 | ```hs 102 | :: Maybe a -> Boolean 103 | ``` 104 | Returns `true` if the given `Maybe` instance is a `Nothing`, otherwise `false`. 105 | 106 | #### `Maybe.toMaybe` 107 | ```hs 108 | :: a? -> Maybe a 109 | ``` 110 | Returns `Nothing` for a `null`/`undefined` value, otherwise a `Just` of the 111 | value for any other value. 112 | 113 | ### Instance methods 114 | 115 | #### `maybe.getOrElse` 116 | ```hs 117 | :: Maybe a ~> a -> a 118 | ``` 119 | Returns the value if the instance is a `Just`, otherwise the provided default 120 | value will be returned. 121 | 122 | #### `maybe.map` 123 | ```hs 124 | :: Maybe a ~> (a -> b) -> Maybe b 125 | ``` 126 | Transforms the value of a `Just` with the provided function, returning a new 127 | `Just`. If `Nothing` is received, `Nothing` will be returned. 128 | 129 | #### `maybe.ap` 130 | ```hs 131 | :: Maybe (a -> b) ~> Maybe a -> Maybe b 132 | ``` 133 | Applies the function contained in the first `Just` to the value of the second 134 | `Just`, returning a `Just` of the result. If either of the arguments are 135 | `Nothing`, the result will be `Nothing`. 136 | 137 | #### `maybe.chain` 138 | ```hs 139 | :: Maybe a ~> (a -> Maybe b) -> Maybe b 140 | ``` 141 | Returns the result of applying the provided function to the value contained in 142 | the `Just` instance. If the instance is a `Nothing`, then a `Nothing` is 143 | returned. 144 | 145 | #### `maybe.reduce` 146 | ```hs 147 | :: Maybe a ~> (b -> a -> b) -> b -> b 148 | ``` 149 | Returns the result of applying the provided function to the initial value and 150 | the value of the `Just`. If the instance is a `Nothing`, then the initial value 151 | is returned instead. 152 | 153 | #### `maybe.equals` 154 | ```hs 155 | :: Maybe a ~> * -> Boolean 156 | ``` 157 | Returns true if both the instance and the provided value are `Nothing`, or if 158 | the instance is a `Just` and the provided value is a `Just`, where both 159 | contained values are also considered equal as determined by `R.equals`. 160 | Otherwise `false` is returned. 161 | 162 | #### `maybe.toString` 163 | ```hs 164 | :: Maybe a ~> () -> String 165 | ``` 166 | Returns a string representation of the instance. 167 | -------------------------------------------------------------------------------- /docs/Reader.md: -------------------------------------------------------------------------------- 1 | # Reader 2 | 3 | The `Reader` type is used for providing a shared "environment" to computations. 4 | It is often used as a way of providing configuration or injecting dependencies, 5 | where the responsibility for these concerns is delegated to the outer edges of 6 | an application. You can think of this in much of the same way as how a function 7 | has access to arguments that are provided by the caller of the function. In 8 | fact, the `Reader` type is effectively a wrapper type around a function to 9 | provide the various monad and functor instances. 10 | 11 | ## Construction 12 | 13 | A `Reader` type can be constructed via the single constructor function 14 | `Reader r a :: (r -> a) -> Reader r a`. The function given to the constructor is 15 | responsible for computing some value based on the environment it receives. 16 | 17 | ```js 18 | const dbReader = Reader(env => env.dbConn); 19 | ``` 20 | 21 | A static `Reader` instance is also available via `Reader.ask` which passes its 22 | environment straight through, providing access to the environment within 23 | functions given to its `chain` method. 24 | 25 | ```js 26 | const dbReader = Reader.ask.chain(env => Reader.of(configureDb(env.dbConn))); 27 | ``` 28 | 29 | To provided better support for nested monad types such as `Reader r (m a)` where 30 | `m` is some monad type, a monad transformer can be constructed by passing the 31 | nested monad type to the transformer constructor. 32 | 33 | ```js 34 | // Reader.T :: Monad m => { of: a -> m a } -> ReaderT r m a 35 | const ReaderTMaybe = Reader.T(Maybe); 36 | ``` 37 | 38 | This wires up the various methods available on a monad (`of`, `map`, `ap` and 39 | `chain`) from the outer `ReaderT` instance to the inner monad type, removing the 40 | need to "unpack" each layer of monad instances where the transformer instance is 41 | interacted with. 42 | 43 | The static `lift` method on a `ReaderT` type allows for an instance of the inner 44 | monad type to be "lifted" into a `ReaderT` instance, similar to how `of` will 45 | lift an ordinary value into an applicative instance. 46 | 47 | ```js 48 | const maybe42R = ReaderTMaybe.lift(Just(42)); 49 | ``` 50 | 51 | ## Interaction 52 | 53 | Code that requires access to the environment needs to exist within a `Reader` 54 | type. To ensure that code is still composable, `Reader` implements the monad 55 | specification and by extension, applicative and functor too. This allows for 56 | various `Reader` instances to be composed with each other, while also being 57 | able to lift plain functions to operate within the context of the `Reader` type. 58 | 59 | ```js 60 | /** 61 | * Env :: { dbConn: DbConnection, httpClientPool: HttpClientPool } 62 | * selectOne :: String -> Table -> Object -> DbConnection -> Future Error DbRow 63 | * fetchImage :: URL -> HttpClientPool -> Future Error Image 64 | */ 65 | 66 | //:: String -> Reader Env (Future Error DbRow) 67 | fetchUser = email => Reader(env => 68 | selectOne('email = :email', Users, { email: email }, env.dbConn)); 69 | 70 | //:: String -> Reader Env (Future Error ImageData) 71 | fetchUserImage = email => Reader.ask.chain(env => 72 | fetchUser(email).map(futureUserRow => futureUserRow.chain(userRow => 73 | fetchImage(userRow.imageURL, env.httpClientPool)))); 74 | 75 | //:: Future Error ImageData 76 | fooImageFuture = fetchUserImage('foo@example.com').run({ 77 | dbConn: initDb(), 78 | httpClientPool: initHttpClientPool 79 | }); 80 | ``` 81 | 82 | Alternatively, a `ReaderT` could be used to help declutter some of the nested 83 | `chain` and `map` calls in the `fetchUserImage` function in the example above. 84 | 85 | ```js 86 | //:: (Env -> Future e a) -> ReaderT Env (Future e) a 87 | App = Reader.T(Future); 88 | 89 | //:: String -> ReaderT Env (Future Error) DbRow 90 | fetchUser = email => App(env => 91 | selectOne('email = :email', Users, { email: email }, env.dbConn)); 92 | 93 | //:: String -> ReaderT Env (Future Error) ImageData 94 | fetchUserImage = email => App.ask.chain(env => 95 | fetchUser(email).chain(userRow => 96 | App.lift(fetchImage(userRow.imageURL, env.httpClientPool)))); 97 | ``` 98 | 99 | ## Reference 100 | 101 | ### Constructors 102 | 103 | #### `Reader` 104 | ```hs 105 | :: (r -> a) -> Reader r a 106 | ``` 107 | Constructs a `Reader` instance that computes some value `a` using some 108 | environment `r`. 109 | 110 | #### `Reader.T` 111 | ```hs 112 | :: Monad m => { of: a -> m a } -> (r -> m a) -> ReaderT r m a 113 | ``` 114 | Constructs a `ReaderT` instance that computes some value `a` within a monad `m` 115 | using some environment `r`. 116 | 117 | ### Static properties 118 | 119 | #### `Reader.ask` 120 | ```hs 121 | :: Reader r r 122 | ``` 123 | A static `Reader` instance that just returns its environment. 124 | 125 | #### `ReaderT.ask` 126 | ```hs 127 | :: Monad m => ReaderT r m a 128 | ``` 129 | A static `ReaderT` instance that just returns its environment. 130 | 131 | ### Static functions 132 | 133 | #### `Reader.of` 134 | ```hs 135 | :: a -> Reader r a 136 | ``` 137 | Produces a pure `Reader` instance for the given value. 138 | 139 | #### `ReaderT.of` 140 | ```hs 141 | :: Monad m => a -> ReaderT r m a 142 | ``` 143 | Produces a pure `ReaderT` instance for the given value. 144 | 145 | #### `ReaderT.lift` 146 | ```hs 147 | :: Monad m => m a -> ReaderT r m a 148 | ``` 149 | Lifts a monad value into a `ReaderT` instance. 150 | 151 | ### Instance methods 152 | 153 | #### `reader.run` 154 | ```hs 155 | :: Reader r a ~> r -> a 156 | ``` 157 | Executes the `Reader` instance with the given environment `r`. 158 | 159 | #### `readerT.run` 160 | ```hs 161 | :: Monad m => ReaderT r m a ~> r -> m a 162 | ``` 163 | Executes the `ReaderT` instance with the given environment `r`. 164 | 165 | #### `reader.map` 166 | ```hs 167 | :: Reader r a ~> (a -> b) -> Reader r b 168 | ``` 169 | Transforms the result of the computation of the `Reader` instance with the given 170 | function. 171 | 172 | #### `readerT.map` 173 | ```hs 174 | :: Monad m => ReaderT r m a ~> (a -> b) -> ReaderT r m b 175 | ``` 176 | Transforms the result of the computation of the `ReaderT` instance with the 177 | given function. 178 | 179 | #### `reader.ap` 180 | ```hs 181 | :: Reader r (a -> b) ~> Reader r a -> Reader r b 182 | ``` 183 | Applies the `a` in the given `Reader` instance to the function in this `Reader` 184 | instance, producing a new `Reader` instance containing the result. 185 | 186 | #### `readerT.ap` 187 | ```hs 188 | :: Monad m => ReaderT r m (a -> b) ~> ReaderT r m a -> ReaderT r m b 189 | ``` 190 | Applies the `a` in the given `ReaderT` instance to the function in this 191 | `ReaderT` instance, producing a new `ReaderT` instance containing the result. 192 | 193 | #### `reader.chain` 194 | ```hs 195 | :: Reader r a ~> (a -> Reader r b) -> Reader r b 196 | ``` 197 | Produces a new `Reader` instance by applying the provided function with the 198 | value of this `Reader` instance. Both this instance and the instance returned by 199 | the provided function will receive the same environment when run. 200 | 201 | #### `readerT.chain` 202 | ```hs 203 | :: Monad m => ReaderT r m a ~> (a -> ReaderT r m b) -> ReaderT r m b 204 | ``` 205 | Produces a new `ReaderT` instance by applying the provided function with the 206 | value of this `ReaderT` instance. Both this instance and the instance returned 207 | by the provided function will receive the same environment when run. 208 | -------------------------------------------------------------------------------- /docs/State.md: -------------------------------------------------------------------------------- 1 | # State 2 | 3 | The `State` type can be used to store some state along with a computation. 4 | 5 | ## Construction 6 | 7 | `State` instances should be obtained via the `get`, `gets`, `put`, and `modify` 8 | static properties on the `State` object. These instances describe the different 9 | ways to access and modify the stateful computation that will eventually be 10 | evaluated and are described in further detail below. 11 | 12 | A `State` transformer is also available via `State.T` which can be used to 13 | extend some monad with stateful behaviour. 14 | 15 | ## Interaction 16 | 17 | `State` instances are primarily interacted with and composed via the `chain` 18 | method of the various static instances available on the `State` type object. To 19 | access the current state, `State.get` is a static instance that can be used to 20 | provide the state to the `chain`, `ap` and `map`. Similarly, `State.gets(fn)` 21 | can provide the state transformed by the provided `fn` function. To change the 22 | state of the computation, `State.put(newValue)` can be used to replace the 23 | existing state with `newValue`. The current state can also be transformed by 24 | providing a transformation function to `State.modify(transformFn)`. 25 | 26 | Once a `State` instance is defined, an initial seed state can be provided to 27 | either the `eval` or `exec` methods to evaluate the computation and return the 28 | result of the computation or the final state, respectively. Alternatively, the 29 | `run` method can be called to return both the result and the final state within 30 | a `Tuple` instance. 31 | 32 | ```js 33 | // An example deterministic pseudorandom number generator 34 | // see: https://en.wikipedia.org/wiki/Linear_congruential_generator 35 | 36 | // type RNG a = State Integer a 37 | 38 | // rng :: RNG Float 39 | const rng = State.get.chain(seed => { 40 | const newSeed = (seed * 1103515245 + 12345) & 0x7FFFFFFF; 41 | const randVal = (newSeed >>> 16) / 0x7FFF; 42 | return State.put(newSeed).map(_ => randVal); 43 | }); 44 | 45 | rng.eval(42); // From initial seed of 42: 0.5823236793115024 46 | rng.eval(42); // Repeating produces the same value: 0.5823236793115024 47 | rng.exec(42); // `exec` returns the next seed: 1250496027 48 | rng.eval(1250496027); // A different seed: 0.5198217719046602 49 | 50 | // Chain together to pass the new seed to the next RNG 51 | // pair :: RNG a -> RNG (Tuple a a) 52 | const pair = rng => rng.chain(a => rng.chain(b => State.of(Tuple(a, b)))); 53 | 54 | pair(rng).eval(42); // Tuple(0.5823236793115024, 0.5198217719046602) 55 | 56 | // Map to produce transformed random values from 1 to 6 57 | // rollDie :: RNG Integer 58 | const rollDie = rng.map(n => Math.ceil(n * 6)); 59 | 60 | // rollDice :: RNG (Tuple Integer Integer) 61 | const rollDice = pair(rollDie); 62 | rollDice.eval(123); // Tuple(2, 5) 63 | ``` 64 | 65 | ## Reference 66 | 67 | ### Constructors 68 | 69 | #### `State` 70 | ```hs 71 | :: (s -> Identity (Tuple a s)) -> State s a 72 | ``` 73 | Constructs a `State` instance that represent a pure computation from some state 74 | to a new state and a result. Note this constructor requires the given function 75 | to return an `Identity` instance. It is generally recommended to use the static 76 | properties and methods provided on the `State` object rather than using this 77 | constructor. 78 | 79 | #### `State.T` 80 | ```hs 81 | :: Monad m => { of :: a -> m a } -> (s -> m (Tuple a s)) -> StateT s m a 82 | ``` 83 | Constructs a `StateT` instance that represent a computation from some state to a 84 | new state and a result in the context of some other monad. It is generally 85 | recommended to use the static properties and methods provided on the `State` 86 | object rather than using this constructor. 87 | 88 | ### Static properties 89 | 90 | #### `State.get` 91 | ```hs 92 | :: State s s 93 | ``` 94 | A static `State` instance that retrieves the current state. 95 | 96 | #### `StateT.get` 97 | ```hs 98 | :: Monad m => StateT s m s 99 | ``` 100 | A static `StateT` instance that retrieves the current state. 101 | 102 | ### Static methods 103 | 104 | #### `State.gets` 105 | ```hs 106 | :: (s -> a) -> State s a 107 | ``` 108 | Returns a `State` instance the retrieves the current state transformed by the 109 | given function. 110 | 111 | #### `StateT.gets` 112 | ```hs 113 | :: Monad m => (s -> a) -> StateT s m a 114 | ``` 115 | Returns a `State` instance the retrieves the current state transformed by the 116 | given function. 117 | 118 | #### `State.put` 119 | ```hs 120 | :: s -> State s a 121 | ``` 122 | Returns a `State` instance the stores the provided state. 123 | 124 | #### `StateT.put` 125 | ```hs 126 | :: Monad m => s -> StateT s m a 127 | ``` 128 | Returns a `StateT` instance the stores the provided state 129 | 130 | #### `State.modify` 131 | ```hs 132 | :: (s -> s) -> State s a 133 | ``` 134 | Returns a `State` instance the modifies the stored state with the provided 135 | function. 136 | 137 | #### `StateT.modify` 138 | ```hs 139 | :: Monad m => (s -> s) -> StateT s m a 140 | ``` 141 | Returns a `StateT` instance the modifies the stored state with the provided 142 | function. 143 | 144 | #### `State.of` 145 | ```hs 146 | :: a -> State s a 147 | ``` 148 | Returns a `State` instance that will evaluate to the provided value. 149 | 150 | #### `StateT.of` 151 | ```hs 152 | :: Monad m => a -> StateT s m a 153 | ``` 154 | Returns a `StateT` instance that will evaluate to the provided value. 155 | 156 | #### `StateT.lift` 157 | ```hs 158 | :: Monad m => m a -> StateT s m a 159 | ``` 160 | Lifts the given monad into a `StateT` instance. 161 | 162 | ### Instance methods 163 | 164 | #### `state.run` 165 | ```hs 166 | :: State s a ~> s -> Tuple a s 167 | ``` 168 | Runs the `State` instance, seeded by the provided value and returns the final 169 | state along with the result in a `Tuple`. 170 | 171 | #### `stateT.run` 172 | ```hs 173 | :: Monad m => StateT s m a ~> s -> m Tuple(a, s) 174 | ``` 175 | Runs the `StateT` instance, seeded by the provided value and returns the final 176 | state along with the result in a `Tuple` within the underlying monad type of the 177 | transformer. 178 | 179 | #### `state.eval` 180 | ```hs 181 | :: State s a ~> s -> a 182 | ``` 183 | Runs the `State` instance, seeded by the provided value and returns the result. 184 | 185 | #### `stateT.eval` 186 | ```hs 187 | :: Monad m => StateT s m a ~> s -> m a 188 | ``` 189 | Runs the `StateT` instance, seeded by the provided value and returns the result 190 | in the context of the underlying monad type of the transformer. 191 | 192 | #### `state.exec` 193 | ```hs 194 | :: State s a ~> s -> s 195 | ``` 196 | Runs the `State` instance, seeded by the provided value and returns the final 197 | state. 198 | 199 | #### `stateT.exec` 200 | ```hs 201 | :: Monad m => StateT s m a ~> s -> m s 202 | ``` 203 | Runs the `StateT` instance, seeded by the provided value and returns the final 204 | state in the context of the underlying monad type of the transformer. 205 | 206 | #### `state.map` 207 | ```hs 208 | :: State s a ~> (a -> b) -> State s b 209 | ``` 210 | Transforms the eventual result of the `State` instance with the provided 211 | function. 212 | 213 | #### `stateT.map` 214 | ```hs 215 | :: Monad m => StateT s m a ~> (a -> b) -> StateT s m b 216 | ``` 217 | Transforms the eventual result of the `StateT` instance with the provided 218 | function. 219 | 220 | #### `state.ap` 221 | ```hs 222 | :: State s (a -> b) ~> State s a -> State s b 223 | ``` 224 | Applies the resulting function of this `State` instance to the result of the 225 | provided `State` instance to produce a new `State` instance. 226 | 227 | #### `stateT.ap` 228 | ```hs 229 | :: Monad m => StateT s m (a -> b) ~> StateT s m a -> StateT s m b 230 | ``` 231 | Applies the resulting function of this `StateT` instance to the result of the 232 | provided `StateT` instance to produce a new `StateT` instance. 233 | 234 | #### `state.chain` 235 | ```hs 236 | :: State s a ~> (a -> State s b) -> State s b 237 | ``` 238 | Creates a new `State` instance by applying the given function to the result of 239 | this `State` instance. 240 | 241 | #### `stateT.chain` 242 | ```hs 243 | :: StateT s m a ~> (a -> StateT s m b) -> StateT s m b 244 | ``` 245 | Creates a new `StateT` instance by applying the given function to the result of 246 | this `StateT` instance. 247 | -------------------------------------------------------------------------------- /docs/Tuple.md: -------------------------------------------------------------------------------- 1 | # Tuple 2 | 3 | A `Tuple` is a simple data type that represents two types of values at the same 4 | time. It is typically used to pass two values around without needing to 5 | introduce some adhoc data type, such as the accumulating state of an operation 6 | along with the previous result, or coupling an ID along with a set of values. 7 | 8 | Note that while other libraries and languages may offer tuples of varying sizes, 9 | the `Tuple` currently offered here is restricted to holding two values. 10 | 11 | ## Construction 12 | 13 | `Tuple` consists of a single constructor that accepts the two values to store in 14 | the tuple `Tuple :: a -> b -> Tuple a b`. It is worth highlighting that the two 15 | values associated with the `Tuple` may be completely different types. 16 | 17 | ## Interaction 18 | 19 | The values of a `Tuple` instance can be accessed by passing the `Tuple` to 20 | `Tuple.fst` or `Tuple.snd` for the first and second values respectively. 21 | 22 | ## Reference 23 | 24 | ### Constructors 25 | 26 | #### `Tuple` 27 | ```hs 28 | :: (a, b) -> Tuple a b 29 | ``` 30 | 31 | ### Static functions 32 | 33 | #### `Tuple.fst` 34 | ```hs 35 | :: Tuple a b -> a 36 | ``` 37 | Returns the first element of a given `Tuple`. 38 | 39 | #### `Tuple.snd` 40 | ```hs 41 | :: Tuple a b -> b 42 | ``` 43 | Returns the second element of a given `Tuple`. 44 | 45 | ### Instance methods 46 | 47 | #### `tuple.concat` 48 | ```hs 49 | :: (Semigroup a, Semigroup b) => Tuple a b ~> Tuple a b -> Tuple a b 50 | ``` 51 | Joins the `Tuple` instance with the given `Tuple` by concatenating the first 52 | elements of each `Tuple` to produce the first element of the returned `Tuple` 53 | and the second elements concatenated together to produce the returned second 54 | element. To ensure the elements can be concatenated together, both types of the 55 | `Tuple` elements _must_ satisfy the `Semigroup` spec. 56 | 57 | #### `tuple.map` 58 | ```hs 59 | :: Tuple a b ~> (b -> c) -> Tuple a c 60 | ``` 61 | Transforms the second element of the `Tuple` instance by applying the given 62 | function over the value. 63 | 64 | #### `tuple.ap` 65 | ```hs 66 | :: Semigroup a => Tuple a (b -> c) ~> Tuple a b -> Tuple a c 67 | ``` 68 | Transforms the second element of the given `Tuple` by applying the function held 69 | in the second element of the `Tuple` instance. As this leaves both of the first 70 | elements unmodified, the two must be combined in some way to produce a single 71 | value to be stored in the first element of the returned `Tuple`. This is done by 72 | requiring that the type of value in the first element satsifies the `Semigroup` 73 | spec, with the resulting value determined by concatenating the two values 74 | together. 75 | 76 | #### `tuple.equals` 77 | ```hs 78 | :: Tuple a b ~> * -> Boolean 79 | ``` 80 | Determines whether the given `Tuple` is equal to the `Tuple` instance. This is 81 | determined by comparing the equality of values of the first element of each 82 | `Tuple`, then comparing the values of the second element of each. 83 | 84 | #### `tuple.toString` 85 | ```hs 86 | :: Tuple a b ~> () -> String 87 | ``` 88 | Returns a string representation of the `Tuple` instance. 89 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Either: require('./src/Either'), 3 | Future: require('./src/Future'), 4 | Identity: require('./src/Identity'), 5 | IO: require('./src/IO'), 6 | lift2: require('./src/lift2'), 7 | lift3: require('./src/lift3'), 8 | Maybe: require('./src/Maybe'), 9 | Reader: require('./src/Reader'), 10 | State: require('./src/State'), 11 | Tuple: require('./src/Tuple') 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Michael Hurley (buzzdecafe.com)", 3 | "contributors": [ 4 | { 5 | "name": "Michael Hurley", 6 | "email": "mh@buzzdecafe.com", 7 | "web": "http://buzzdecafe.com" 8 | }, 9 | { 10 | "name": "Ludwig Magnusson", 11 | "email": "ludwig@mediatool.com" 12 | } 13 | ], 14 | "name": "ramda-fantasy", 15 | "description": "Fantasy Land compatible types for easy integration with Ramda", 16 | "version": "0.8.1", 17 | "homepage": "https://www.github.com/ramda/ramda-fantasy", 18 | "license": "MIT", 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/ramda/ramda-fantasy.git" 22 | }, 23 | "scripts": { 24 | "build": "browserify -r ./index.js -s RF | derequire > dist/ramda-fantasy.js ", 25 | "postbuild": "uglifyjs dist/ramda-fantasy.js -m -c unused=false -o dist/ramda-fantasy.min.js", 26 | "jscs": "node_modules/.bin/jscs src/* test/*", 27 | "jshint": "node_modules/.bin/jshint src/* test/*", 28 | "pretest": "npm run jshint", 29 | "release-major": "xyz --repo git@github.com:ramda/ramda-fantasy.git --increment major", 30 | "release-minor": "xyz --repo git@github.com:ramda/ramda-fantasy.git --increment minor", 31 | "release-patch": "xyz --repo git@github.com:ramda/ramda-fantasy.git --increment patch", 32 | "test": "mocha" 33 | }, 34 | "dependencies": { 35 | "ramda": ">=0.15.0" 36 | }, 37 | "devDependencies": { 38 | "browserify": "13.1.x", 39 | "derequire": "^2.0.3", 40 | "jscs": "1.13.x", 41 | "jshint": "~2.7.0", 42 | "jsverify": "^0.7.1", 43 | "mocha": "^2.1.0", 44 | "promise": "7.1.1", 45 | "uglify-js": "2.6.x", 46 | "xyz": "0.5.x" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Either.js: -------------------------------------------------------------------------------- 1 | var curry = require('ramda/src/curry'); 2 | var toString = require('ramda/src/toString'); 3 | 4 | var util = require('./internal/util'); 5 | 6 | 7 | function Either(left, right) { 8 | switch (arguments.length) { 9 | case 0: 10 | throw new TypeError('no arguments to Either'); 11 | case 1: 12 | return function(right) { 13 | return right == null ? Either.Left(left) : Either.Right(right); 14 | }; 15 | default: 16 | return right == null ? Either.Left(left) : Either.Right(right); 17 | } 18 | } 19 | 20 | Either.prototype['@@type'] = 'ramda-fantasy/Either'; 21 | 22 | Either.prototype.map = util.returnThis; 23 | 24 | Either.of = Either.prototype.of = function(value) { 25 | return Either.Right(value); 26 | }; 27 | 28 | Either.prototype.chain = util.returnThis; // throw? 29 | 30 | Either.either = curry(function either(leftFn, rightFn, e) { 31 | if (e instanceof _Left) { 32 | return leftFn(e.value); 33 | } else if (e instanceof _Right) { 34 | return rightFn(e.value); 35 | } else { 36 | throw new TypeError('invalid type given to Either.either'); 37 | } 38 | }); 39 | 40 | Either.isLeft = function(x) { 41 | return x.isLeft; 42 | }; 43 | 44 | Either.isRight = function(x) { 45 | return x.isRight; 46 | }; 47 | 48 | 49 | // Right 50 | function _Right(x) { 51 | this.value = x; 52 | } 53 | util.extend(_Right, Either); 54 | 55 | _Right.prototype.isRight = true; 56 | _Right.prototype.isLeft = false; 57 | 58 | _Right.prototype.map = function(fn) { 59 | return new _Right(fn(this.value)); 60 | }; 61 | 62 | _Right.prototype.ap = function(that) { 63 | return that.map(this.value); 64 | }; 65 | 66 | _Right.prototype.chain = function(f) { 67 | return f(this.value); 68 | }; 69 | 70 | //chainRec 71 | Either.chainRec = Either.prototype.chainRec = function(f, i) { 72 | var res, state = util.chainRecNext(i); 73 | while (state.isNext) { 74 | res = f(util.chainRecNext, util.chainRecDone, state.value); 75 | if (Either.isLeft(res)) { 76 | return res; 77 | } 78 | state = res.value; 79 | } 80 | return Either.Right(state.value); 81 | }; 82 | 83 | _Right.prototype.bimap = function(_, f) { 84 | return new _Right(f(this.value)); 85 | }; 86 | 87 | _Right.prototype.extend = function(f) { 88 | return new _Right(f(this)); 89 | }; 90 | 91 | _Right.prototype.toString = function() { 92 | return 'Either.Right(' + toString(this.value) + ')'; 93 | }; 94 | 95 | _Right.prototype.equals = util.getEquals(_Right); 96 | 97 | Either.Right = function(value) { 98 | return new _Right(value); 99 | }; 100 | 101 | 102 | // Left 103 | function _Left(x) { 104 | this.value = x; 105 | } 106 | util.extend(_Left, Either); 107 | 108 | _Left.prototype.isLeft = true; 109 | _Left.prototype.isRight = false; 110 | 111 | _Left.prototype.ap = util.returnThis; 112 | 113 | _Left.prototype.bimap = function(f) { 114 | return new _Left(f(this.value)); 115 | }; 116 | 117 | _Left.prototype.extend = util.returnThis; 118 | 119 | _Left.prototype.toString = function() { 120 | return 'Either.Left(' + toString(this.value) + ')'; 121 | }; 122 | 123 | _Left.prototype.equals = util.getEquals(_Left); 124 | 125 | Either.Left = function(value) { 126 | return new _Left(value); 127 | }; 128 | 129 | 130 | // either 131 | Either.prototype.either = function instanceEither(leftFn, rightFn) { 132 | return this.isLeft ? leftFn(this.value) : rightFn(this.value); 133 | }; 134 | 135 | module.exports = Either; 136 | -------------------------------------------------------------------------------- /src/Future.js: -------------------------------------------------------------------------------- 1 | var once = require('ramda/src/once'); 2 | var forEach = require('ramda/src/forEach'); 3 | var toString = require('ramda/src/toString'); 4 | var curry = require('ramda/src/curry'); 5 | 6 | var util = require('./internal/util'); 7 | 8 | function jail(handler, f){ 9 | return function(a){ 10 | try{ 11 | return f(a); 12 | } catch(err) { 13 | handler(err); 14 | } 15 | }; 16 | } 17 | 18 | // `f` is a function that takes two function arguments: `reject` (failure) and `resolve` (success) 19 | function Future(f) { 20 | if (!(this instanceof Future)) { 21 | return new Future(f); 22 | } 23 | this._fork = f; 24 | } 25 | 26 | Future.prototype['@@type'] = 'ramda-fantasy/Future'; 27 | 28 | Future.prototype.fork = function(reject, resolve) { 29 | this._fork(reject, jail(reject, resolve)); 30 | }; 31 | 32 | // functor 33 | Future.prototype.map = function(f) { 34 | return this.chain(function(a) { return Future.of(f(a)); }); 35 | }; 36 | 37 | // apply 38 | Future.prototype.ap = function(m) { 39 | var self = this; 40 | 41 | return new Future(function(rej, res) { 42 | var applyFn, val; 43 | var doReject = once(rej); 44 | 45 | var resolveIfDone = jail(doReject, function() { 46 | if (applyFn != null && val != null) { 47 | return res(applyFn(val)); 48 | } 49 | }); 50 | 51 | self._fork(doReject, function(fn) { 52 | applyFn = fn; 53 | resolveIfDone(); 54 | }); 55 | 56 | m._fork(doReject, function(v) { 57 | val = v; 58 | resolveIfDone(); 59 | }); 60 | 61 | }); 62 | 63 | }; 64 | 65 | // applicative 66 | Future.of = function(x) { 67 | // should include a default rejection? 68 | return new Future(function(_, resolve) { return resolve(x); }); 69 | }; 70 | 71 | Future.prototype.of = Future.of; 72 | 73 | // chain 74 | // f must be a function which returns a value 75 | // f must return a value of the same Chain 76 | // chain must return a value of the same Chain 77 | //:: Future a, b => (b -> Future c) -> Future c 78 | Future.prototype.chain = function(f) { // Sorella's: 79 | return new Future(function(reject, resolve) { 80 | return this._fork( 81 | function(a) { return reject(a); }, 82 | jail(reject, function(b) { return f(b)._fork(reject, resolve); }) 83 | ); 84 | }.bind(this)); 85 | }; 86 | 87 | // chainRec 88 | // 89 | // Heavily influenced by the Aff MonadRec instance 90 | // https://github.com/slamdata/purescript-aff/blob/51106474122d0e5aec8e3d5da5bb66cfe8062f55/src/Control/Monad/Aff.js#L263-L322 91 | Future.chainRec = Future.prototype.chainRec = function(f, a) { 92 | return Future(function(reject, resolve) { 93 | return function go(acc) { 94 | // isSync could be in three possable states 95 | // * null - unresolved status 96 | // * true - synchronous future 97 | // * false - asynchronous future 98 | var isSync = null; 99 | var state = util.chainRecNext(acc); 100 | var onResolve = function(v) { 101 | // If the `isSync` is still unresolved, we have observed a 102 | // synchronous future. Otherwise, `isSync` will be `false`. 103 | if (isSync === null) { 104 | isSync = true; 105 | // Store the result for further synchronous processing. 106 | state = v; 107 | } else { 108 | // When we have observed an asynchronous future, we use normal 109 | // recursion. This is safe because we will be on a new stack. 110 | (v.isNext ? go : resolve)(v.value); 111 | } 112 | }; 113 | while (state.isNext) { 114 | isSync = null; 115 | f(util.chainRecNext, util.chainRecDone, state.value).fork(reject, onResolve); 116 | // If the `isSync` has already resolved to `true` by our `onResolve`, then 117 | // we have observed a synchronous future. Otherwise it will still be `null`. 118 | if (isSync === true) { 119 | continue; 120 | } else { 121 | // If the status has not resolved yet, then we have observed an 122 | // asynchronous or failed future so update status and exit the loop. 123 | isSync = false; 124 | return; 125 | } 126 | } 127 | resolve(state.value); 128 | }(a); 129 | }); 130 | }; 131 | 132 | // chainReject 133 | // Like chain but operates on the reject instead of the resolve case. 134 | //:: Future a, b => (a -> Future c) -> Future c 135 | Future.prototype.chainReject = function(f) { 136 | return new Future(function(reject, resolve) { 137 | return this._fork( 138 | jail(reject, function(a) { return f(a)._fork(reject, resolve); }), 139 | function(b) { return resolve(b); } 140 | ); 141 | }.bind(this)); 142 | }; 143 | 144 | // monad 145 | // A value that implements the Monad specification must also implement the Applicative and Chain specifications. 146 | // see above. 147 | 148 | Future.prototype.bimap = function(errFn, successFn) { 149 | var self = this; 150 | return new Future(function(reject, resolve) { 151 | self._fork( 152 | jail(reject, function(err) { reject(errFn(err)); }), 153 | jail(reject, function(val) { resolve(successFn(val)); }) 154 | ); 155 | }); 156 | }; 157 | 158 | Future.reject = function(val) { 159 | return new Future(function(reject) { 160 | reject(val); 161 | }); 162 | }; 163 | 164 | Future.prototype.toString = function() { 165 | return 'Future(' + toString(this._fork) + ')'; 166 | }; 167 | 168 | Future.cache = function(f) { 169 | var status = 'IDLE'; 170 | var listeners = []; 171 | var cachedValue; 172 | 173 | var handleCompletion = curry(function(newStatus, cb, val) { 174 | status = newStatus; 175 | cachedValue = val; 176 | cb(val); 177 | forEach(function(listener) { 178 | listener[status](cachedValue); 179 | }, listeners); 180 | }); 181 | 182 | function addListeners(reject, resolve) { 183 | listeners.push({ REJECTED: reject, RESOLVED: resolve } ); 184 | } 185 | 186 | function doResolve(reject, resolve) { 187 | status = 'PENDING'; 188 | return f._fork( 189 | handleCompletion('REJECTED', reject), 190 | handleCompletion('RESOLVED', resolve) 191 | ); 192 | } 193 | 194 | return new Future(function(reject, resolve) { 195 | 196 | switch(status) { 197 | case 'IDLE': doResolve(reject, resolve); break; 198 | case 'PENDING': addListeners(reject, resolve); break; 199 | case 'REJECTED': reject(cachedValue); break; 200 | case 'RESOLVED': resolve(cachedValue); break; 201 | } 202 | 203 | }); 204 | }; 205 | 206 | module.exports = Future; 207 | -------------------------------------------------------------------------------- /src/IO.js: -------------------------------------------------------------------------------- 1 | var compose = require('ramda/src/compose'); 2 | var toString = require('ramda/src/toString'); 3 | 4 | var util = require('./internal/util'); 5 | 6 | module.exports = IO; 7 | 8 | function IO(fn) { 9 | if (!(this instanceof IO)) { 10 | return new IO(fn); 11 | } 12 | this.fn = fn; 13 | } 14 | 15 | IO.prototype['@@type'] = 'ramda-fantasy/IO'; 16 | 17 | // `f` must return an IO 18 | IO.prototype.chain = function(f) { 19 | var io = this; 20 | return new IO(function() { 21 | var next = f(io.fn.apply(io, arguments)); 22 | return next.fn.apply(next, arguments); 23 | }); 24 | }; 25 | 26 | //chainRec 27 | IO.chainRec = IO.prototype.chainRec = function(f, i) { 28 | return new IO(function() { 29 | var state = util.chainRecNext(i); 30 | while (state.isNext) { 31 | state = f(util.chainRecNext, util.chainRecDone, state.value).fn(); 32 | } 33 | return state.value; 34 | }); 35 | }; 36 | 37 | IO.prototype.map = function(f) { 38 | var io = this; 39 | return new IO(compose(f, io.fn)); 40 | }; 41 | 42 | // `this` IO must wrap a function `f` that takes an IO (`thatIo`) as input 43 | // `f` must return an IO 44 | IO.prototype.ap = function(thatIo) { 45 | return this.chain(function(f) { 46 | return thatIo.map(f); 47 | }); 48 | }; 49 | 50 | IO.runIO = function(io) { 51 | return io.runIO.apply(io, [].slice.call(arguments, 1)); 52 | }; 53 | 54 | IO.prototype.runIO = function() { 55 | return this.fn.apply(this, arguments); 56 | }; 57 | 58 | IO.prototype.of = function(x) { 59 | return new IO(function() { return x; }); 60 | }; 61 | 62 | IO.of = IO.prototype.of; 63 | 64 | IO.prototype.toString = function() { 65 | return 'IO(' + toString(this.fn) + ')'; 66 | }; 67 | -------------------------------------------------------------------------------- /src/Identity.js: -------------------------------------------------------------------------------- 1 | var toString = require('ramda/src/toString'); 2 | 3 | var util = require('./internal/util'); 4 | 5 | 6 | /** 7 | * A data type that holds a value and exposes a monadic api. 8 | */ 9 | 10 | /** 11 | * Constructs a new `Identity[a]` data type that holds a single 12 | * value `a`. 13 | * @param {*} a Value of any type 14 | * @sig a -> Identity[a] 15 | */ 16 | function Identity(x) { 17 | if (!(this instanceof Identity)) { 18 | return new Identity(x); 19 | } 20 | this.value = x; 21 | } 22 | 23 | Identity.prototype['@@type'] = 'ramda-fantasy/Identity'; 24 | 25 | /** 26 | * Applicative specification. Creates a new `Identity[a]` holding the value `a`. 27 | * @param {*} a Value of any type 28 | * @returns Identity[a] 29 | * @sig a -> Identity[a] 30 | */ 31 | Identity.of = function(x) { 32 | return new Identity(x); 33 | }; 34 | Identity.prototype.of = Identity.of; 35 | 36 | /** 37 | * Functor specification. Creates a new `Identity[a]` mapping function `f` onto 38 | * `a` returning any value b. 39 | * @param {Function} f Maps `a` to any value `b` 40 | * @returns Identity[b] 41 | * @sig @Identity[a] => (a -> b) -> Identity[b] 42 | */ 43 | Identity.prototype.map = function(f) { 44 | return new Identity(f(this.value)); 45 | }; 46 | 47 | /** 48 | * Apply specification. Applies the function inside the `Identity[a]` 49 | * type to another applicative type. 50 | * @param {Applicative[a]} app Applicative that will apply its function 51 | * @returns Applicative[b] 52 | * @sig (Identity[a -> b], f: Applicative[_]) => f[a] -> f[b] 53 | */ 54 | Identity.prototype.ap = function(app) { 55 | return app.map(this.value); 56 | }; 57 | 58 | /** 59 | * Chain specification. Transforms the value of the `Identity[a]` 60 | * type using an unary function to monads. The `Identity[a]` type 61 | * should contain a function, otherwise an error is thrown. 62 | * 63 | * @param {Function} fn Transforms `a` into a `Monad[b]` 64 | * @returns Monad[b] 65 | * @sig (Identity[a], m: Monad[_]) => (a -> m[b]) -> m[b] 66 | */ 67 | Identity.prototype.chain = function(fn) { 68 | return fn(this.value); 69 | }; 70 | 71 | // chainRec 72 | Identity.chainRec = Identity.prototype.chainRec = function(f, i) { 73 | var state = util.chainRecNext(i); 74 | while (state.isNext) { 75 | state = f(util.chainRecNext, util.chainRecDone, state.value).get(); 76 | } 77 | return Identity(state.value); 78 | }; 79 | 80 | /** 81 | * Returns the value of `Identity[a]` 82 | * 83 | * @returns a 84 | * @sig (Identity[a]) => a 85 | */ 86 | Identity.prototype.get = function() { 87 | return this.value; 88 | }; 89 | 90 | // equality method to enable testing 91 | Identity.prototype.equals = util.getEquals(Identity); 92 | 93 | Identity.prototype.toString = function() { 94 | return 'Identity(' + toString(this.value) + ')'; 95 | }; 96 | 97 | module.exports = Identity; 98 | -------------------------------------------------------------------------------- /src/Maybe.js: -------------------------------------------------------------------------------- 1 | var toString = require('ramda/src/toString'); 2 | var curry = require('ramda/src/curry'); 3 | 4 | var util = require('./internal/util.js'); 5 | 6 | function Maybe(x) { 7 | return x == null ? _nothing : Maybe.Just(x); 8 | } 9 | 10 | Maybe.prototype['@@type'] = 'ramda-fantasy/Maybe'; 11 | 12 | function Just(x) { 13 | this.value = x; 14 | } 15 | util.extend(Just, Maybe); 16 | 17 | Just.prototype.isJust = true; 18 | Just.prototype.isNothing = false; 19 | 20 | function Nothing() {} 21 | util.extend(Nothing, Maybe); 22 | 23 | Nothing.prototype.isNothing = true; 24 | Nothing.prototype.isJust = false; 25 | 26 | var _nothing = new Nothing(); 27 | 28 | Maybe.Nothing = function() { 29 | return _nothing; 30 | }; 31 | 32 | Maybe.Just = function(x) { 33 | return new Just(x); 34 | }; 35 | 36 | Maybe.of = Maybe.Just; 37 | 38 | Maybe.prototype.of = Maybe.Just; 39 | 40 | Maybe.isJust = function(x) { 41 | return x.isJust; 42 | }; 43 | 44 | Maybe.isNothing = function(x) { 45 | return x.isNothing; 46 | }; 47 | 48 | Maybe.maybe = curry(function(nothingVal, justFn, m) { 49 | return m.reduce(function(_, x) { 50 | return justFn(x); 51 | }, nothingVal); 52 | }); 53 | 54 | Maybe.toMaybe = Maybe; 55 | 56 | // semigroup 57 | Just.prototype.concat = function(that) { 58 | return that.isNothing ? this : this.of( 59 | this.value.concat(that.value) 60 | ); 61 | }; 62 | 63 | Nothing.prototype.concat = util.identity; 64 | 65 | // functor 66 | Just.prototype.map = function(f) { 67 | return this.of(f(this.value)); 68 | }; 69 | 70 | Nothing.prototype.map = util.returnThis; 71 | 72 | // apply 73 | // takes a Maybe that wraps a function (`app`) and applies its `map` 74 | // method to this Maybe's value, which must be a function. 75 | Just.prototype.ap = function(m) { 76 | return m.map(this.value); 77 | }; 78 | 79 | Nothing.prototype.ap = util.returnThis; 80 | 81 | // applicative 82 | // `of` inherited from `Maybe` 83 | 84 | 85 | // chain 86 | // f must be a function which returns a value 87 | // f must return a value of the same Chain 88 | // chain must return a value of the same Chain 89 | Just.prototype.chain = util.baseMap; 90 | 91 | Nothing.prototype.chain = util.returnThis; 92 | 93 | 94 | //chainRec 95 | Maybe.chainRec = Maybe.prototype.chainRec = function(f, i) { 96 | var res, state = util.chainRecNext(i); 97 | while (state.isNext) { 98 | res = f(util.chainRecNext, util.chainRecDone, state.value); 99 | if (Maybe.isNothing(res)) { 100 | return res; 101 | } 102 | state = res.value; 103 | } 104 | return Maybe.Just(state.value); 105 | }; 106 | 107 | 108 | // 109 | Just.prototype.datatype = Just; 110 | 111 | Nothing.prototype.datatype = Nothing; 112 | 113 | // monad 114 | // A value that implements the Monad specification must also implement the Applicative and Chain specifications. 115 | // see above. 116 | 117 | // equality method to enable testing 118 | Just.prototype.equals = util.getEquals(Just); 119 | 120 | Nothing.prototype.equals = function(that) { 121 | return that === _nothing; 122 | }; 123 | 124 | Maybe.prototype.isNothing = function() { 125 | return this === _nothing; 126 | }; 127 | 128 | Maybe.prototype.isJust = function() { 129 | return this instanceof Just; 130 | }; 131 | 132 | Just.prototype.getOrElse = function() { 133 | return this.value; 134 | }; 135 | 136 | Nothing.prototype.getOrElse = function(a) { 137 | return a; 138 | }; 139 | 140 | Just.prototype.reduce = function(f, x) { 141 | return f(x, this.value); 142 | }; 143 | 144 | Nothing.prototype.reduce = function(f, x) { 145 | return x; 146 | }; 147 | 148 | Just.prototype.toString = function() { 149 | return 'Maybe.Just(' + toString(this.value) + ')'; 150 | }; 151 | 152 | Nothing.prototype.toString = function() { 153 | return 'Maybe.Nothing()'; 154 | }; 155 | 156 | module.exports = Maybe; 157 | -------------------------------------------------------------------------------- /src/Reader.js: -------------------------------------------------------------------------------- 1 | var compose = require('ramda/src/compose'); 2 | var identity = require('ramda/src/identity'); 3 | var toString = require('ramda/src/toString'); 4 | var always = require('ramda/src/always'); 5 | 6 | 7 | function Reader(run) { 8 | if (!(this instanceof Reader)) { 9 | return new Reader(run); 10 | } 11 | this.run = run; 12 | } 13 | 14 | Reader.run = function(reader) { 15 | return reader.run.apply(reader, [].slice.call(arguments, 1)); 16 | }; 17 | 18 | Reader.prototype['@@type'] = 'ramda-fantasy/Reader'; 19 | 20 | Reader.prototype.chain = function(f) { 21 | var reader = this; 22 | return new Reader(function(r) { 23 | return f(reader.run(r)).run(r); 24 | }); 25 | }; 26 | 27 | Reader.prototype.ap = function(a) { 28 | return this.chain(function(f) { 29 | return a.map(f); 30 | }); 31 | }; 32 | 33 | Reader.prototype.map = function(f) { 34 | return this.chain(function(a) { 35 | return Reader.of(f(a)); 36 | }); 37 | }; 38 | 39 | Reader.prototype.of = function(a) { 40 | return new Reader(function() { 41 | return a; 42 | }); 43 | }; 44 | Reader.of = Reader.prototype.of; 45 | 46 | Reader.ask = Reader(identity); 47 | 48 | Reader.prototype.toString = function() { 49 | return 'Reader(' + toString(this.run) + ')'; 50 | }; 51 | 52 | Reader.T = function(M) { 53 | var ReaderT = function ReaderT(run) { 54 | if (!(this instanceof ReaderT)) { 55 | return new ReaderT(run); 56 | } 57 | this.run = run; 58 | }; 59 | 60 | ReaderT.lift = compose(ReaderT, always); 61 | 62 | ReaderT.ask = ReaderT(M.of); 63 | 64 | ReaderT.prototype.of = ReaderT.of = function(a) { 65 | return ReaderT(function() { 66 | return M.of(a); 67 | }); 68 | }; 69 | 70 | ReaderT.prototype.chain = function(f) { 71 | var readerT = this; 72 | return ReaderT(function(e) { 73 | var m = readerT.run(e); 74 | return m.chain(function(a) { 75 | return f(a).run(e); 76 | }); 77 | }); 78 | }; 79 | 80 | ReaderT.prototype.map = function(f) { 81 | return this.chain(function(a) { 82 | return ReaderT.of(f(a)); 83 | }); 84 | }; 85 | 86 | ReaderT.prototype.ap = function(a) { 87 | var readerT = this; 88 | return ReaderT(function(e) { 89 | return readerT.run(e).ap(a.run(e)); 90 | }); 91 | }; 92 | 93 | ReaderT.prototype.toString = function() { 94 | return 'ReaderT[' + M.name + '](' + toString(this.run) + ')'; 95 | }; 96 | 97 | return ReaderT; 98 | }; 99 | 100 | module.exports = Reader; 101 | -------------------------------------------------------------------------------- /src/State.js: -------------------------------------------------------------------------------- 1 | var curry = require('ramda/src/curry'); 2 | 3 | var Identity = require('./Identity'); 4 | var Tuple = require('./Tuple'); 5 | var util = require('./internal/util'); 6 | 7 | 8 | function T(M) { 9 | function StateT(run) { 10 | if (!(this instanceof StateT)) { 11 | return new StateT(run); 12 | } 13 | this._run = run; 14 | } 15 | StateT.prototype.run = function(s) { 16 | return this._run(s); 17 | }; 18 | StateT.prototype.eval = function(s) { 19 | return Tuple.fst(this.run(s)); 20 | }; 21 | StateT.prototype.exec = function(s) { 22 | return Tuple.snd(this.run(s)); 23 | }; 24 | StateT.prototype.chain = function(f) { 25 | var state = this; 26 | return StateT(function(s) { 27 | return state._run(s).chain(function(t) { 28 | return f(Tuple.fst(t))._run(Tuple.snd(t)); 29 | }); 30 | }); 31 | }; 32 | StateT.of = StateT.prototype.of = function(a) { 33 | return StateT(function (s) { 34 | return M.of(Tuple(a, s)); 35 | }); 36 | }; 37 | StateT.prototype.ap = util.deriveAp(StateT); 38 | StateT.prototype.map = util.deriveMap(StateT); 39 | StateT.tailRec = curry(function(stepFn, init) { 40 | return StateT(function(s) { 41 | return M.tailRec(function(t) { 42 | return stepFn(Tuple.fst(t))._run(Tuple.snd(t)).chain(function (t_) { 43 | return M.of(Tuple.fst(t_).bimap( 44 | function(a) { return Tuple(a, Tuple.snd(t_)); }, 45 | function(b) { return Tuple(b, Tuple.snd(t_)); } 46 | )); 47 | }); 48 | }, Tuple(init, s)); 49 | }); 50 | }); 51 | StateT.lift = function(ma) { 52 | return StateT(function(s) { 53 | return ma.chain(function(a) { 54 | return M.of(Tuple(a, s)); 55 | }); 56 | }); 57 | }; 58 | StateT.get = StateT(function(s) { 59 | return M.of(Tuple(s, s)); 60 | }); 61 | StateT.gets = function(f) { 62 | return StateT(function(s) { 63 | return M.of(Tuple(f(s), s)); 64 | }); 65 | }; 66 | StateT.put = function(s) { 67 | return StateT(function(_) { 68 | return M.of(Tuple(void _, s)); 69 | }); 70 | }; 71 | StateT.modify = function(f) { 72 | return StateT(function(s) { 73 | return M.of(Tuple(void 0, f(s))); 74 | }); 75 | }; 76 | 77 | return StateT; 78 | } 79 | 80 | var State = T(Identity); 81 | State.T = T; 82 | State.prototype.run = function(s) { 83 | return this._run(s).value; 84 | }; 85 | 86 | module.exports = State; 87 | -------------------------------------------------------------------------------- /src/Tuple.js: -------------------------------------------------------------------------------- 1 | var toString = require('ramda/src/toString'); 2 | var equals = require('ramda/src/equals'); 3 | 4 | 5 | function Tuple(x, y) { 6 | switch (arguments.length) { 7 | case 0: 8 | throw new TypeError('no arguments to Tuple'); 9 | case 1: 10 | return function(y) { 11 | return new _Tuple(x, y); 12 | }; 13 | default: 14 | return new _Tuple(x, y); 15 | } 16 | } 17 | 18 | function _Tuple(x, y) { 19 | this[0] = x; 20 | this[1] = y; 21 | this.length = 2; 22 | } 23 | 24 | function ensureConcat(xs) { 25 | xs.forEach(function(x) { 26 | if (typeof x.concat != 'function') { 27 | throw new TypeError(toString(x) + ' must be a semigroup to perform this operation'); 28 | } 29 | }); 30 | } 31 | 32 | Tuple.fst = function(x) { 33 | return x[0]; 34 | }; 35 | 36 | Tuple.snd = function(x) { 37 | return x[1]; 38 | }; 39 | 40 | _Tuple.prototype['@@type'] = 'ramda-fantasy/Tuple'; 41 | 42 | // semigroup 43 | _Tuple.prototype.concat = function(x) { 44 | ensureConcat([this[0], this[1]]); 45 | return Tuple(this[0].concat(x[0]), this[1].concat(x[1])); 46 | }; 47 | 48 | // functor 49 | _Tuple.prototype.map = function(f) { 50 | return Tuple(this[0], f(this[1])); 51 | }; 52 | 53 | // apply 54 | _Tuple.prototype.ap = function(m) { 55 | ensureConcat([this[0]]); 56 | return Tuple(this[0].concat(m[0]), this[1](m[1])); 57 | }; 58 | 59 | // setoid 60 | _Tuple.prototype.equals = function(that) { 61 | return that instanceof _Tuple && equals(this[0], that[0]) && equals(this[1], that[1]); 62 | }; 63 | 64 | _Tuple.prototype.toString = function() { 65 | return 'Tuple(' + toString(this[0]) + ', ' + toString(this[1]) + ')'; 66 | }; 67 | 68 | module.exports = Tuple; 69 | -------------------------------------------------------------------------------- /src/internal/util.js: -------------------------------------------------------------------------------- 1 | var _equals = require('ramda/src/equals'); 2 | 3 | 4 | module.exports = { 5 | 6 | baseMap: function(f) { 7 | return f(this.value); 8 | }, 9 | 10 | getEquals: function(constructor) { 11 | return function equals(that) { 12 | return that instanceof constructor && _equals(this.value, that.value); 13 | }; 14 | }, 15 | 16 | extend: function(Child, Parent) { 17 | function Ctor() { 18 | this.constructor = Child; 19 | } 20 | Ctor.prototype = Parent.prototype; 21 | Child.prototype = new Ctor(); 22 | Child.super_ = Parent.prototype; 23 | }, 24 | 25 | identity: function(x) { return x; }, 26 | 27 | notImplemented: function(str) { 28 | return function() { 29 | throw new Error(str + ' is not implemented'); 30 | }; 31 | }, 32 | 33 | notCallable: function(fn) { 34 | return function() { 35 | throw new Error(fn + ' cannot be called directly'); 36 | }; 37 | }, 38 | 39 | returnThis: function() { return this; }, 40 | 41 | chainRecNext: function(v) { 42 | return { isNext: true, value: v }; 43 | }, 44 | 45 | chainRecDone: function(v) { 46 | return { isNext: false, value: v }; 47 | }, 48 | 49 | deriveAp: function (Type) { 50 | return function(fa) { 51 | return this.chain(function (f) { 52 | return fa.chain(function (a) { 53 | return Type.of(f(a)); 54 | }); 55 | }); 56 | }; 57 | }, 58 | 59 | deriveMap: function (Type) { 60 | return function (f) { 61 | return this.chain(function (a) { 62 | return Type.of(f(a)); 63 | }); 64 | }; 65 | } 66 | 67 | }; 68 | -------------------------------------------------------------------------------- /src/lift2.js: -------------------------------------------------------------------------------- 1 | var curryN = require('ramda/src/curryN'); 2 | 3 | module.exports = curryN(3, function lift2(f, a1, a2) { 4 | return a1.map(f).ap(a2); 5 | }); 6 | -------------------------------------------------------------------------------- /src/lift3.js: -------------------------------------------------------------------------------- 1 | var curryN = require('ramda/src/curryN'); 2 | 3 | module.exports = curryN(4, function lift3(f, a1, a2, a3) { 4 | return a1.map(f).ap(a2).ap(a3); 5 | }); 6 | -------------------------------------------------------------------------------- /test/either.test.js: -------------------------------------------------------------------------------- 1 | var R = require('ramda'); 2 | var assert = require('assert'); 3 | var jsv = require('jsverify'); 4 | var types = require('./types')(R.equals); 5 | 6 | var Either = require('..').Either; 7 | 8 | var leftArb = function(arb) { 9 | return arb.smap(Either.Left, R.prop('value'), R.toString); 10 | }; 11 | var rightArb = function(arb) { 12 | return arb.smap(Either.Right, R.prop('value'), R.toString); 13 | }; 14 | var eitherArb = function(arb) { 15 | return jsv.oneof([leftArb(arb), rightArb(arb)]); 16 | }; 17 | 18 | var eNatArb = eitherArb(jsv.nat); 19 | var eFnArb = eitherArb(jsv.fn(jsv.nat)); 20 | var fnNatArb = jsv.fn(jsv.nat); 21 | 22 | describe('Either', function() { 23 | 24 | it('is a Functor', function() { 25 | var fTest = types.functor; 26 | jsv.assert(jsv.forall(eNatArb, fTest.iface)); 27 | jsv.assert(jsv.forall(eNatArb, fTest.id)); 28 | jsv.assert(jsv.forall(eNatArb, fnNatArb, fnNatArb, fTest.compose)); 29 | }); 30 | 31 | it('is an Apply', function() { 32 | var aTest = types.apply; 33 | jsv.assert(jsv.forall(eFnArb, eFnArb, eNatArb, aTest.compose)); 34 | jsv.assert(jsv.forall(eNatArb, aTest.iface)); 35 | }); 36 | 37 | it('is an Applicative', function() { 38 | var aTest = types.applicative; 39 | jsv.assert(jsv.forall(eNatArb, aTest.iface)); 40 | jsv.assert(jsv.forall(eNatArb, eNatArb, aTest.id)); 41 | jsv.assert(jsv.forall(eNatArb, fnNatArb, jsv.nat, aTest.homomorphic)); 42 | jsv.assert(jsv.forall(eNatArb, eFnArb, jsv.nat, aTest.interchange)); 43 | }); 44 | 45 | it('is a Chain', function() { 46 | var cTest = types.chain; 47 | var fnEArb = jsv.fn(eNatArb); 48 | jsv.assert(jsv.forall(eNatArb, cTest.iface)); 49 | jsv.assert(jsv.forall(eNatArb, fnEArb, fnEArb, cTest.associative)); 50 | }); 51 | 52 | describe('ChainRec', function() { 53 | it('is a ChainRec', function() { 54 | var cTest = types.chainRec; 55 | var predicate = function(a) { 56 | return a.length > 5; 57 | }; 58 | var done = Either.of; 59 | var x = 1; 60 | var initial = [x]; 61 | var next = function(a) { 62 | return Either.of(a.concat([x])); 63 | }; 64 | assert.equal(true, cTest.iface(Either.of(1))); 65 | assert.equal(true, cTest.equivalence(Either, predicate, done, next, initial)); 66 | }); 67 | 68 | it('is stacksafe', function() { 69 | assert.equal(true, Either.of('DONE').equals(Either.chainRec(function(next, done, n) { 70 | if (n === 0) { 71 | return Either.of(done('DONE')); 72 | } else { 73 | return Either.of(next(n - 1)); 74 | } 75 | }, 100000))); 76 | }); 77 | 78 | it('responds to failure immediately', function() { 79 | assert.equal(true, Either.Left("ERROR").equals(Either.chainRec(function(/*next, done, n*/) { 80 | return Either.Left("ERROR"); 81 | }, 100))); 82 | }); 83 | 84 | it('responds to failure on next step', function() { 85 | assert.equal(true, Either.Left("ERROR").equals(Either.chainRec(function(next, done, n) { 86 | if (n === 0) { 87 | return Either.Left("ERROR"); 88 | } 89 | return Either.of(next(n - 1)); 90 | }, 100))); 91 | }); 92 | }); 93 | 94 | it('is a Monad', function() { 95 | jsv.assert(jsv.forall(eNatArb, types.monad.iface)); 96 | }); 97 | 98 | it('is an Extend', function() { 99 | var eTest = types.extend; 100 | jsv.assert(jsv.forall(eNatArb, eTest.iface)); 101 | jsv.assert(jsv.forall(eNatArb, fnNatArb, fnNatArb, eTest.associative)); 102 | }); 103 | 104 | describe('checking for Left | Right', function() { 105 | it('should allow the user to check if the instance is a Left', function() { 106 | jsv.assert(jsv.forall(leftArb(jsv.nat), rightArb(jsv.nat), function(l, r) { 107 | return l.isLeft === true && r.isLeft === false; 108 | })); 109 | }); 110 | 111 | it('should allow the user to check if the instance is a Right', function() { 112 | jsv.assert(jsv.forall(leftArb(jsv.nat), rightArb(jsv.nat), function(l, r) { 113 | return l.isRight === false && r.isRight === true; 114 | })); 115 | }); 116 | 117 | it('can check the type statically', function() { 118 | jsv.assert(jsv.forall(leftArb(jsv.nat), rightArb(jsv.nat), function(l, r) { 119 | return Either.isRight(l) === false && Either.isRight(r) === true && 120 | Either.isLeft(l) === true && Either.isLeft(r) === false; 121 | })); 122 | }); 123 | }); 124 | 125 | describe('#bimap', function() { 126 | 127 | it('maps the first function over the left value', function() { 128 | jsv.assert(jsv.forall(leftArb(jsv.nat), fnNatArb, fnNatArb, function(e, f, g) { 129 | return e.bimap(f, g).value === f(e.value); 130 | })); 131 | }); 132 | 133 | it('maps the second function over the right value', function() { 134 | jsv.assert(jsv.forall(rightArb(jsv.nat), fnNatArb, fnNatArb, function(e, f, g) { 135 | return e.bimap(f, g).value === g(e.value); 136 | })); 137 | }); 138 | 139 | }); 140 | 141 | describe('.either', function() { 142 | it('returns the value of a Left after applying the first function arg', function() { 143 | jsv.assert(jsv.forall(leftArb(jsv.nat), fnNatArb, fnNatArb, function(e, f, g) { 144 | return Either.either(f, g, e) === f(e.value); 145 | })); 146 | }); 147 | 148 | it('returns the value of a Right after applying the second function arg', function() { 149 | jsv.assert(jsv.forall(rightArb(jsv.nat), fnNatArb, fnNatArb, function(e, f, g) { 150 | return Either.either(f, g, e) === g(e.value); 151 | })); 152 | }); 153 | }); 154 | 155 | describe('#@@type', function() { 156 | 157 | it('is "ramda-fantasy/Either"', function() { 158 | assert.strictEqual(Either.Left(null)['@@type'], 'ramda-fantasy/Either'); 159 | assert.strictEqual(Either.Right(null)['@@type'], 'ramda-fantasy/Either'); 160 | }); 161 | 162 | }); 163 | 164 | describe('#toString', function() { 165 | 166 | it('returns the string representation of a Left', function() { 167 | assert.strictEqual(Either.Left('Cannot divide by zero').toString(), 168 | 'Either.Left("Cannot divide by zero")'); 169 | }); 170 | 171 | it('returns the string representation of a Right', function() { 172 | assert.strictEqual(Either.Right([1, 2, 3]).toString(), 173 | 'Either.Right([1, 2, 3])'); 174 | }); 175 | 176 | }); 177 | 178 | describe('#equals', function() { 179 | 180 | it('returns true if both contain equal values and are both Left', function() { 181 | assert.equal(true, Either.Left(1).equals(Either.Left(1))); 182 | }); 183 | 184 | it('returns true if both contain equal values and are both Right', function() { 185 | assert.equal(true, Either.Right(1).equals(Either.Right(1))); 186 | }); 187 | 188 | it('returns false if both contain equal values but are of different constructors', function() { 189 | assert.equal(false, Either.Left(1).equals(Either.Right(1))); 190 | }); 191 | 192 | it('returns false if both contain different values and are both Left', function() { 193 | assert.equal(false, Either.Left(0).equals(Either.Left(1))); 194 | }); 195 | 196 | it('returns false if both contain different values and are both Right', function() { 197 | assert.equal(false, Either.Right(0).equals(Either.Right(1))); 198 | }); 199 | 200 | }); 201 | 202 | 203 | 204 | describe('#either', function() { 205 | it('returns the value of a Left after applying the first function arg', function() { 206 | jsv.assert(jsv.forall(leftArb(jsv.nat), fnNatArb, fnNatArb, function(e, f, g) { 207 | return e.either(f, g) === f(e.value); 208 | })); 209 | }); 210 | 211 | it('returns the value of a Right after applying the second function arg', function() { 212 | jsv.assert(jsv.forall(rightArb(jsv.nat), fnNatArb, fnNatArb, function(e, f, g) { 213 | return e.either(f, g) === g(e.value); 214 | })); 215 | }); 216 | }); 217 | 218 | }); 219 | -------------------------------------------------------------------------------- /test/future.test.js: -------------------------------------------------------------------------------- 1 | var R = require('ramda'); 2 | var assert = require('assert'); 3 | var equalsInvoker = require('./utils').equalsInvoker; 4 | var types = require('./types')(equalsInvoker); 5 | var Future = require('../src/Future'); 6 | var Promise = require('promise'); 7 | 8 | Future.prototype.equals = function(b) { 9 | var self = this; 10 | return new Promise(function(resolve, reject) { 11 | self.fork(function(e1) { 12 | b.fork(function(e2) { 13 | try { 14 | assert.deepEqual(e1, e2); 15 | } catch (e) { reject(e); } 16 | resolve(); 17 | }, function() { 18 | try{ 19 | assert.fail(null, e1, 'Futures not equal: f1 failed, f2 did not', '==='); 20 | } catch (e) { reject(e); } 21 | reject(); 22 | }); 23 | }, function(v1) { 24 | b.fork(function() { 25 | try{ 26 | assert.fail(null, v1, 'Futures not equal: f1 succeeded, f2 did not', '==='); 27 | } catch (e) { reject(e); } 28 | reject(); 29 | }, function(v2) { 30 | try { 31 | assert.deepEqual(v1, v2); 32 | } catch (e) { reject(e); } 33 | resolve(); 34 | }); 35 | }); 36 | }); 37 | }; 38 | 39 | describe('Future', function() { 40 | 41 | describe('Equal', function() { 42 | it('should equal another future', function() { 43 | var f1 = Future.of(2); 44 | var f2 = Future.of(2); 45 | return f1.equals(f2); 46 | }); 47 | 48 | it('should equal another future (async)', function() { 49 | var f1 = Future.of(2); 50 | var f2 = Future(function(rej, res) { 51 | setTimeout(res, 1, 2); 52 | }); 53 | return f1.equals(f2); 54 | }); 55 | 56 | it('should equal another future (non-primitive value)', function() { 57 | var f1 = Future.of([2,2]); 58 | var f2 = Future.of([2,2]); 59 | return f1.equals(f2); 60 | }); 61 | }); 62 | 63 | it('is a Functor', function() { 64 | var fTest = types.functor; 65 | var f = Future.of(2); 66 | assert.equal(true, fTest.iface(f)); 67 | return Promise.all([ 68 | fTest.id(f), 69 | fTest.compose(f, R.multiply(2), R.add(3)) 70 | ]); 71 | }); 72 | 73 | it('is an Apply', function() { 74 | var aTest = types.apply; 75 | var appA = Future.of(R.multiply(10)); 76 | var appU = Future.of(R.add(5)); 77 | var appV = Future.of(10); 78 | assert.equal(true, aTest.iface(appA)); 79 | return aTest.compose(appA, appU, appV); 80 | }); 81 | 82 | it('is an Applicative', function() { 83 | var aTest = types.applicative; 84 | var app1 = Future.of(101); 85 | var app2 = Future.of(-123); 86 | var appF = Future.of(R.multiply(3)); 87 | 88 | assert.equal(true, aTest.iface(app1)); 89 | return Promise.all([ 90 | aTest.id(app1, app2), 91 | aTest.homomorphic(app1, R.add(3), 46), 92 | aTest.interchange(app1, appF, 17), 93 | ]); 94 | }); 95 | 96 | it('is a Chain', function() { 97 | var cTest = types.chain; 98 | var f = Future.of(2); 99 | var f1 = function(x) {return Future.of((3 * x));}; 100 | var f2 = function(x) {return Future.of((5 + x));}; 101 | 102 | assert.equal(true, cTest.iface(f)); 103 | return cTest.associative(f, f1, f2); 104 | }); 105 | 106 | describe('ChainRec', function() { 107 | it('is a ChainRec', function() { 108 | var cTest = types.chainRec; 109 | var predicate = function(a) { 110 | return a.length > 5; 111 | }; 112 | var done = Future.of; 113 | var x = 1; 114 | var initial = [x]; 115 | var next = function(a) { 116 | return Future.of(a.concat([x])); 117 | }; 118 | assert.equal(true, cTest.iface(Future.of(1))); 119 | return cTest.equivalence(Future, predicate, done, next, initial); 120 | }); 121 | 122 | it('works when mixing sync and async Futures', function() { 123 | return Future.of('DONE').equals(Future.chainRec(function(next, done, n) { 124 | if (n === 0) { 125 | return Future.of(done('DONE')); 126 | } else if (n > 100 || n === 1) { 127 | return Future.of(next(n - 1)); 128 | } else { 129 | return new Future(function(rej, res) { setTimeout(res, 0, next(n - 1)); }); 130 | } 131 | }, 100000)); 132 | }); 133 | 134 | it('responds to failure immediately', function() { 135 | return Future.reject('ERROR').equals(Future.chainRec(function(/*next, done, n*/) { 136 | return Future.reject('ERROR'); 137 | }, 100)); 138 | }); 139 | 140 | it('responds to failure on next step', function() { 141 | return Future.reject('ERROR').equals(Future.chainRec(function(next, done, n) { 142 | if (n === 0) { 143 | return Future.reject('ERROR'); 144 | } 145 | return Future.of(next(n - 1)); 146 | }, 100)); 147 | }); 148 | }); 149 | 150 | it('is a Monad', function() { 151 | var mTest = types.monad; 152 | var f = Future.of(null); 153 | assert.equal(true, mTest.iface(f)); 154 | }); 155 | 156 | it('.map should work according to the functor specification', function() { 157 | var result = Future.of(1).map(R.inc); 158 | return Future.of(2).equals(result); 159 | }); 160 | 161 | it('.chain should work according to the chainable specification', function() { 162 | var incInTheFuture = function(val) { 163 | return Future.of(R.inc(val)); 164 | }; 165 | var result = Future.of(1).chain(incInTheFuture); 166 | return Future.of(2).equals(result); 167 | }); 168 | 169 | describe('chainReject', function() { 170 | it('.chainReject should work like chain but off reject case', function() { 171 | var f1 = Future.reject(2); 172 | var f2 = function(val){ return Future.of(val + 3);}; 173 | return Future.of(5).equals(f1.chainReject(f2)); 174 | }); 175 | }); 176 | 177 | describe('#ap', function() { 178 | /*jshint browser:true */ 179 | var add = R.add; 180 | function delayError(delay, err) { 181 | /*jshint unused:false */ 182 | return new Future(function(reject, resolve) { 183 | setTimeout(reject, delay, err); 184 | }); 185 | } 186 | 187 | function delayValue(delay, value) { 188 | return new Future(function(reject, resolve) { 189 | setTimeout(resolve, delay, value); 190 | }); 191 | } 192 | 193 | function assertCbVal(done, expectedVal) { 194 | return function(val) { 195 | assert.equal(expectedVal, val); 196 | done(); 197 | }; 198 | } 199 | 200 | it('applies its function to the passed in future', function() { 201 | var f1 = Future.of(add(1)); 202 | var result = f1.ap(Future.of(2)); 203 | return Future.of(3).equals(result); 204 | }); 205 | 206 | it('does the apply in parallel', function(done) { 207 | this.timeout(25); 208 | var f1 = delayValue(15, 1); 209 | var f2 = delayValue(15, 2); 210 | f1.map(add).ap(f2).fork(assert.fail, assertCbVal(done, 3)); 211 | }); 212 | 213 | it('can handle itself being resolved first', function(done) { 214 | var f1 = delayValue(1, 1); 215 | var f2 = delayValue(15, 2); 216 | f1.map(add).ap(f2).fork(assert.fail, assertCbVal(done, 3)); 217 | }); 218 | 219 | it('can handle the input future being resolved first', function(done) { 220 | var f1 = delayValue(15, 1); 221 | var f2 = delayValue(1, 2); 222 | f1.map(add).ap(f2).fork(assert.fail, assertCbVal(done, 3)); 223 | }); 224 | 225 | it('is rejected with the first error to occur - case 1', function(done) { 226 | var f1 = delayError(10, 'firstError'); 227 | var f2 = delayError(20, 'secondError'); 228 | f1.map(add).ap(f2).fork(assertCbVal(done, 'firstError')); 229 | }); 230 | 231 | it('is rejected with the first error to occur - case 2', function(done) { 232 | var f1 = delayError(20, 'firstError'); 233 | var f2 = delayError(10, 'secondError'); 234 | f1.map(add).ap(f2).fork(assertCbVal(done, 'secondError')); 235 | }); 236 | 237 | }); 238 | 239 | describe('reject', function() { 240 | 241 | it('creates a rejected future with the given value', function() { 242 | var f = Future.reject('foo'); 243 | var forked; 244 | f.fork(function(err) { 245 | forked = true; 246 | assert.equal('foo', err); 247 | }); 248 | assert.equal(true, forked); 249 | }); 250 | 251 | }); 252 | 253 | describe('#bimap', function() { 254 | 255 | it('maps the first function over the rejected value', function() { 256 | var f = Future.reject('err'); 257 | var result = f.bimap(R.concat('map over ')); 258 | result.fork(function(e) { 259 | assert.equal(e, 'map over err'); 260 | }); 261 | }); 262 | 263 | it('maps the second function over the resolved value', function() { 264 | var f = Future.of(1); 265 | var result = f.bimap(null, R.add(1)); 266 | result.fork(null, function(v) { 267 | assert.equal(v, 2); 268 | }); 269 | }); 270 | 271 | }); 272 | 273 | describe('#@@type', function() { 274 | 275 | it('is "ramda-fantasy/Future"', function() { 276 | assert.strictEqual( 277 | Future(function(reject, resolve) { void resolve; })['@@type'], 278 | 'ramda-fantasy/Future' 279 | ); 280 | }); 281 | 282 | }); 283 | 284 | describe('#toString', function() { 285 | 286 | it('returns the string representation of a Future', function() { 287 | assert.strictEqual( 288 | Future(function(reject, resolve) { void resolve; }).toString(), 289 | 'Future(function (reject, resolve) { void resolve; })' 290 | ); 291 | }); 292 | 293 | }); 294 | 295 | describe('#fork', function() { 296 | 297 | var result; 298 | var futureOne = Future.of(1); 299 | var throwError = function() { 300 | throw new Error('Some error message'); 301 | }; 302 | var setErrorResult = function(e) { 303 | result = e.message; 304 | }; 305 | var delayValue = function(delay, value) { 306 | return new Future(function(reject, resolve) { 307 | setTimeout(resolve, delay, value); 308 | }); 309 | }; 310 | 311 | beforeEach(function() { 312 | result = null; 313 | }); 314 | 315 | it('creates a rejected future if the resolve function throws an error', function() { 316 | futureOne.fork(setErrorResult, throwError); 317 | assert.equal('Some error message', result); 318 | }); 319 | 320 | it('rejects the future if an error is thrown in a map function', function() { 321 | futureOne.map(throwError).fork(setErrorResult); 322 | assert.equal('Some error message', result); 323 | }); 324 | 325 | it('rejects the future if an error is thrown in a chain function', function() { 326 | futureOne.chain(throwError).fork(setErrorResult); 327 | assert.equal('Some error message', result); 328 | }); 329 | 330 | it('rejects the future if an error is thrown in a ap function', function() { 331 | Future.of(throwError).ap(futureOne).fork(setErrorResult); 332 | assert.equal('Some error message', result); 333 | }); 334 | 335 | it('eventually rejects the future if an error is thrown in a chain function', function(done){ 336 | var throwEscapeError = function(){ 337 | throw new Error('This error should be caught'); 338 | }; 339 | delayValue(15, 1).chain(throwEscapeError).fork( 340 | function(err){ 341 | assert.equal('This error should be caught', err.message); 342 | done(); 343 | }, 344 | function(){ 345 | done(new Error('The future resolved')); 346 | } 347 | ); 348 | }); 349 | 350 | }); 351 | 352 | describe('#cache', function() { 353 | var cached; 354 | var throwIfCalledTwice; 355 | 356 | beforeEach(function() { 357 | throwIfCalledTwice = (function() { 358 | var count = 0; 359 | return function(val) { 360 | if (++count > 1) { 361 | throw new Error('Was called twice'); 362 | } 363 | return val; 364 | }; 365 | }()); 366 | }); 367 | 368 | describe('resolve cases', function() { 369 | 370 | beforeEach(function() { 371 | cached = Future.cache(Future.of(1).map(throwIfCalledTwice)); 372 | }); 373 | 374 | it('can be forked with a resolved value', function(done) { 375 | cached.fork(done, function(v) { 376 | assert.equal(1, v); 377 | done(); 378 | }); 379 | }); 380 | 381 | it('passes on the same value to the cached future', function(done) { 382 | cached.fork(done, function() { 383 | cached.fork(done, function(v) { 384 | assert.equal(1, v); 385 | done(); 386 | }); 387 | }); 388 | }); 389 | 390 | }); 391 | 392 | describe('reject cases', function() { 393 | 394 | var throwError = function() { 395 | throw new Error('SomeError'); 396 | }; 397 | 398 | beforeEach(function() { 399 | cached = Future.cache(Future.of(1).map(throwIfCalledTwice).map(throwError)); 400 | }); 401 | 402 | it('can be forked with a rejected value', function() { 403 | var result; 404 | cached.fork(function(err) { 405 | result = err.message; 406 | }); 407 | assert.equal('SomeError', result); 408 | }); 409 | 410 | it('does not call the underlying fork twice', function() { 411 | var result; 412 | cached.fork(function() { 413 | cached.fork(function(err) { 414 | result = err.message; 415 | }); 416 | }); 417 | assert.equal('SomeError', result); 418 | }); 419 | 420 | }); 421 | 422 | describe('pending cases', function() { 423 | 424 | it('calls all fork resolve functions when the cached future is resolved', function(done) { 425 | var delayed = new Future(function(reject, resolve) { 426 | setTimeout(resolve, 5, 'resolvedValue'); 427 | }); 428 | var cached = Future.cache(delayed.map(throwIfCalledTwice)); 429 | var result1; 430 | var result2; 431 | function assertBoth() { 432 | if (result1 !== undefined && result2 !== undefined) { 433 | assert.equal('resolvedValue', result1); 434 | assert.equal('resolvedValue', result2); 435 | done(); 436 | } 437 | } 438 | cached.fork(done, function(v) { 439 | result1 = v; 440 | assertBoth(); 441 | }); 442 | cached.fork(done, function(v) { 443 | result2 = v; 444 | assertBoth(); 445 | }); 446 | }); 447 | 448 | it('calls all fork reject fnctions when the cached future is rejected', function(done) { 449 | var delayed = new Future(function(reject) { 450 | setTimeout(reject, 5, 'rejectedValue'); 451 | }); 452 | var cached = Future.cache(delayed.bimap(throwIfCalledTwice, R.identity)); 453 | var result1; 454 | var result2; 455 | function assertBoth() { 456 | if (result1 !== undefined && result2 !== undefined) { 457 | assert.equal('rejectedValue', result1); 458 | assert.equal('rejectedValue', result2); 459 | done(); 460 | } 461 | } 462 | cached.fork(function(e) { 463 | result1 = e; 464 | assertBoth(); 465 | }); 466 | cached.fork(function(e) { 467 | result2 = e; 468 | assertBoth(); 469 | }); 470 | 471 | }); 472 | 473 | }); 474 | 475 | }); 476 | 477 | }); 478 | -------------------------------------------------------------------------------- /test/identity.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var equalsInvoker = require('./utils').equalsInvoker; 3 | var types = require('./types')(equalsInvoker); 4 | 5 | var Identity = require('..').Identity; 6 | 7 | describe('Identity', function() { 8 | var m = Identity(1); 9 | 10 | function mult(a) { 11 | return function(b) { return a * b; }; 12 | } 13 | 14 | function add(a) { 15 | return function(b) { return a + b; }; 16 | } 17 | 18 | 19 | it('is a Functor', function() { 20 | var fTest = types.functor; 21 | assert.equal(true, fTest.iface(m)); 22 | assert.equal(true, fTest.id(m)); 23 | assert.equal(true, fTest.compose(m, mult(2), add(3))); 24 | }); 25 | 26 | it('is an Apply', function() { 27 | var aTest = types.apply; 28 | var appA = Identity(mult(10)); 29 | var appU = Identity(add(7)); 30 | var appV = Identity(10); 31 | 32 | assert.equal(true, aTest.iface(appA)); 33 | assert.equal(true, aTest.compose(appA, appU, appV)); 34 | }); 35 | 36 | it('is an Applicative', function() { 37 | var aTest = types.applicative; 38 | var app1 = Identity(101); 39 | var app2 = Identity(-123); 40 | var appF = Identity(mult(3)); 41 | 42 | assert.equal(true, aTest.iface(app1)); 43 | assert.equal(true, aTest.id(app1, app2)); 44 | assert.equal(true, aTest.homomorphic(app1, add(3), 46)); 45 | assert.equal(true, aTest.interchange(app2, appF, 17)); 46 | }); 47 | 48 | it('is a Chain', function() { 49 | var cTest = types.chain; 50 | var f1 = function(x) {return Identity(3 * x);}; 51 | var f2 = function(x) {return Identity(5 + x);}; 52 | var fNull = function() {return Identity(null);}; 53 | assert.equal(true, cTest.iface(m)); 54 | assert.equal(true, cTest.associative(m, f1, f2)); 55 | assert.equal(true, cTest.associative(m, fNull, f2)); 56 | assert.equal(true, cTest.associative(m, f1, fNull)); 57 | assert.equal(true, cTest.associative(m, fNull, fNull)); 58 | }); 59 | 60 | describe('ChainRec', function() { 61 | it('is a ChainRec', function() { 62 | var cTest = types.chainRec; 63 | var predicate = function(a) { 64 | return a.length > 5; 65 | }; 66 | var done = Identity.of; 67 | var x = 1; 68 | var initial = [x]; 69 | var next = function(a) { 70 | return Identity.of(a.concat([x])); 71 | }; 72 | assert.equal(true, cTest.iface(Identity.of(1))); 73 | assert.equal(true, cTest.equivalence(Identity, predicate, done, next, initial)); 74 | }); 75 | 76 | it('is stacksafe', function() { 77 | assert.equal(true, Identity.of('DONE').equals(Identity.chainRec(function(next, done, n) { 78 | if (n === 0) { 79 | return Identity.of(done('DONE')); 80 | } else { 81 | return Identity.of(next(n - 1)); 82 | } 83 | }, 100000))); 84 | }); 85 | }); 86 | 87 | it('is a Monad', function() { 88 | var mTest = types.monad; 89 | assert.equal(true, mTest.iface(m)); 90 | }); 91 | 92 | describe('#@@type', function() { 93 | 94 | it('is "ramda-fantasy/Identity"', function() { 95 | assert.strictEqual(Identity(null)['@@type'], 'ramda-fantasy/Identity'); 96 | }); 97 | 98 | }); 99 | 100 | describe('#toString', function() { 101 | 102 | it('returns the string representation of an Identity', function() { 103 | assert.strictEqual(Identity([1, 2, 3]).toString(), 104 | 'Identity([1, 2, 3])'); 105 | assert.strictEqual(Identity(Identity('abc')).toString(), 106 | 'Identity(Identity("abc"))'); 107 | }); 108 | 109 | }); 110 | 111 | }); 112 | 113 | describe('Identity example', function() { 114 | 115 | it('returns wrapped value', function() { 116 | var identNumber = Identity(4); 117 | assert.equal(identNumber.get(), 4); 118 | 119 | var identArray = Identity([1, 2, 3, 4]); 120 | assert.deepEqual(identArray.get(), [1, 2, 3, 4]); 121 | }); 122 | 123 | }); 124 | -------------------------------------------------------------------------------- /test/io.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var equals = require('ramda/src/equals'); 3 | var types = require('./types')(function(io1, io2) { 4 | return io1.equals(io2); 5 | }); 6 | 7 | var IO = require('..').IO; 8 | 9 | IO.prototype.equals = function(b) { 10 | return equals(this.runIO('x'), b.runIO('x')); 11 | }; 12 | 13 | function add(a) { 14 | return function(b) { return a + b; }; 15 | } 16 | 17 | function always(x) { 18 | return function() { return x; }; 19 | } 20 | 21 | function mult(a) { 22 | return function(b) { return a * b; }; 23 | } 24 | 25 | function identity(x) { return x; } 26 | 27 | describe('IO', function() { 28 | var logger = (function() { 29 | var results = []; 30 | return { 31 | log: function(str) {results.push(str);}, 32 | clear: function() {results = [];}, 33 | report: function() {return results.join(' ~ ');} 34 | }; 35 | }()); 36 | var f1 = function() { logger.log('IO 1'); return '1 '; }; 37 | var f2 = function(x) { logger.log('IO 2'); return x + '2 '; }; 38 | var f3 = function(x) { logger.log('IO 3'); return x + '3 '; }; 39 | var i1 = IO(f1); 40 | var i2 = IO(f2); 41 | 42 | beforeEach(function() { 43 | logger.clear(); 44 | }); 45 | 46 | it('is a Functor', function() { 47 | var fTest = types.functor; 48 | assert.equal(true, fTest.iface(i1)); 49 | assert.equal(true, fTest.id(i1)); 50 | assert.equal(logger.report(), 'IO 1 ~ IO 1'); 51 | logger.clear(); 52 | assert.equal(true, fTest.compose(i1, f2, f3)); 53 | assert.equal(logger.report(), 'IO 1 ~ IO 3 ~ IO 2 ~ IO 1 ~ IO 3 ~ IO 2'); 54 | }); 55 | 56 | it('is an Apply', function() { 57 | var aTest = types.apply; 58 | var a = IO(function() { return add(1); }); 59 | var b = IO(function() { return always(2); }); 60 | var c = IO(always(4)); 61 | 62 | assert.equal(true, aTest.iface(i1)); 63 | assert.equal(true, aTest.compose(a, b, c)); 64 | }); 65 | 66 | it('is an Applicative', function() { 67 | var aTest = types.applicative; 68 | 69 | assert.equal(true, aTest.iface(i1)); 70 | assert.equal(true, aTest.id(IO, i2)); 71 | assert.equal(logger.report(), 'IO 2 ~ IO 2'); 72 | assert.equal(true, aTest.homomorphic(i1, add(3), 46)); 73 | assert.equal(true, aTest.interchange( 74 | IO(function() { return mult(20); }), 75 | IO(function() { return mult(0.5); }), 76 | 73 77 | )); 78 | }); 79 | 80 | it('is a Chain', function() { 81 | var cTest = types.chain; 82 | var c = IO(function() { 83 | return IO(function() { 84 | return IO(function() { 85 | return 3; 86 | }); 87 | }); 88 | }); 89 | assert.equal(true, cTest.iface(i1)); 90 | assert.equal(true, cTest.associative(c, identity, identity)); 91 | }); 92 | 93 | describe('ChainRec', function() { 94 | it('is a ChainRec', function() { 95 | var cTest = types.chainRec; 96 | var predicate = function(a) { 97 | return a.length > 5; 98 | }; 99 | var done = IO.of; 100 | var x = 1; 101 | var initial = [x]; 102 | var next = function(a) { 103 | return IO.of(a.concat([x])); 104 | }; 105 | assert.equal(true, cTest.iface(IO.of(1))); 106 | assert.equal(true, cTest.equivalence(IO, predicate, done, next, initial)); 107 | }); 108 | 109 | it('is stacksafe', function() { 110 | assert.equal(true, IO.of('DONE').equals(IO.chainRec(function(next, done, n) { 111 | if (n === 0) { 112 | return IO.of(done('DONE')); 113 | } else { 114 | return IO.of(next(n - 1)); 115 | } 116 | }, 100000))); 117 | }); 118 | }); 119 | 120 | it('is a Monad', function() { 121 | var mTest = types.monad; 122 | assert.equal(true, mTest.iface(i1)); 123 | }); 124 | 125 | describe('#@@type', function() { 126 | 127 | it('is "ramda-fantasy/IO"', function() { 128 | assert.strictEqual(IO(function() {})['@@type'], 'ramda-fantasy/IO'); 129 | }); 130 | 131 | }); 132 | 133 | describe('#toString', function() { 134 | 135 | it('returns the string representation of an IO', function() { 136 | assert.strictEqual(IO(function() {}).toString(), 137 | 'IO(function () {})'); 138 | }); 139 | 140 | }); 141 | 142 | }); 143 | -------------------------------------------------------------------------------- /test/lift2.test.js: -------------------------------------------------------------------------------- 1 | var lift2 = require('../src/lift2'); 2 | var Identity = require('../src/Identity'); 3 | var R = require('ramda'); 4 | var assert = require('assert'); 5 | 6 | describe('lift2', function() { 7 | 8 | var i1 = Identity.of(1); 9 | var i2 = Identity.of(2); 10 | 11 | it('lifts the values of two applys into a curried function', function() { 12 | var result = lift2(R.add, i1, i2); 13 | assert.equal(true, Identity.of(3).equals(result)); 14 | }); 15 | 16 | it('is itself curried', function() { 17 | var step1 = lift2(R.add); 18 | var step2 = step1(i1); 19 | var result = step2(i2); 20 | assert.equal(true, Identity.of(3).equals(result)); 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /test/lift3.test.js: -------------------------------------------------------------------------------- 1 | var lift3 = require('../src/lift3'); 2 | var Identity = require('../src/Identity'); 3 | var R = require('ramda'); 4 | var assert = require('assert'); 5 | 6 | describe('lift3', function() { 7 | 8 | var combine3 = R.curry(function(a, b, c) { 9 | return [a, b, c].join(' '); 10 | }); 11 | 12 | var i1 = Identity.of('foo'); 13 | var i2 = Identity.of('bar'); 14 | var i3 = Identity.of('baz'); 15 | 16 | it('lifts the values of three applys into a curried function', function() { 17 | var result = lift3(combine3, i1, i2, i3); 18 | assert.equal(true, Identity.of('foo bar baz').equals(result)); 19 | }); 20 | 21 | it('is itself curried', function() { 22 | var step1 = lift3(combine3); 23 | var step2 = step1(i1); 24 | var step3 = step2(i2); 25 | var result = step3(i3); 26 | assert.equal(true, Identity.of('foo bar baz').equals(result)); 27 | }); 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /test/maybe.test.js: -------------------------------------------------------------------------------- 1 | var R = require('ramda'); 2 | var assert = require('assert'); 3 | var equalsInvoker = require('./utils').equalsInvoker; 4 | var types = require('./types')(equalsInvoker); 5 | var jsv = require('jsverify'); 6 | 7 | var Maybe = require('..').Maybe; 8 | 9 | var MaybeGen = R.curry(function(a, n) { 10 | return n % 2 === 0 ? Maybe.Just(a.generator(n)) : Maybe.Nothing(); 11 | }); 12 | 13 | var MaybeShow = R.curry(function(a, m) { 14 | return (Maybe.isJust(m)) ? 15 | 'Just(' + a.show(m.value) + ')' : 16 | 'Nothing'; 17 | }); 18 | 19 | var MaybeShrink = R.curry(function(a, m) { 20 | return (Maybe.isJust(m)) ? 21 | [Maybe.Nothing()].concat(a.shrink(m.value).map(Maybe.Just)) : 22 | []; 23 | }); 24 | 25 | var MaybeArb = function(a) { 26 | return { 27 | generator: jsv.generator.bless(MaybeGen(a)), 28 | show: MaybeShow(a), 29 | shrink: jsv.shrink.bless(MaybeShrink(a)) 30 | }; 31 | }; 32 | 33 | describe('Maybe', function() { 34 | var m = MaybeArb(jsv.nat); 35 | var env = {Maybe: MaybeArb}; 36 | var appF = 'Maybe (nat -> nat)'; 37 | var appN = 'Maybe nat'; 38 | 39 | it('has an arbitrary', function() { 40 | var arb = jsv.forall(m, function(m) { 41 | return m instanceof Maybe; 42 | }); 43 | jsv.assert(arb); 44 | }); 45 | 46 | it('is a Semigroup', function() { 47 | var sTest = types.semigroup; 48 | 49 | // Inner type needs to be a semigroup. 50 | var s = MaybeArb(jsv.array(jsv.nat)); 51 | var s1 = MaybeArb(jsv.array(jsv.nat)); 52 | var s2 = MaybeArb(jsv.array(jsv.nat)); 53 | 54 | jsv.assert(jsv.forall(s, sTest.iface)); 55 | jsv.assert(jsv.forall(s, s1, s2, sTest.associative)); 56 | }); 57 | 58 | it('is a Functor', function() { 59 | var fTest = types.functor; 60 | 61 | jsv.assert(jsv.forall(m, fTest.iface)); 62 | jsv.assert(jsv.forall(m, fTest.id)); 63 | jsv.assert(jsv.forall(m, 'nat -> nat', 'nat -> nat', fTest.compose)); 64 | }); 65 | 66 | it('is an Apply', function() { 67 | var aTest = types.apply; 68 | 69 | jsv.assert(jsv.forall(m, aTest.iface)); 70 | jsv.assert(jsv.forall(appF, appF, appN, env, aTest.compose)); 71 | }); 72 | 73 | it('is an Applicative', function() { 74 | var aTest = types.applicative; 75 | 76 | jsv.assert(jsv.forall(m, aTest.iface)); 77 | jsv.assert(jsv.forall(appN, appN, env, aTest.id)); 78 | jsv.assert(jsv.forall(appN, 'nat -> nat', 'nat', env, aTest.homomorphic)); 79 | jsv.assert(jsv.forall(appN, appF, 'nat', env, aTest.interchange)); 80 | }); 81 | 82 | it('is a Chain', function() { 83 | var cTest = types.chain; 84 | var f = 'nat -> Maybe nat'; 85 | 86 | jsv.assert(jsv.forall(m, cTest.iface)); 87 | jsv.assert(jsv.forall(m, f, f, env, cTest.associative)); 88 | }); 89 | 90 | describe('ChainRec', function() { 91 | it('is a ChainRec', function() { 92 | var cTest = types.chainRec; 93 | var predicate = function(a) { 94 | return a.length > 5; 95 | }; 96 | var done = Maybe.of; 97 | var x = 1; 98 | var initial = [x]; 99 | var next = function(a) { 100 | return Maybe.of(a.concat([x])); 101 | }; 102 | assert.equal(true, cTest.iface(Maybe.of(1))); 103 | assert.equal(true, cTest.equivalence(Maybe, predicate, done, next, initial)); 104 | }); 105 | 106 | it('is stacksafe', function() { 107 | var a = Maybe.chainRec(function(next, done, n) { 108 | if (n === 0) { 109 | return Maybe.of(done('DONE')); 110 | } else { 111 | return Maybe.of(next(n - 1)); 112 | } 113 | }, 100000); 114 | assert.equal(true, Maybe.of('DONE').equals(a)); 115 | }); 116 | 117 | it('responds to failure immediately', function() { 118 | assert.equal(true, Maybe.Nothing().equals(Maybe.chainRec(function(/*next, done, n*/) { 119 | return Maybe.Nothing(); 120 | }, 100))); 121 | }); 122 | 123 | it('responds to failure on next step', function() { 124 | return Maybe.Nothing().equals(Maybe.chainRec(function(next, done, n) { 125 | if (n === 0) { 126 | return Maybe.Nothing(); 127 | } 128 | return Maybe.of(next(n - 1)); 129 | }, 100)); 130 | }); 131 | }); 132 | 133 | it('is a Monad', function() { 134 | var mTest = types.monad; 135 | 136 | jsv.assert(jsv.forall(m, mTest.iface)); 137 | }); 138 | 139 | it('is Foldable', function() { 140 | var fTest = types.foldable; 141 | 142 | jsv.assert(jsv.forall(m, fTest.iface)); 143 | jsv.assert(jsv.forall('nat -> nat -> nat', 'nat', 'nat', function(f, n1, n2) { 144 | return Maybe.Just(n1).reduce(R.uncurryN(2, f), n2) === f(n2)(n1); 145 | })); 146 | jsv.assert(jsv.forall('nat -> nat -> nat', 'nat', function(f, n) { 147 | return Maybe.Nothing().reduce(R.uncurryN(2, f), n) === n; 148 | })); 149 | }); 150 | }); 151 | 152 | describe('Maybe usage', function() { 153 | 154 | describe('checking for Just | Nothing', function() { 155 | it('should allow the user to check if the instance is a Nothing', function() { 156 | assert.equal(true, Maybe(null).isNothing); 157 | assert.equal(false, Maybe(42).isNothing); 158 | }); 159 | 160 | it('should allow the user to check if the instance is a Just', function() { 161 | assert.equal(true, Maybe(42).isJust); 162 | assert.equal(false, Maybe(null).isJust); 163 | }); 164 | 165 | it('can check the type statically', function() { 166 | var nada = Maybe.Nothing(); 167 | var just1 = Maybe.Just(1); 168 | assert.equal(Maybe.isJust(nada), false); 169 | assert.equal(Maybe.isNothing(nada), true); 170 | assert.equal(Maybe.isJust(just1), true); 171 | assert.equal(Maybe.isNothing(just1), false); 172 | }); 173 | }); 174 | 175 | describe('#getOrElse', function() { 176 | 177 | it('should return the contained value for if the instance is a Just', function() { 178 | assert.equal(42, Maybe(42).getOrElse(24)); 179 | }); 180 | 181 | it('should return the input value if the instance is a Nothing', function() { 182 | assert.equal(24, Maybe(null).getOrElse(24)); 183 | }); 184 | 185 | }); 186 | 187 | describe('#@@type', function() { 188 | 189 | it('is "ramda-fantasy/Maybe"', function() { 190 | assert.strictEqual(Maybe.Just(null)['@@type'], 'ramda-fantasy/Maybe'); 191 | assert.strictEqual(Maybe.Nothing()['@@type'], 'ramda-fantasy/Maybe'); 192 | }); 193 | 194 | }); 195 | 196 | describe('#toString', function() { 197 | 198 | it('returns the string representation of a Just', function() { 199 | assert.strictEqual(Maybe.Just([1, 2, 3]).toString(), 200 | 'Maybe.Just([1, 2, 3])'); 201 | }); 202 | 203 | it('returns the string representation of a Nothing', function() { 204 | assert.strictEqual(Maybe.Nothing().toString(), 205 | 'Maybe.Nothing()'); 206 | }); 207 | 208 | }); 209 | 210 | describe('#maybe', function() { 211 | 212 | it('returns the result of applying the value of a Just to the second argument', function() { 213 | jsv.assert(jsv.forall('nat -> nat', 'nat', 'nat', function(f, n1, n2) { 214 | return R.equals(Maybe.maybe(n2, f, Maybe.Just(n1)), f(n1)); 215 | })); 216 | }); 217 | 218 | it('returns the first argument for a Nothing', function() { 219 | jsv.assert(jsv.forall('nat -> nat', 'nat', 'nat', function(f, n) { 220 | return R.equals(Maybe.maybe(n, f, Maybe.Nothing()), n); 221 | })); 222 | }); 223 | }); 224 | 225 | describe('#toMaybe', function() { 226 | it('produces a Nothing for null/undefined', function() { 227 | assert(Maybe.toMaybe(null).isNothing); 228 | assert(Maybe.toMaybe(void 0).isNothing); 229 | }); 230 | 231 | it('produces a Just for non-null values', function() { 232 | jsv.assert(jsv.forall(jsv.integer, function(n) { 233 | return Maybe.toMaybe(n).isJust; 234 | })); 235 | }); 236 | }); 237 | }); 238 | -------------------------------------------------------------------------------- /test/reader.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var types = require('./types'); 3 | 4 | var readerTypes = types(function(r1, r2) { 5 | return r1.run('x') === r2.run('x'); 6 | }); 7 | 8 | var transformerTypes = types(function(r1, r2) { 9 | return r1.run('x').get() === r2.run('x').get(); 10 | }); 11 | 12 | var Identity = require('..').Identity; 13 | var Reader = require('../src/Reader'); 14 | 15 | var ReaderTIdentity = Reader.T(Identity); 16 | 17 | function add(a) { 18 | return function(b) { return a + b; }; 19 | } 20 | 21 | function always(x) { 22 | return function() { return x; }; 23 | } 24 | 25 | function mult(a) { 26 | return function(b) { return a * b; }; 27 | } 28 | 29 | function identity(x) { return x; } 30 | 31 | describe('Reader properties', function() { 32 | 33 | var f1 = function(x) { return x + '1 '; }; 34 | var f2 = function(x) { return x + '2 '; }; 35 | var f3 = function(x) { return x + '3 '; }; 36 | var r1 = Reader(f1); 37 | var r2 = Reader(f2); 38 | 39 | it('is a Functor', function() { 40 | var fTest = readerTypes.functor; 41 | assert.ok(fTest.iface(r1)); 42 | assert.ok(fTest.id(r1)); 43 | assert.ok(fTest.compose(r1, f2, f3)); 44 | }); 45 | 46 | it('is an Apply', function() { 47 | var aTest = readerTypes.apply; 48 | var a = Reader(function() { return add(1); }); 49 | var b = Reader(function() { return always(2); }); 50 | var c = Reader(always(4)); 51 | 52 | assert.equal(true, aTest.iface(r1)); 53 | assert.equal(true, aTest.compose(a, b, c)); 54 | }); 55 | 56 | it('is an Applicative', function() { 57 | var aTest = readerTypes.applicative; 58 | 59 | assert.equal(true, aTest.iface(r1)); 60 | assert.equal(true, aTest.id(Reader, r2)); 61 | assert.equal(true, aTest.homomorphic(r1, add(3), 46)); 62 | assert.equal(true, aTest.interchange( 63 | Reader(function() { return mult(20); }), 64 | Reader(function() { return mult(0.5); }), 65 | 73 66 | )); 67 | }); 68 | 69 | it('is a Chain', function() { 70 | var cTest = readerTypes.chain; 71 | var c = Reader(function() { 72 | return Reader(function() { 73 | return Reader(function() { 74 | return 3; 75 | }); 76 | }); 77 | }); 78 | assert.equal(true, cTest.iface(r1)); 79 | assert.equal(true, cTest.associative(c, identity, identity)); 80 | }); 81 | 82 | it('is a Monad', function() { 83 | var mTest = readerTypes.monad; 84 | assert.equal(true, mTest.iface(r1)); 85 | }); 86 | 87 | describe('.ask', function() { 88 | it('provides access to the environment', function() { 89 | var r = Reader.ask.map(add(100)); 90 | assert.equal(101, r.run(1)); 91 | }); 92 | }); 93 | 94 | describe('#@@type', function() { 95 | 96 | it('is "ramda-fantasy/Reader"', function() { 97 | assert.strictEqual(Reader(function(x) { void x; })['@@type'], 98 | 'ramda-fantasy/Reader'); 99 | }); 100 | 101 | }); 102 | 103 | describe('#toString', function() { 104 | 105 | it('returns the string representation of a Reader', function() { 106 | assert.strictEqual(Reader(function(x) { void x; }).toString(), 107 | 'Reader(function (x) { void x; })'); 108 | }); 109 | 110 | }); 111 | 112 | }); 113 | 114 | describe('Reader examples', function() { 115 | it('should write name of options object', function() { 116 | 117 | var options = {name: 'header'}; 118 | var Printer = {}; 119 | Printer.write = function(x) { 120 | return '/** ' + x + ' */'; 121 | }; 122 | 123 | function getOptionsName(opts) { 124 | return Reader(function(printer) { 125 | return printer.write(opts.name); 126 | }); 127 | } 128 | 129 | var nameReader = getOptionsName(options); 130 | 131 | assert.equal(Reader.run(nameReader, Printer), '/** header */'); 132 | }); 133 | }); 134 | 135 | describe('Reader.T', function() { 136 | var r1 = ReaderTIdentity(function(x) { 137 | return Identity(x + '1 '); 138 | }); 139 | var r2 = ReaderTIdentity(function(x) { 140 | return Identity(x + '2 '); 141 | }); 142 | 143 | it('is a Functor', function() { 144 | var fTest = transformerTypes.functor; 145 | assert(fTest.iface(r1)); 146 | assert(fTest.id(r1)); 147 | assert(fTest.compose(r1, 148 | function(x) { return x + 'a'; }, 149 | function(x) { return x + 'b'; } 150 | )); 151 | }); 152 | 153 | it('is an Apply', function() { 154 | var aTest = transformerTypes.apply; 155 | var a = ReaderTIdentity(function() { return Identity(add(1)); }); 156 | var b = ReaderTIdentity(function() { return Identity(always(2)); }); 157 | var c = ReaderTIdentity(always(Identity(4))); 158 | 159 | assert(aTest.iface(r1)); 160 | assert(aTest.compose(a, b, c)); 161 | }); 162 | 163 | it('is an Applicative', function() { 164 | var aTest = transformerTypes.applicative; 165 | 166 | assert(aTest.iface(r1)); 167 | assert(aTest.id(ReaderTIdentity, r2)); 168 | assert(aTest.homomorphic(r1, add(3), 46)); 169 | assert(aTest.interchange( 170 | ReaderTIdentity(function() { return Identity(mult(20)); }), 171 | ReaderTIdentity(function() { return Identity(mult(0.5)); }), 172 | 73 173 | )); 174 | }); 175 | 176 | it('is a Chain', function() { 177 | var cTest = transformerTypes.chain; 178 | var c = ReaderTIdentity(function() { 179 | return Identity(ReaderTIdentity(function() { 180 | return Identity(ReaderTIdentity(function() { 181 | return Identity(3); 182 | })); 183 | })); 184 | }); 185 | assert(cTest.iface(r1)); 186 | assert(cTest.associative(c, identity, identity)); 187 | }); 188 | 189 | it('is a Monad', function() { 190 | var mTest = transformerTypes.monad; 191 | assert(mTest.iface(r1)); 192 | }); 193 | 194 | it('is a Transformer', function() { 195 | var mtTest = transformerTypes.transformer; 196 | assert(mtTest.iface(Reader.T)); 197 | assert(mtTest.id(Reader.T)); 198 | assert(mtTest.associative(Reader.T)); 199 | }); 200 | }); 201 | 202 | describe('Reader.T examples', function() { 203 | it('should provide its environment to a lifted monad', function() { 204 | var readerTimes10 = ReaderTIdentity.ask.chain(function(env) { 205 | return ReaderTIdentity.lift(Identity(env * 10)); 206 | }); 207 | assert.strictEqual(readerTimes10.run(3).get(), 30); 208 | }); 209 | }); 210 | -------------------------------------------------------------------------------- /test/state.test.js: -------------------------------------------------------------------------------- 1 | var R = require('ramda'); 2 | var jsv = require('jsverify'); 3 | var types = require('./types')(function(sA, sB) { 4 | return R.equals(sA.run(0), sB.run(0)); 5 | }); 6 | 7 | var Identity = require('..').Identity; 8 | var State = require('..').State; 9 | var Tuple = require('..').Tuple; 10 | 11 | function tupleArb(firstArb, secondArb) { 12 | return jsv.pair(firstArb, secondArb).smap( 13 | function (pair) { return Tuple(pair[0], pair[1]); }, 14 | function (t) { return [Tuple.fst(t), Tuple.snd(t)]; }, 15 | R.toString 16 | ); 17 | } 18 | 19 | function stateArb(evalArb, execArb) { 20 | return jsv.fn(tupleArb(evalArb, execArb)).smap( 21 | function (f) { return State(R.compose(Identity, f)); }, 22 | function (s) { return s.run; } 23 | ); 24 | } 25 | 26 | function identityArb(valueArb) { 27 | return valueArb.smap( 28 | Identity, 29 | function (i) { return i.value; }, 30 | R.toString 31 | ); 32 | } 33 | 34 | var fnNatArb = jsv.fn(jsv.nat); 35 | var stateNatArb = stateArb(jsv.nat, jsv.nat); 36 | var stateFnNatArb = stateArb(fnNatArb, jsv.nat); 37 | var stateConstructorArb = jsv.fn(identityArb(tupleArb(jsv.nat, jsv.nat))); 38 | 39 | describe('State', function() { 40 | it('returns a Tuple of the state and result via state.run', function () { 41 | jsv.assert(jsv.forall(jsv.nat, stateConstructorArb, function (s, fn) { 42 | return R.equals(State(fn).run(s), fn(s).value); 43 | })); 44 | }); 45 | 46 | it('returns the result via state.eval', function () { 47 | jsv.assert(jsv.forall(jsv.nat, stateConstructorArb, function (s, fn) { 48 | return R.equals(State(fn).eval(s), Tuple.fst(fn(s).value)); 49 | })); 50 | }); 51 | 52 | it('returns the state via state.exec', function () { 53 | jsv.assert(jsv.forall(jsv.nat, stateConstructorArb, function (s, fn) { 54 | return R.equals(State(fn).exec(s), Tuple.snd(fn(s).value)); 55 | })); 56 | }); 57 | 58 | it('retrieves the current state via the State.get instance', function () { 59 | jsv.assert(jsv.forall(jsv.nat, function (s) { 60 | return R.equals(State.get.eval(s), s); 61 | })); 62 | }); 63 | 64 | it('retrieves a transformed state via State.gets', function () { 65 | jsv.assert(jsv.forall(jsv.nat, jsv.fn(jsv.nat), function (s, fn) { 66 | return R.equals(State.gets(fn).eval(s), fn(s)); 67 | })); 68 | }); 69 | 70 | it('stores the given state via State.put', function () { 71 | jsv.assert(jsv.forall(jsv.nat, jsv.nat, function (s1, s2) { 72 | return R.equals(State.put(s1).exec(s2), s1); 73 | })); 74 | }); 75 | 76 | it('modifies the stored state via State.modify', function () { 77 | jsv.assert(jsv.forall(jsv.nat, jsv.fn(jsv.nat), function (s, fn) { 78 | return State.modify(fn).exec(s) === fn(s); 79 | })); 80 | }); 81 | 82 | it('is a Functor', function () { 83 | var fTest = types.functor; 84 | jsv.assert(jsv.forall(stateNatArb, fTest.iface)); 85 | jsv.assert(jsv.forall(stateNatArb, fTest.id)); 86 | jsv.assert(jsv.forall(stateNatArb, fnNatArb, fnNatArb, fTest.compose)); 87 | }); 88 | 89 | it('is an Apply', function () { 90 | var aTest = types.apply; 91 | jsv.assert(jsv.forall(stateFnNatArb, stateFnNatArb, stateNatArb, aTest.compose)); 92 | jsv.assert(jsv.forall(stateNatArb, aTest.iface)); 93 | }); 94 | 95 | it('is an Applicative', function () { 96 | var aTest = types.applicative; 97 | jsv.assert(jsv.forall(stateNatArb, aTest.iface)); 98 | jsv.assert(jsv.forall(stateNatArb, stateNatArb, aTest.id)); 99 | jsv.assert(jsv.forall(stateNatArb, fnNatArb, jsv.nat, aTest.homomorphic)); 100 | jsv.assert(jsv.forall(stateNatArb, stateFnNatArb, jsv.nat, aTest.interchange)); 101 | }); 102 | 103 | it('is a Chain', function () { 104 | var cTest = types.chain; 105 | var fnEArb = jsv.fn(stateNatArb); 106 | jsv.assert(jsv.forall(stateNatArb, cTest.iface)); 107 | jsv.assert(jsv.forall(stateNatArb, fnEArb, fnEArb, cTest.associative)); 108 | }); 109 | 110 | it('is a Monad', function () { 111 | jsv.assert(jsv.forall(stateNatArb, types.monad.iface)); 112 | }); 113 | }); 114 | -------------------------------------------------------------------------------- /test/tuple.test.js: -------------------------------------------------------------------------------- 1 | var R = require('ramda'); 2 | var assert = require('assert'); 3 | var equalsInvoker = require('./utils').equalsInvoker; 4 | var types = require('./types')(equalsInvoker); 5 | var jsv = require('jsverify'); 6 | 7 | var Tuple = require('..').Tuple; 8 | var constructor = Tuple('', '').constructor; 9 | 10 | var TupleGen = R.curry(function(a, b, n) { 11 | return Tuple(a.generator(n), b.generator(n)); 12 | }); 13 | 14 | var TupleShow = R.curry(function(a, m) { 15 | return 'Tuple(' + a.show(m[0]) + ', ' + a.show(m[1]) + ')'; 16 | }); 17 | 18 | var TupleShrink = R.curry(function(a, m) { 19 | return [Tuple(a.shrink(m[0]), a.shrink(m[1]))]; 20 | }); 21 | 22 | var TupleArb = function(a, b) { 23 | return { 24 | generator: jsv.generator.bless(TupleGen(a, b)), 25 | show: TupleShow(a), 26 | shrink: jsv.shrink.bless(TupleShrink(a)) 27 | }; 28 | }; 29 | 30 | var stringArb = jsv.generator.bless({ 31 | generator: function() { 32 | switch (jsv.random(0, 2)) { 33 | case 0: return 'foo'; 34 | case 1: return 'bar'; 35 | case 2: return 'quux'; 36 | } 37 | }, 38 | show: function(a) { return a; }, 39 | shrink: jsv.shrink.bless(function(m) { return [m.slice(1)]; }) 40 | }); 41 | 42 | function mult(a) { 43 | return function(b) { return a * b; }; 44 | } 45 | 46 | function add(a) { 47 | return function(b) { return a + b; }; 48 | } 49 | 50 | 51 | describe('Tuple', function() { 52 | var m = TupleArb(stringArb, jsv.nat); 53 | 54 | it('has an arbitrary', function() { 55 | var arb = jsv.forall(m, function(m) { 56 | return m instanceof constructor; 57 | }); 58 | jsv.assert(arb); 59 | }); 60 | 61 | it('is a Semigroup', function() { 62 | var t = TupleArb(stringArb, stringArb); 63 | var t1 = TupleArb(stringArb, stringArb); 64 | var t2 = TupleArb(stringArb, stringArb); 65 | var sTest = types.semigroup; 66 | 67 | jsv.assert(jsv.forall(t, sTest.iface)); 68 | jsv.assert(jsv.forall(t, t1, t2, sTest.associative)); 69 | }); 70 | 71 | it('is a Functor', function() { 72 | var fTest = types.functor; 73 | 74 | jsv.assert(jsv.forall(m, fTest.iface)); 75 | jsv.assert(jsv.forall(m, fTest.id)); 76 | jsv.assert(jsv.forall(m, 'nat -> nat', 'nat -> nat', fTest.compose)); 77 | }); 78 | 79 | it('is an Apply', function() { 80 | var aTest = types.apply; 81 | var appA = Tuple('', mult(10)); 82 | var appU = Tuple('', add(7)); 83 | var appV = Tuple('', 10); 84 | 85 | jsv.assert(jsv.forall(m, aTest.iface)); 86 | assert.equal(true, aTest.compose(appA, appU, appV)); 87 | }); 88 | }); 89 | 90 | describe('Tuple usage', function() { 91 | 92 | describe('creation', function() { 93 | it('should be curried', function() { 94 | var tpl = Tuple('dr')(true); 95 | assert.equal('dr', tpl[0]); 96 | assert.equal(true, tpl[1]); 97 | }); 98 | }); 99 | 100 | describe('element access', function() { 101 | var tuple = Tuple('nacho', 'cheese'); 102 | 103 | it('should work with indexes', function() { 104 | assert.equal('nacho', tuple[0]); 105 | assert.equal('cheese', tuple[1]); 106 | }); 107 | 108 | it('should return the value in the first position', function() { 109 | assert.equal('nacho', Tuple.fst(tuple)); 110 | assert.equal('cheese', Tuple.snd(tuple)); 111 | }); 112 | 113 | it('should work with head', function() { 114 | assert.equal('nacho', R.head(tuple)); 115 | }); 116 | 117 | it('should work with nth', function() { 118 | assert.equal('cheese', R.nth(1, tuple)); 119 | }); 120 | 121 | it('should work with tail', function() { 122 | assert.equal('cheese', R.tail(tuple)); 123 | }); 124 | 125 | it('should work with take', function() { 126 | assert.equal('nacho', R.take(1, tuple)[0]); 127 | } 128 | ); 129 | it('should work with drop', function() { 130 | assert.equal('cheese', R.drop(1, tuple)[0]); 131 | }); 132 | 133 | it('will tell us the length', function() { 134 | assert.equal(2, tuple.length); 135 | }); 136 | }); 137 | 138 | describe('interface sanity check', function() { 139 | var tuple = Tuple('mixed', 'nuts'); 140 | 141 | it('only maps the snd', function() { 142 | var t = tuple.map(add('coco')); 143 | assert.equal('mixed', t[0]); 144 | assert.equal('coconuts', t[1]); 145 | }); 146 | 147 | it('will combine two tuples', function() { 148 | var t = tuple.concat(Tuple(' chocolate', ' bars')); 149 | assert.equal('mixed chocolate', t[0]); 150 | assert.equal('nuts bars', t[1]); 151 | }); 152 | 153 | it('will apply and concat', function() { 154 | var t = Tuple('Re', 'dough').map(add).ap(tuple); 155 | assert.equal('Remixed', t[0]); 156 | assert.equal('doughnuts', t[1]); 157 | }); 158 | }); 159 | 160 | describe('#@@type', function() { 161 | 162 | it('is "ramda-fantasy/Tuple"', function() { 163 | assert.strictEqual(Tuple(null, null)['@@type'], 'ramda-fantasy/Tuple'); 164 | }); 165 | 166 | }); 167 | 168 | describe('#toString', function() { 169 | 170 | it('returns the string representation of a Tuple', function() { 171 | assert.strictEqual(Tuple('abc', [1, 2, 3]).toString(), 172 | 'Tuple("abc", [1, 2, 3])'); 173 | assert.strictEqual(Tuple('abc', Tuple(1, 2)).toString(), 174 | 'Tuple("abc", Tuple(1, 2))'); 175 | }); 176 | 177 | }); 178 | 179 | }); 180 | -------------------------------------------------------------------------------- /test/types.js: -------------------------------------------------------------------------------- 1 | var interfaces = { 2 | semigroup: ['concat'], 3 | monoid: ['concat', 'empty'], 4 | functor: ['map'], 5 | apply: ['map', 'ap'], 6 | applicative: ['map', 'ap', 'of'], 7 | chain: ['map', 'ap', 'chain'], 8 | chainRec: ['map', 'ap', 'chain', 'chainRec'], 9 | monad: ['map', 'ap', 'chain', 'of'], 10 | extend: ['extend'], 11 | comonad: ['extend', 'extract'], 12 | foldable: ['reduce'], 13 | transformer: ['lift'] 14 | }; 15 | 16 | var Identity = require('..').Identity; 17 | 18 | function correctInterface(type) { 19 | return function(obj) { 20 | return interfaces[type].every(function(method) { 21 | return obj[method] && typeof obj[method] === 'function'; 22 | }); 23 | }; 24 | } 25 | 26 | function identity(x) { return x; } 27 | 28 | module.exports = function(eq) { 29 | return { 30 | semigroup: { 31 | iface: correctInterface('semigroup'), 32 | associative: function (a, b, c) { 33 | return eq(a.concat(b).concat(c), a.concat(b.concat(c))); 34 | } 35 | }, 36 | 37 | functor: { 38 | iface: correctInterface('functor'), 39 | id: function (obj) { 40 | return eq(obj, obj.map(identity)); 41 | }, 42 | compose: function (obj, f, g) { 43 | return eq( 44 | obj.map(function (x) { 45 | return f(g(x)); 46 | }), 47 | obj.map(g).map(f) 48 | ); 49 | } 50 | }, 51 | 52 | apply: { 53 | iface: correctInterface('apply'), 54 | compose: function (a, u, v) { 55 | return eq( 56 | a.ap(u.ap(v)), 57 | a.map(function (f) { 58 | return function (g) { 59 | return function (x) { 60 | return f(g(x)); 61 | }; 62 | }; 63 | }).ap(u).ap(v) 64 | ); 65 | } 66 | }, 67 | 68 | applicative: { 69 | iface: correctInterface('applicative'), 70 | id: function (obj, obj2) { 71 | return eq(obj.of(identity).ap(obj2), obj2); 72 | }, 73 | homomorphic: function (obj, f, x) { 74 | return eq(obj.of(f).ap(obj.of(x)), obj.of(f(x))); 75 | }, 76 | interchange: function (obj1, obj2, x) { 77 | return eq( 78 | obj2.ap(obj1.of(x)), 79 | obj1.of(function (f) { 80 | return f(x); 81 | }).ap(obj2) 82 | ); 83 | } 84 | }, 85 | 86 | chain: { 87 | iface: correctInterface('chain'), 88 | associative: function (obj, f, g) { 89 | return eq( 90 | obj.chain(f).chain(g), 91 | obj.chain(function (x) { 92 | return f(x).chain(g); 93 | }) 94 | ); 95 | } 96 | }, 97 | chainRec: { 98 | iface: correctInterface('chainRec'), 99 | equivalence: function (T, p, d, n, x) { 100 | return eq( 101 | T.chainRec(function(next, done, v) { 102 | return p(v) ? d(v).map(done) : n(v).map(next); 103 | }, x), 104 | (function step(v) { 105 | return p(v) ? d(v) : n(v).chain(step); 106 | }(x)) 107 | ); 108 | } 109 | }, 110 | 111 | monad: { 112 | iface: correctInterface('monad') 113 | }, 114 | 115 | extend: { 116 | iface: correctInterface('extend'), 117 | associative: function(obj, f, g) { 118 | return eq( 119 | obj.extend(g).extend(f), 120 | obj.extend(function(_obj) { 121 | return f(_obj.extend(g)); 122 | }) 123 | ); 124 | } 125 | }, 126 | 127 | comonad: { 128 | iface: correctInterface('comonad'), 129 | leftIdentity: function (obj) { 130 | return eq(obj.extend(function(_obj) { return _obj.extract(); }), obj); 131 | }, 132 | rightIdentity: function (obj, f) { 133 | return eq(obj.extend(f).extract(), f(obj)); 134 | } 135 | }, 136 | 137 | foldable: { 138 | iface: correctInterface('foldable') 139 | }, 140 | 141 | transformer: { 142 | iface: function (T) { 143 | return correctInterface('transformer')(T(Identity)) && 144 | correctInterface('monad')(T(Identity)(identity)); 145 | }, 146 | id: function (transformer) { 147 | var T = transformer(Identity); 148 | return eq(T.lift(Identity.of(1)), T.of(1)); 149 | }, 150 | associative: function (transformer) { 151 | var T = transformer(Identity); 152 | var m = Identity(1); 153 | var f = function (x) { 154 | return Identity(x * x); 155 | }; 156 | return eq( 157 | T.lift(m.chain(f)), 158 | T.lift(m).chain(function (x) { 159 | return T.lift(f(x)); 160 | }) 161 | ); 162 | } 163 | } 164 | }; 165 | }; 166 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | var R = require('ramda'); 2 | 3 | module.exports = { 4 | equalsInvoker: R.invoker(1, 'equals') 5 | }; 6 | --------------------------------------------------------------------------------