├── .gitignore ├── README.md ├── dist ├── pointfree.amd.js └── pointfree.browser.js ├── index.js ├── instances ├── array.js ├── const.js ├── function.js ├── identity.js ├── maybe.js ├── monoids.js ├── string.js └── sum.js ├── package.json ├── pointfree.js ├── test ├── array.js ├── const.js ├── function.js ├── helper.js ├── identity.js ├── maybe.js ├── monoids.js └── string.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | tmp 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pointfree-fantasy 2 | ================= 3 | 4 | Point-free wrappers for [fantasy-land](https://github.com/fantasyland/fantasy-land). Functions are curried using 5 | [lodash's curry function](http://lodash.com/docs#curry), and receive their 6 | data last. Gives us aliases with our familar haskell names as well. 7 | 8 | ```js 9 | require('pointfree-fantasy').expose(global); // or if browser pointfree.expose(window) 10 | var Maybe = require('pointfree-fantasy/instances/maybe'); 11 | 12 | // setup an easy test fn 13 | var toUpperCase = function(x) { return x.toUpperCase(); }; 14 | 15 | map(toUpperCase, Maybe('mystring')) // Just('MYSTRING') 16 | map(toUpperCase, Maybe(null)) // Nothing 17 | 18 | ``` 19 | 20 | THE LIST 21 | -------- 22 | 23 | * `I` combinator 24 | * `K` combinator 25 | * `compose`, taking arbitrarily many functions 26 | * `map`, aliased `fmap` 27 | * `ap` 28 | * `liftA2` 29 | * `liftA3` 30 | * `chain`, takes chainable first, function last 31 | * `flatMap`, flipped `chain` 32 | * `mjoin` 33 | * `concat`, aliased `mappend` 34 | * `mconcat`, a monoidal reduction. 35 | * `foldMap` 36 | * `fold` 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Tutorial 46 | ---------- 47 | pointfree-fantasy implements a point-free version of the fantasy-land spec, in order to promote a less cluttered, more Haskell-like approach to algebraic programming. We'll justify this approach using Functor as an example. 48 | 49 | In Haskell the Functor typeclass is defined (http://www.haskell.org/haskellwiki/Functor) as: 50 | 51 | ```haskell 52 | class Functor f where 53 | fmap :: (a -> b) -> f a -> f b 54 | ``` 55 | 56 | We can read this as: 57 | 58 | f is a Functor if it provides a function fmap 59 | that takes a function from type a to type b 60 | and returns a function from type f a to type f b. 61 | 62 | and the Functor laws are 63 | 64 | ```haskell 65 | fmap id = id 66 | fmap (p . q) = (fmap p) . (fmap q) 67 | ``` 68 | 69 | These are the requirements that fmap must satisfy: 70 | fmap of the identity function returns the identity function, and 71 | fmap distributes over function composition. 72 | 73 | (We're usually going to call it map rather than fmap.) 74 | 75 | We can think of a Functor as a container that implements a map function. So if we had a Functor named Container, we'd expect to be able to say 76 | 77 | ```js 78 | Container('hello').map(concat(' world')).map(length); 79 | ``` 80 | 81 | and wind up with `Container(11)`. As long as each function works on the *content* of the Container, mapping the function over the Container will return a Container with the transformed content. 82 | 83 | Notice that the type of the content will change if the mapped function returns a different type, as with `length` above. So a Functor has to be able to contain any type. 84 | 85 | Or as Julian Birch says: 86 | 87 | > So what the heck is a functor? Well, really it's just something you can map over and it makes sense. 88 | 89 | We haven't mentioned any specific Functors yet. It's the particular things that each different Functor can do that distinguish them from one another. Usually these particular things have to do with how you access the values in the container. The Functor laws, and the clean ways they help us to manipulate Functors, are what all Functors have in common. 90 | 91 | Take a look at Birch's little list of [things you can map over](http://web.archive.org/web/20150918214956/http://www.colourcoding.net/blog/archive/2014/06/27/not-a-haskell-monad-tutorial-functors.aspx). 92 | 93 | 94 | ------------------------------- 95 | 96 | 97 | Our most common Functor in JavaScript is Array. Of course Array is normally written as a pair of square brackets around the value(s) it's operating on, but we can imagine it as an explicit function, namely the Array constructor, instead. (Also imagine that the Array constructor does not have special-case behavior when passed a single argument!) 98 | 99 | Also note that we're considering Arrays whose contents are all of a single type. The VALUE of an Array is a sequence of values of that underlying type. MAPPING a function f (such as the function "+1") over an Array produces a new Array whose value is a sequence of underlying values, each value equal to f(x), or "+1" of the original. In code: 100 | 101 | `Array(2,3,4).map(function(n){ return n + 1; }) // Array(3,4,5)` 102 | 103 | The fmap function for Array, conveniently, is the similarly-named Array.prototype.map. 104 | 105 | Does it satisfy the Functor laws? 106 | 107 | 108 | Let's see how fantasy-land expresses the Functor laws (https://github.com/fantasyland/fantasy-land#functor): 109 | 110 | ```js 111 | u.map(function(a) { return a; }) // is equivalent to u (identity) 112 | u.map(function(x) { return f(g(x)); }) // is equivalent to u.map(g).map(f) (composition) 113 | ``` 114 | 115 | You can verify these pretty easily. 116 | 117 | But notice that the laws have gotten a bit more prolix. Of course, once we define 118 | 119 | ```js 120 | var id = function(a) { return a; }; 121 | var compose = function(f, g) { return function(x) { return f(g(x)); }}; 122 | ``` 123 | 124 | they become 125 | 126 | ```js 127 | u.map(id) == u 128 | u.map(compose(f, g)) == u.map(g).map(f) 129 | ``` 130 | 131 | This is neater, but we also want to avoid mentioning that u! We want to speak of standalone functions, not methods, and so we provide a map function that's NOT defined on Array.prototype. Here's why: 132 | 133 | If you have a function that works on a single value and you want to transform it to work on some functor, say Array, you can call map on your function like this: `map(f)` 134 | 135 | ```js 136 | funnyFortune = function(x){ 137 | return x + ' in bed'; 138 | }; 139 | ``` 140 | 141 | `var worksOnArrayFortune = map(funnyFortune)` 142 | 143 | This saves you from having to type 144 | 145 | ```js 146 | var worksOnArrayFortune = function(xs){ 147 | return xs.map(funnyFortune); 148 | }; 149 | ``` 150 | 151 | In fact you may not need to define `worksOnArrayFortune` at all, 152 | since you can simply say map(funnyFortune) inline when you need it. 153 | 154 | And this works for every Functor, not just Array! We'll be talking soon enough about other Functors you may already be using. 155 | 156 | With this and compose, the laws become 157 | 158 | ```js 159 | map(id) == id 160 | map(compose(f, g)) == compose(map(f), map(g)) 161 | ``` 162 | 163 | Composing functions without explicitly mentioning their arguments is what we call "point-free style." The "points" in question are the actual values. In topology, where the term originated, the values are always points! 164 | 165 | 166 | 167 | 168 | ............................... 169 | 170 | Now let's look at a more practical example. 171 | 172 | We're fetching rows from a database, formatting what we want from each one, and displaying them on the screen. 173 | Say we have three functions: 174 | 175 | getRows, which takes an Int and returns an Array of Rows: 176 | 177 | ```js 178 | //+ getRows :: Int -> [Row] 179 | getRows = function(i) { return db.getSomeRows(i); }; 180 | ``` 181 | 182 | renderRow, which takes a Row and returns a snippet of Html: 183 | 184 | ```js 185 | //+ renderRow :: Row -> Html 186 | renderRow = function(row) { 187 | return '
' + row.title + '
' 188 | }; 189 | ``` 190 | 191 | and drawOnScreen, which takes an Array of Html snippets and returns a Dom tree: 192 | 193 | ```js 194 | //+ drawOnScreen :: [Html] -> Dom 195 | // you supply the code 196 | ``` 197 | 198 | From these bite-size pieces, each responsible for doing its own little job, 199 | we use compose and map to build up our program: 200 | 201 | ```js 202 | //+ prog: Int -> Dom 203 | prog = compose(drawOnScreen, map(renderRow), getRows); 204 | ``` 205 | 206 | Notice that when we compose functions, the data flows from right to left: 207 | getRows takes in an Int and feeds an Array of Rows to the left, 208 | where we use map to convert renderRow into a function that takes in that Array of Rows and feeds an Array of Html snippets to the left, 209 | where drawOnScreen is ready to accept it and produce the Dom. 210 | Here's how we think about it in terms of the type signatures, showing the same data flow from left to right: 211 | 212 | 213 | ``` 214 | //+ getRows :: Int -> [Row] 215 | //+ map(renderRow) :: [Row] -> [Html] 216 | //+ drawOnScreen :: [Html] -> Dom 217 | //------------------------------------------------- 218 | //+ prog :: Int -> Dom 219 | ``` 220 | 221 | Haskell would be keeping track of the types for us and stopping us if we slip up, 222 | but in JavaScript it's up to us to manage them. That's why we always try to annotate our functions with these comments. 223 | 224 | 225 | ---------------------------- 226 | 227 | Now shit's about to get realer because we need to fetch the rows asynchronously from a remote database! For this we're going to use our next Functor: Future. The VALUE of a Future is the actions that will produce the underlying value. That underlying value might be an Array of Rows, or it might be the Dom. Once we have our Future, it provides a way to actually run those actions and resolve to the underlying value, but in the meantime we can compose and map until we're satisfied with how we've constructed the pure Future. MAPPING a function f over a Future that will resolve to a value x produces a new Future that will resolve to the value f(x). 228 | 229 | (Future is defined in folktale's data.future repository (#!https://github.com/folktale/data.future). 230 | fantasyland-pointfree incorporates functions that work the way we like 231 | from several external libraries, and Future is one.) 232 | 233 | We revise getRows to return a Future of an Array of Rows: 234 | 235 | ```js 236 | //+ getRows :: Int -> Future([Row]) 237 | getRows = function(i){ 238 | return new Future(function(reject, resolve) { 239 | resolve(i + ' rows from the database'); 240 | }); 241 | }; 242 | ``` 243 | 244 | and our program becomes: 245 | 246 | ``` 247 | //+ getRows :: Int -> Future([Row]) 248 | //+ map(map(renderRow)) :: Future([Row]) -> Future([Html]) 249 | //+ map(drawOnScreen) :: Future([Html]) -> Future(Dom) 250 | //------------------------------------------------------------------------------ 251 | //+ prog :: Int -> Future(Dom) 252 | ``` 253 | 254 | ```js 255 | //+ prog :: Int -> Future(Dom) 256 | prog = compose(map(drawOnScreen), map(map(renderRow)), getRows) 257 | ``` 258 | 259 | In a bit we'll cause the Future(Dom) to run its actions and resolve to that Dom object, but first let's unpack this a bit. We've mentioned that map(f) does the same thing conceptually over any Functor: transform the Functor into the corresponding Functor whose underlying value(s) are f(x). But when we say `map(map(renderRow))`, you might ask how HAL knows which map is which. (Besides from reading your lips.) Map is polymorphic, and if we look at its definition, we'll see how that is implemented: 260 | 261 | ```js 262 | map = _.curry(function(f, obj){ return obj.map(f); }) 263 | ``` 264 | 265 | Map simply delegates to the member function .map on whatever object it receives as its second argument. This allows us to partially apply it with the first argument f. The second argument, the one that determines which .map implementation will be called, is passed in from the right along the chain of composition. So the outer map of `map(map(renderRow))` will receive the Future from getRows, and the inner map will receive the Array of Rows inside the Future: 266 | 267 | ```js 268 | //+ map(map(renderRow), future_of_rows) :: Future([Row]) -> Future([Html]) 269 | //+ map(renderRow, rows) :: [Row] -> [Html] 270 | ``` 271 | 272 | (If you want to hear more about currying, see the talk [Hey Underscore, You're Doing It Wrong!](http://www.youtube.com/watch?v=m3svKOdZijA). 273 | 274 | 275 | Future has a method `fork` that runs its actions and resolves to its underlying value, so we can invoke our prog like this: 276 | 277 | ```js 278 | prog(2).fork(function(err){}, function(result){}) // see folktale 279 | ``` 280 | 281 | and the page displaying two rows will be drawn when the Future's underlying value is realized. 282 | 283 | 284 | 285 | 286 | Now let's see how the laws help us do a bit of refactoring. 287 | 288 | 289 | - we remember compose is associative so we can simplify our prog with a helper 290 | (This comes not from the Functor laws but from the fact that functions form a category; see below): 291 | 292 | 293 | ```js 294 | //+ makePage :: Future([Row]) -> Future(Dom) 295 | makePage = compose(map(drawOnScreen), map(map(renderRow))); 296 | 297 | prog = compose(makePage, getRows); 298 | ``` 299 | 300 | - we remember our law: `compose(map(f), map(g)) == map(compose(f, g))`, so we factor out a map just like we do in oh, I don't know, ALGEBRA?: 301 | 302 | 303 | ```js 304 | //+ makePage :: Future([Row]) -> Future(Dom) 305 | makePage = map(compose(drawOnScreen, map(renderRow))); 306 | 307 | prog = compose(makePage, getRows); 308 | ``` 309 | 310 | - finally we notice we'd rather have makePage work on simpler types than Futures: 311 | 312 | 313 | ```js 314 | //+ makePage :: [Row] -> Dom 315 | makePage = compose(drawOnScreen, map(renderRow)); 316 | 317 | prog = compose(map(makePage), getRows); 318 | ``` 319 | 320 | 321 | 322 | You will have noticed that as we manipulate functions with map and compose, we are exactly paralleling the way we manipulate variables with addition and multiplication; hence the term *algebra*. 323 | 324 | ```js 325 | (x + y) + z == x + (y + z) == add(x, y, z) 326 | compose(compose(f, g), h) == compose(f, compose(g, h)) == compose(f, g, h) 327 | ``` 328 | 329 | ```js 330 | add(mul(2, 4), mul(2,3)) == mul(add(4, 3), 2) 331 | compose(map(f), map(g)) == map(compose(f, g)) 332 | ``` 333 | 334 | 335 | -------------------------------------------------------------------------------- /dist/pointfree.amd.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 167 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 168 | * Copyright 2012-2013 The Dojo Foundation 169 | * Based on Underscore.js 1.5.2 170 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 171 | * Available under MIT license 172 | */ 173 | var createWrapper = require('lodash._createwrapper'); 174 | 175 | /** 176 | * Creates a function which accepts one or more arguments of `func` that when 177 | * invoked either executes `func` returning its result, if all `func` arguments 178 | * have been provided, or returns a function that accepts one or more of the 179 | * remaining `func` arguments, and so on. The arity of `func` can be specified 180 | * if `func.length` is not sufficient. 181 | * 182 | * @static 183 | * @memberOf _ 184 | * @category Functions 185 | * @param {Function} func The function to curry. 186 | * @param {number} [arity=func.length] The arity of `func`. 187 | * @returns {Function} Returns the new curried function. 188 | * @example 189 | * 190 | * var curried = _.curry(function(a, b, c) { 191 | * console.log(a + b + c); 192 | * }); 193 | * 194 | * curried(1)(2)(3); 195 | * // => 6 196 | * 197 | * curried(1, 2)(3); 198 | * // => 6 199 | * 200 | * curried(1, 2, 3); 201 | * // => 6 202 | */ 203 | function curry(func, arity) { 204 | arity = typeof arity == 'number' ? arity : (+arity || func.length); 205 | return createWrapper(func, 4, null, null, null, arity); 206 | } 207 | 208 | module.exports = curry; 209 | 210 | },{"lodash._createwrapper":5}],5:[function(require,module,exports){ 211 | /** 212 | * Lo-Dash 2.4.1 (Custom Build) 213 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 214 | * Copyright 2012-2013 The Dojo Foundation 215 | * Based on Underscore.js 1.5.2 216 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 217 | * Available under MIT license 218 | */ 219 | var baseBind = require('lodash._basebind'), 220 | baseCreateWrapper = require('lodash._basecreatewrapper'), 221 | isFunction = require('lodash.isfunction'), 222 | slice = require('lodash._slice'); 223 | 224 | /** 225 | * Used for `Array` method references. 226 | * 227 | * Normally `Array.prototype` would suffice, however, using an array literal 228 | * avoids issues in Narwhal. 229 | */ 230 | var arrayRef = []; 231 | 232 | /** Native method shortcuts */ 233 | var push = arrayRef.push, 234 | unshift = arrayRef.unshift; 235 | 236 | /** 237 | * Creates a function that, when called, either curries or invokes `func` 238 | * with an optional `this` binding and partially applied arguments. 239 | * 240 | * @private 241 | * @param {Function|string} func The function or method name to reference. 242 | * @param {number} bitmask The bitmask of method flags to compose. 243 | * The bitmask may be composed of the following flags: 244 | * 1 - `_.bind` 245 | * 2 - `_.bindKey` 246 | * 4 - `_.curry` 247 | * 8 - `_.curry` (bound) 248 | * 16 - `_.partial` 249 | * 32 - `_.partialRight` 250 | * @param {Array} [partialArgs] An array of arguments to prepend to those 251 | * provided to the new function. 252 | * @param {Array} [partialRightArgs] An array of arguments to append to those 253 | * provided to the new function. 254 | * @param {*} [thisArg] The `this` binding of `func`. 255 | * @param {number} [arity] The arity of `func`. 256 | * @returns {Function} Returns the new function. 257 | */ 258 | function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) { 259 | var isBind = bitmask & 1, 260 | isBindKey = bitmask & 2, 261 | isCurry = bitmask & 4, 262 | isCurryBound = bitmask & 8, 263 | isPartial = bitmask & 16, 264 | isPartialRight = bitmask & 32; 265 | 266 | if (!isBindKey && !isFunction(func)) { 267 | throw new TypeError; 268 | } 269 | if (isPartial && !partialArgs.length) { 270 | bitmask &= ~16; 271 | isPartial = partialArgs = false; 272 | } 273 | if (isPartialRight && !partialRightArgs.length) { 274 | bitmask &= ~32; 275 | isPartialRight = partialRightArgs = false; 276 | } 277 | var bindData = func && func.__bindData__; 278 | if (bindData && bindData !== true) { 279 | // clone `bindData` 280 | bindData = slice(bindData); 281 | if (bindData[2]) { 282 | bindData[2] = slice(bindData[2]); 283 | } 284 | if (bindData[3]) { 285 | bindData[3] = slice(bindData[3]); 286 | } 287 | // set `thisBinding` is not previously bound 288 | if (isBind && !(bindData[1] & 1)) { 289 | bindData[4] = thisArg; 290 | } 291 | // set if previously bound but not currently (subsequent curried functions) 292 | if (!isBind && bindData[1] & 1) { 293 | bitmask |= 8; 294 | } 295 | // set curried arity if not yet set 296 | if (isCurry && !(bindData[1] & 4)) { 297 | bindData[5] = arity; 298 | } 299 | // append partial left arguments 300 | if (isPartial) { 301 | push.apply(bindData[2] || (bindData[2] = []), partialArgs); 302 | } 303 | // append partial right arguments 304 | if (isPartialRight) { 305 | unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs); 306 | } 307 | // merge flags 308 | bindData[1] |= bitmask; 309 | return createWrapper.apply(null, bindData); 310 | } 311 | // fast path for `_.bind` 312 | var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper; 313 | return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]); 314 | } 315 | 316 | module.exports = createWrapper; 317 | 318 | },{"lodash._basebind":6,"lodash._basecreatewrapper":15,"lodash._slice":24,"lodash.isfunction":25}],6:[function(require,module,exports){ 319 | /** 320 | * Lo-Dash 2.4.1 (Custom Build) 321 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 322 | * Copyright 2012-2013 The Dojo Foundation 323 | * Based on Underscore.js 1.5.2 324 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 325 | * Available under MIT license 326 | */ 327 | var baseCreate = require('lodash._basecreate'), 328 | isObject = require('lodash.isobject'), 329 | setBindData = require('lodash._setbinddata'), 330 | slice = require('lodash._slice'); 331 | 332 | /** 333 | * Used for `Array` method references. 334 | * 335 | * Normally `Array.prototype` would suffice, however, using an array literal 336 | * avoids issues in Narwhal. 337 | */ 338 | var arrayRef = []; 339 | 340 | /** Native method shortcuts */ 341 | var push = arrayRef.push; 342 | 343 | /** 344 | * The base implementation of `_.bind` that creates the bound function and 345 | * sets its meta data. 346 | * 347 | * @private 348 | * @param {Array} bindData The bind data array. 349 | * @returns {Function} Returns the new bound function. 350 | */ 351 | function baseBind(bindData) { 352 | var func = bindData[0], 353 | partialArgs = bindData[2], 354 | thisArg = bindData[4]; 355 | 356 | function bound() { 357 | // `Function#bind` spec 358 | // http://es5.github.io/#x15.3.4.5 359 | if (partialArgs) { 360 | // avoid `arguments` object deoptimizations by using `slice` instead 361 | // of `Array.prototype.slice.call` and not assigning `arguments` to a 362 | // variable as a ternary expression 363 | var args = slice(partialArgs); 364 | push.apply(args, arguments); 365 | } 366 | // mimic the constructor's `return` behavior 367 | // http://es5.github.io/#x13.2.2 368 | if (this instanceof bound) { 369 | // ensure `new bound` is an instance of `func` 370 | var thisBinding = baseCreate(func.prototype), 371 | result = func.apply(thisBinding, args || arguments); 372 | return isObject(result) ? result : thisBinding; 373 | } 374 | return func.apply(thisArg, args || arguments); 375 | } 376 | setBindData(bound, bindData); 377 | return bound; 378 | } 379 | 380 | module.exports = baseBind; 381 | 382 | },{"lodash._basecreate":7,"lodash._setbinddata":10,"lodash._slice":24,"lodash.isobject":13}],7:[function(require,module,exports){ 383 | var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};/** 384 | * Lo-Dash 2.4.1 (Custom Build) 385 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 386 | * Copyright 2012-2013 The Dojo Foundation 387 | * Based on Underscore.js 1.5.2 388 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 389 | * Available under MIT license 390 | */ 391 | var isNative = require('lodash._isnative'), 392 | isObject = require('lodash.isobject'), 393 | noop = require('lodash.noop'); 394 | 395 | /* Native method shortcuts for methods with the same name as other `lodash` methods */ 396 | var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate; 397 | 398 | /** 399 | * The base implementation of `_.create` without support for assigning 400 | * properties to the created object. 401 | * 402 | * @private 403 | * @param {Object} prototype The object to inherit from. 404 | * @returns {Object} Returns the new object. 405 | */ 406 | function baseCreate(prototype, properties) { 407 | return isObject(prototype) ? nativeCreate(prototype) : {}; 408 | } 409 | // fallback for browsers without `Object.create` 410 | if (!nativeCreate) { 411 | baseCreate = (function() { 412 | function Object() {} 413 | return function(prototype) { 414 | if (isObject(prototype)) { 415 | Object.prototype = prototype; 416 | var result = new Object; 417 | Object.prototype = null; 418 | } 419 | return result || global.Object(); 420 | }; 421 | }()); 422 | } 423 | 424 | module.exports = baseCreate; 425 | 426 | },{"lodash._isnative":8,"lodash.isobject":13,"lodash.noop":9}],8:[function(require,module,exports){ 427 | /** 428 | * Lo-Dash 2.4.1 (Custom Build) 429 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 430 | * Copyright 2012-2013 The Dojo Foundation 431 | * Based on Underscore.js 1.5.2 432 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 433 | * Available under MIT license 434 | */ 435 | 436 | /** Used for native method references */ 437 | var objectProto = Object.prototype; 438 | 439 | /** Used to resolve the internal [[Class]] of values */ 440 | var toString = objectProto.toString; 441 | 442 | /** Used to detect if a method is native */ 443 | var reNative = RegExp('^' + 444 | String(toString) 445 | .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') 446 | .replace(/toString| for [^\]]+/g, '.*?') + '$' 447 | ); 448 | 449 | /** 450 | * Checks if `value` is a native function. 451 | * 452 | * @private 453 | * @param {*} value The value to check. 454 | * @returns {boolean} Returns `true` if the `value` is a native function, else `false`. 455 | */ 456 | function isNative(value) { 457 | return typeof value == 'function' && reNative.test(value); 458 | } 459 | 460 | module.exports = isNative; 461 | 462 | },{}],9:[function(require,module,exports){ 463 | /** 464 | * Lo-Dash 2.4.1 (Custom Build) 465 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 466 | * Copyright 2012-2013 The Dojo Foundation 467 | * Based on Underscore.js 1.5.2 468 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 469 | * Available under MIT license 470 | */ 471 | 472 | /** 473 | * A no-operation function. 474 | * 475 | * @static 476 | * @memberOf _ 477 | * @category Utilities 478 | * @example 479 | * 480 | * var object = { 'name': 'fred' }; 481 | * _.noop(object) === undefined; 482 | * // => true 483 | */ 484 | function noop() { 485 | // no operation performed 486 | } 487 | 488 | module.exports = noop; 489 | 490 | },{}],10:[function(require,module,exports){ 491 | /** 492 | * Lo-Dash 2.4.1 (Custom Build) 493 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 494 | * Copyright 2012-2013 The Dojo Foundation 495 | * Based on Underscore.js 1.5.2 496 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 497 | * Available under MIT license 498 | */ 499 | var isNative = require('lodash._isnative'), 500 | noop = require('lodash.noop'); 501 | 502 | /** Used as the property descriptor for `__bindData__` */ 503 | var descriptor = { 504 | 'configurable': false, 505 | 'enumerable': false, 506 | 'value': null, 507 | 'writable': false 508 | }; 509 | 510 | /** Used to set meta data on functions */ 511 | var defineProperty = (function() { 512 | // IE 8 only accepts DOM elements 513 | try { 514 | var o = {}, 515 | func = isNative(func = Object.defineProperty) && func, 516 | result = func(o, o, o) && func; 517 | } catch(e) { } 518 | return result; 519 | }()); 520 | 521 | /** 522 | * Sets `this` binding data on a given function. 523 | * 524 | * @private 525 | * @param {Function} func The function to set data on. 526 | * @param {Array} value The data array to set. 527 | */ 528 | var setBindData = !defineProperty ? noop : function(func, value) { 529 | descriptor.value = value; 530 | defineProperty(func, '__bindData__', descriptor); 531 | }; 532 | 533 | module.exports = setBindData; 534 | 535 | },{"lodash._isnative":11,"lodash.noop":12}],11:[function(require,module,exports){ 536 | module.exports=require(8) 537 | },{}],12:[function(require,module,exports){ 538 | module.exports=require(9) 539 | },{}],13:[function(require,module,exports){ 540 | /** 541 | * Lo-Dash 2.4.1 (Custom Build) 542 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 543 | * Copyright 2012-2013 The Dojo Foundation 544 | * Based on Underscore.js 1.5.2 545 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 546 | * Available under MIT license 547 | */ 548 | var objectTypes = require('lodash._objecttypes'); 549 | 550 | /** 551 | * Checks if `value` is the language type of Object. 552 | * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) 553 | * 554 | * @static 555 | * @memberOf _ 556 | * @category Objects 557 | * @param {*} value The value to check. 558 | * @returns {boolean} Returns `true` if the `value` is an object, else `false`. 559 | * @example 560 | * 561 | * _.isObject({}); 562 | * // => true 563 | * 564 | * _.isObject([1, 2, 3]); 565 | * // => true 566 | * 567 | * _.isObject(1); 568 | * // => false 569 | */ 570 | function isObject(value) { 571 | // check if the value is the ECMAScript language type of Object 572 | // http://es5.github.io/#x8 573 | // and avoid a V8 bug 574 | // http://code.google.com/p/v8/issues/detail?id=2291 575 | return !!(value && objectTypes[typeof value]); 576 | } 577 | 578 | module.exports = isObject; 579 | 580 | },{"lodash._objecttypes":14}],14:[function(require,module,exports){ 581 | /** 582 | * Lo-Dash 2.4.1 (Custom Build) 583 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 584 | * Copyright 2012-2013 The Dojo Foundation 585 | * Based on Underscore.js 1.5.2 586 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 587 | * Available under MIT license 588 | */ 589 | 590 | /** Used to determine if values are of the language type Object */ 591 | var objectTypes = { 592 | 'boolean': false, 593 | 'function': true, 594 | 'object': true, 595 | 'number': false, 596 | 'string': false, 597 | 'undefined': false 598 | }; 599 | 600 | module.exports = objectTypes; 601 | 602 | },{}],15:[function(require,module,exports){ 603 | /** 604 | * Lo-Dash 2.4.1 (Custom Build) 605 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 606 | * Copyright 2012-2013 The Dojo Foundation 607 | * Based on Underscore.js 1.5.2 608 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 609 | * Available under MIT license 610 | */ 611 | var baseCreate = require('lodash._basecreate'), 612 | isObject = require('lodash.isobject'), 613 | setBindData = require('lodash._setbinddata'), 614 | slice = require('lodash._slice'); 615 | 616 | /** 617 | * Used for `Array` method references. 618 | * 619 | * Normally `Array.prototype` would suffice, however, using an array literal 620 | * avoids issues in Narwhal. 621 | */ 622 | var arrayRef = []; 623 | 624 | /** Native method shortcuts */ 625 | var push = arrayRef.push; 626 | 627 | /** 628 | * The base implementation of `createWrapper` that creates the wrapper and 629 | * sets its meta data. 630 | * 631 | * @private 632 | * @param {Array} bindData The bind data array. 633 | * @returns {Function} Returns the new function. 634 | */ 635 | function baseCreateWrapper(bindData) { 636 | var func = bindData[0], 637 | bitmask = bindData[1], 638 | partialArgs = bindData[2], 639 | partialRightArgs = bindData[3], 640 | thisArg = bindData[4], 641 | arity = bindData[5]; 642 | 643 | var isBind = bitmask & 1, 644 | isBindKey = bitmask & 2, 645 | isCurry = bitmask & 4, 646 | isCurryBound = bitmask & 8, 647 | key = func; 648 | 649 | function bound() { 650 | var thisBinding = isBind ? thisArg : this; 651 | if (partialArgs) { 652 | var args = slice(partialArgs); 653 | push.apply(args, arguments); 654 | } 655 | if (partialRightArgs || isCurry) { 656 | args || (args = slice(arguments)); 657 | if (partialRightArgs) { 658 | push.apply(args, partialRightArgs); 659 | } 660 | if (isCurry && args.length < arity) { 661 | bitmask |= 16 & ~32; 662 | return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]); 663 | } 664 | } 665 | args || (args = arguments); 666 | if (isBindKey) { 667 | func = thisBinding[key]; 668 | } 669 | if (this instanceof bound) { 670 | thisBinding = baseCreate(func.prototype); 671 | var result = func.apply(thisBinding, args); 672 | return isObject(result) ? result : thisBinding; 673 | } 674 | return func.apply(thisBinding, args); 675 | } 676 | setBindData(bound, bindData); 677 | return bound; 678 | } 679 | 680 | module.exports = baseCreateWrapper; 681 | 682 | },{"lodash._basecreate":16,"lodash._setbinddata":19,"lodash._slice":24,"lodash.isobject":22}],16:[function(require,module,exports){ 683 | arguments[4][7][0].apply(exports,arguments) 684 | },{"lodash._isnative":17,"lodash.isobject":22,"lodash.noop":18}],17:[function(require,module,exports){ 685 | module.exports=require(8) 686 | },{}],18:[function(require,module,exports){ 687 | module.exports=require(9) 688 | },{}],19:[function(require,module,exports){ 689 | module.exports=require(10) 690 | },{"lodash._isnative":20,"lodash.noop":21}],20:[function(require,module,exports){ 691 | module.exports=require(8) 692 | },{}],21:[function(require,module,exports){ 693 | module.exports=require(9) 694 | },{}],22:[function(require,module,exports){ 695 | module.exports=require(13) 696 | },{"lodash._objecttypes":23}],23:[function(require,module,exports){ 697 | module.exports=require(14) 698 | },{}],24:[function(require,module,exports){ 699 | /** 700 | * Lo-Dash 2.4.1 (Custom Build) 701 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 702 | * Copyright 2012-2013 The Dojo Foundation 703 | * Based on Underscore.js 1.5.2 704 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 705 | * Available under MIT license 706 | */ 707 | 708 | /** 709 | * Slices the `collection` from the `start` index up to, but not including, 710 | * the `end` index. 711 | * 712 | * Note: This function is used instead of `Array#slice` to support node lists 713 | * in IE < 9 and to ensure dense arrays are returned. 714 | * 715 | * @private 716 | * @param {Array|Object|string} collection The collection to slice. 717 | * @param {number} start The start index. 718 | * @param {number} end The end index. 719 | * @returns {Array} Returns the new array. 720 | */ 721 | function slice(array, start, end) { 722 | start || (start = 0); 723 | if (typeof end == 'undefined') { 724 | end = array ? array.length : 0; 725 | } 726 | var index = -1, 727 | length = end - start || 0, 728 | result = Array(length < 0 ? 0 : length); 729 | 730 | while (++index < length) { 731 | result[index] = array[start + index]; 732 | } 733 | return result; 734 | } 735 | 736 | module.exports = slice; 737 | 738 | },{}],25:[function(require,module,exports){ 739 | /** 740 | * Lo-Dash 2.4.1 (Custom Build) 741 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 742 | * Copyright 2012-2013 The Dojo Foundation 743 | * Based on Underscore.js 1.5.2 744 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 745 | * Available under MIT license 746 | */ 747 | 748 | /** 749 | * Checks if `value` is a function. 750 | * 751 | * @static 752 | * @memberOf _ 753 | * @category Objects 754 | * @param {*} value The value to check. 755 | * @returns {boolean} Returns `true` if the `value` is a function, else `false`. 756 | * @example 757 | * 758 | * _.isFunction(_); 759 | * // => true 760 | */ 761 | function isFunction(value) { 762 | return typeof value == 'function'; 763 | } 764 | 765 | module.exports = isFunction; 766 | 767 | },{}],26:[function(require,module,exports){ 768 | var curry = require('lodash.curry'); 769 | 770 | var BUILT_INS = { 'array': require('./instances/array') 771 | , 'function': require('./instances/function') 772 | , 'string': require('./instances/string') 773 | } 774 | 775 | var _groupsOf = curry(function(n, xs) { 776 | if(!xs.length) return []; 777 | return [xs.slice(0, n)].concat(_groupsOf(n, xs.slice(n, xs.length))); 778 | }); 779 | 780 | var _compose = curry(function(f,g,x) { return f(g(x)) }); 781 | 782 | var I = function(x){ return x; } 783 | 784 | // f . g . h == compose(f, g, h) 785 | var toAssociativeCommaInfix = function(fn) { 786 | return function() { 787 | var fns = [].slice.call(arguments) 788 | return function() { 789 | return _groupsOf(2, fns).reverse().map(function(g) { 790 | return (g.length > 1) ? fn.apply(this,g) : g[0]; 791 | }).reduce(function(x, f) { 792 | return [f.apply(f,x)]; 793 | }, arguments)[0]; 794 | }; 795 | }; 796 | }; 797 | 798 | var compose = toAssociativeCommaInfix(_compose); 799 | 800 | 801 | var pointy = {}; 802 | 803 | var id = function(x) { return x; } 804 | var K = function(x) { return function(){ return x; } } 805 | 806 | var map = curry(function(f, u) { 807 | return u.fmap ? u.fmap(f) : u.map(f); //sometimes map passes index so we use fmap if it has it. 808 | }); 809 | 810 | var ap = curry(function(a1, a2) { 811 | return a1.ap(a2); 812 | }); 813 | 814 | var liftA2 = curry(function(f, x, y) { 815 | return map(f,x).ap(y); 816 | }); 817 | 818 | var liftA3 = curry(function(f, x, y, z) { 819 | return map(f, x).ap(y).ap(z); 820 | }); 821 | 822 | var chain = curry(function(f, mv) { 823 | return mv.chain(f); 824 | }); 825 | 826 | var mjoin = function(mmv) { 827 | return chain(id, mmv); 828 | }; 829 | 830 | var concat = curry(function(x, y) { 831 | return x.concat(y); 832 | }); 833 | 834 | var mconcat = function(xs, empty) { 835 | return xs.length ? xs.reduce(concat) : empty(); 836 | }; 837 | 838 | var sequenceA = curry(function(point, fctr) { 839 | return fctr.traverse(id, point); 840 | }); 841 | 842 | var of = function(x) { 843 | return x.of; 844 | }; 845 | 846 | var traverse = curry(function(f, point, fctr) { 847 | return compose(sequenceA(point), map(f))(fctr); 848 | }); 849 | 850 | var foldMap = curry(function(f, fldable) { 851 | return fldable.reduce(function(acc, x) { 852 | var r = f(x); 853 | acc = acc || r.empty(); 854 | return acc.concat(r); 855 | }, null); 856 | }); 857 | 858 | var fold = foldMap(I); 859 | 860 | var toList = function(x) { 861 | return x.reduce(function(acc, y) { 862 | return [y].concat(acc); 863 | }, []); 864 | }; 865 | 866 | var expose = function(env) { 867 | var f; 868 | for (f in pointy) { 869 | if (f !== 'expose' && pointy.hasOwnProperty(f)) { 870 | env[f] = pointy[f]; 871 | } 872 | } 873 | } 874 | 875 | pointy.I = id; 876 | pointy.K = K; 877 | pointy.compose = compose; 878 | pointy.curry = curry; //lodash curry 879 | pointy.of = of; 880 | pointy.map = map; 881 | pointy.ap = ap; 882 | pointy.liftA2 = liftA2; 883 | pointy.liftA3 = liftA3; 884 | pointy.chain = chain; 885 | pointy.mjoin = mjoin; 886 | pointy.concat = concat; 887 | pointy.mappend = concat; 888 | pointy.mconcat = mconcat; 889 | pointy.sequenceA = sequenceA; 890 | pointy.traverse = traverse; 891 | pointy.foldMap = foldMap; 892 | pointy.fold = fold; 893 | pointy.toList = toList; 894 | pointy.expose = expose; 895 | 896 | module.exports = pointy; 897 | 898 | if(typeof window == "object") { 899 | pointfree = pointy; 900 | } 901 | 902 | },{"./instances/array":1,"./instances/function":2,"./instances/string":3,"lodash.curry":4}]},{},[26]) 903 | return pointfree 904 | }); -------------------------------------------------------------------------------- /dist/pointfree.browser.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 166 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 167 | * Copyright 2012-2013 The Dojo Foundation 168 | * Based on Underscore.js 1.5.2 169 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 170 | * Available under MIT license 171 | */ 172 | var createWrapper = require('lodash._createwrapper'); 173 | 174 | /** 175 | * Creates a function which accepts one or more arguments of `func` that when 176 | * invoked either executes `func` returning its result, if all `func` arguments 177 | * have been provided, or returns a function that accepts one or more of the 178 | * remaining `func` arguments, and so on. The arity of `func` can be specified 179 | * if `func.length` is not sufficient. 180 | * 181 | * @static 182 | * @memberOf _ 183 | * @category Functions 184 | * @param {Function} func The function to curry. 185 | * @param {number} [arity=func.length] The arity of `func`. 186 | * @returns {Function} Returns the new curried function. 187 | * @example 188 | * 189 | * var curried = _.curry(function(a, b, c) { 190 | * console.log(a + b + c); 191 | * }); 192 | * 193 | * curried(1)(2)(3); 194 | * // => 6 195 | * 196 | * curried(1, 2)(3); 197 | * // => 6 198 | * 199 | * curried(1, 2, 3); 200 | * // => 6 201 | */ 202 | function curry(func, arity) { 203 | arity = typeof arity == 'number' ? arity : (+arity || func.length); 204 | return createWrapper(func, 4, null, null, null, arity); 205 | } 206 | 207 | module.exports = curry; 208 | 209 | },{"lodash._createwrapper":5}],5:[function(require,module,exports){ 210 | /** 211 | * Lo-Dash 2.4.1 (Custom Build) 212 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 213 | * Copyright 2012-2013 The Dojo Foundation 214 | * Based on Underscore.js 1.5.2 215 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 216 | * Available under MIT license 217 | */ 218 | var baseBind = require('lodash._basebind'), 219 | baseCreateWrapper = require('lodash._basecreatewrapper'), 220 | isFunction = require('lodash.isfunction'), 221 | slice = require('lodash._slice'); 222 | 223 | /** 224 | * Used for `Array` method references. 225 | * 226 | * Normally `Array.prototype` would suffice, however, using an array literal 227 | * avoids issues in Narwhal. 228 | */ 229 | var arrayRef = []; 230 | 231 | /** Native method shortcuts */ 232 | var push = arrayRef.push, 233 | unshift = arrayRef.unshift; 234 | 235 | /** 236 | * Creates a function that, when called, either curries or invokes `func` 237 | * with an optional `this` binding and partially applied arguments. 238 | * 239 | * @private 240 | * @param {Function|string} func The function or method name to reference. 241 | * @param {number} bitmask The bitmask of method flags to compose. 242 | * The bitmask may be composed of the following flags: 243 | * 1 - `_.bind` 244 | * 2 - `_.bindKey` 245 | * 4 - `_.curry` 246 | * 8 - `_.curry` (bound) 247 | * 16 - `_.partial` 248 | * 32 - `_.partialRight` 249 | * @param {Array} [partialArgs] An array of arguments to prepend to those 250 | * provided to the new function. 251 | * @param {Array} [partialRightArgs] An array of arguments to append to those 252 | * provided to the new function. 253 | * @param {*} [thisArg] The `this` binding of `func`. 254 | * @param {number} [arity] The arity of `func`. 255 | * @returns {Function} Returns the new function. 256 | */ 257 | function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) { 258 | var isBind = bitmask & 1, 259 | isBindKey = bitmask & 2, 260 | isCurry = bitmask & 4, 261 | isCurryBound = bitmask & 8, 262 | isPartial = bitmask & 16, 263 | isPartialRight = bitmask & 32; 264 | 265 | if (!isBindKey && !isFunction(func)) { 266 | throw new TypeError; 267 | } 268 | if (isPartial && !partialArgs.length) { 269 | bitmask &= ~16; 270 | isPartial = partialArgs = false; 271 | } 272 | if (isPartialRight && !partialRightArgs.length) { 273 | bitmask &= ~32; 274 | isPartialRight = partialRightArgs = false; 275 | } 276 | var bindData = func && func.__bindData__; 277 | if (bindData && bindData !== true) { 278 | // clone `bindData` 279 | bindData = slice(bindData); 280 | if (bindData[2]) { 281 | bindData[2] = slice(bindData[2]); 282 | } 283 | if (bindData[3]) { 284 | bindData[3] = slice(bindData[3]); 285 | } 286 | // set `thisBinding` is not previously bound 287 | if (isBind && !(bindData[1] & 1)) { 288 | bindData[4] = thisArg; 289 | } 290 | // set if previously bound but not currently (subsequent curried functions) 291 | if (!isBind && bindData[1] & 1) { 292 | bitmask |= 8; 293 | } 294 | // set curried arity if not yet set 295 | if (isCurry && !(bindData[1] & 4)) { 296 | bindData[5] = arity; 297 | } 298 | // append partial left arguments 299 | if (isPartial) { 300 | push.apply(bindData[2] || (bindData[2] = []), partialArgs); 301 | } 302 | // append partial right arguments 303 | if (isPartialRight) { 304 | unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs); 305 | } 306 | // merge flags 307 | bindData[1] |= bitmask; 308 | return createWrapper.apply(null, bindData); 309 | } 310 | // fast path for `_.bind` 311 | var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper; 312 | return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]); 313 | } 314 | 315 | module.exports = createWrapper; 316 | 317 | },{"lodash._basebind":6,"lodash._basecreatewrapper":15,"lodash._slice":24,"lodash.isfunction":25}],6:[function(require,module,exports){ 318 | /** 319 | * Lo-Dash 2.4.1 (Custom Build) 320 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 321 | * Copyright 2012-2013 The Dojo Foundation 322 | * Based on Underscore.js 1.5.2 323 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 324 | * Available under MIT license 325 | */ 326 | var baseCreate = require('lodash._basecreate'), 327 | isObject = require('lodash.isobject'), 328 | setBindData = require('lodash._setbinddata'), 329 | slice = require('lodash._slice'); 330 | 331 | /** 332 | * Used for `Array` method references. 333 | * 334 | * Normally `Array.prototype` would suffice, however, using an array literal 335 | * avoids issues in Narwhal. 336 | */ 337 | var arrayRef = []; 338 | 339 | /** Native method shortcuts */ 340 | var push = arrayRef.push; 341 | 342 | /** 343 | * The base implementation of `_.bind` that creates the bound function and 344 | * sets its meta data. 345 | * 346 | * @private 347 | * @param {Array} bindData The bind data array. 348 | * @returns {Function} Returns the new bound function. 349 | */ 350 | function baseBind(bindData) { 351 | var func = bindData[0], 352 | partialArgs = bindData[2], 353 | thisArg = bindData[4]; 354 | 355 | function bound() { 356 | // `Function#bind` spec 357 | // http://es5.github.io/#x15.3.4.5 358 | if (partialArgs) { 359 | // avoid `arguments` object deoptimizations by using `slice` instead 360 | // of `Array.prototype.slice.call` and not assigning `arguments` to a 361 | // variable as a ternary expression 362 | var args = slice(partialArgs); 363 | push.apply(args, arguments); 364 | } 365 | // mimic the constructor's `return` behavior 366 | // http://es5.github.io/#x13.2.2 367 | if (this instanceof bound) { 368 | // ensure `new bound` is an instance of `func` 369 | var thisBinding = baseCreate(func.prototype), 370 | result = func.apply(thisBinding, args || arguments); 371 | return isObject(result) ? result : thisBinding; 372 | } 373 | return func.apply(thisArg, args || arguments); 374 | } 375 | setBindData(bound, bindData); 376 | return bound; 377 | } 378 | 379 | module.exports = baseBind; 380 | 381 | },{"lodash._basecreate":7,"lodash._setbinddata":10,"lodash._slice":24,"lodash.isobject":13}],7:[function(require,module,exports){ 382 | var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};/** 383 | * Lo-Dash 2.4.1 (Custom Build) 384 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 385 | * Copyright 2012-2013 The Dojo Foundation 386 | * Based on Underscore.js 1.5.2 387 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 388 | * Available under MIT license 389 | */ 390 | var isNative = require('lodash._isnative'), 391 | isObject = require('lodash.isobject'), 392 | noop = require('lodash.noop'); 393 | 394 | /* Native method shortcuts for methods with the same name as other `lodash` methods */ 395 | var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate; 396 | 397 | /** 398 | * The base implementation of `_.create` without support for assigning 399 | * properties to the created object. 400 | * 401 | * @private 402 | * @param {Object} prototype The object to inherit from. 403 | * @returns {Object} Returns the new object. 404 | */ 405 | function baseCreate(prototype, properties) { 406 | return isObject(prototype) ? nativeCreate(prototype) : {}; 407 | } 408 | // fallback for browsers without `Object.create` 409 | if (!nativeCreate) { 410 | baseCreate = (function() { 411 | function Object() {} 412 | return function(prototype) { 413 | if (isObject(prototype)) { 414 | Object.prototype = prototype; 415 | var result = new Object; 416 | Object.prototype = null; 417 | } 418 | return result || global.Object(); 419 | }; 420 | }()); 421 | } 422 | 423 | module.exports = baseCreate; 424 | 425 | },{"lodash._isnative":8,"lodash.isobject":13,"lodash.noop":9}],8:[function(require,module,exports){ 426 | /** 427 | * Lo-Dash 2.4.1 (Custom Build) 428 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 429 | * Copyright 2012-2013 The Dojo Foundation 430 | * Based on Underscore.js 1.5.2 431 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 432 | * Available under MIT license 433 | */ 434 | 435 | /** Used for native method references */ 436 | var objectProto = Object.prototype; 437 | 438 | /** Used to resolve the internal [[Class]] of values */ 439 | var toString = objectProto.toString; 440 | 441 | /** Used to detect if a method is native */ 442 | var reNative = RegExp('^' + 443 | String(toString) 444 | .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') 445 | .replace(/toString| for [^\]]+/g, '.*?') + '$' 446 | ); 447 | 448 | /** 449 | * Checks if `value` is a native function. 450 | * 451 | * @private 452 | * @param {*} value The value to check. 453 | * @returns {boolean} Returns `true` if the `value` is a native function, else `false`. 454 | */ 455 | function isNative(value) { 456 | return typeof value == 'function' && reNative.test(value); 457 | } 458 | 459 | module.exports = isNative; 460 | 461 | },{}],9:[function(require,module,exports){ 462 | /** 463 | * Lo-Dash 2.4.1 (Custom Build) 464 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 465 | * Copyright 2012-2013 The Dojo Foundation 466 | * Based on Underscore.js 1.5.2 467 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 468 | * Available under MIT license 469 | */ 470 | 471 | /** 472 | * A no-operation function. 473 | * 474 | * @static 475 | * @memberOf _ 476 | * @category Utilities 477 | * @example 478 | * 479 | * var object = { 'name': 'fred' }; 480 | * _.noop(object) === undefined; 481 | * // => true 482 | */ 483 | function noop() { 484 | // no operation performed 485 | } 486 | 487 | module.exports = noop; 488 | 489 | },{}],10:[function(require,module,exports){ 490 | /** 491 | * Lo-Dash 2.4.1 (Custom Build) 492 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 493 | * Copyright 2012-2013 The Dojo Foundation 494 | * Based on Underscore.js 1.5.2 495 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 496 | * Available under MIT license 497 | */ 498 | var isNative = require('lodash._isnative'), 499 | noop = require('lodash.noop'); 500 | 501 | /** Used as the property descriptor for `__bindData__` */ 502 | var descriptor = { 503 | 'configurable': false, 504 | 'enumerable': false, 505 | 'value': null, 506 | 'writable': false 507 | }; 508 | 509 | /** Used to set meta data on functions */ 510 | var defineProperty = (function() { 511 | // IE 8 only accepts DOM elements 512 | try { 513 | var o = {}, 514 | func = isNative(func = Object.defineProperty) && func, 515 | result = func(o, o, o) && func; 516 | } catch(e) { } 517 | return result; 518 | }()); 519 | 520 | /** 521 | * Sets `this` binding data on a given function. 522 | * 523 | * @private 524 | * @param {Function} func The function to set data on. 525 | * @param {Array} value The data array to set. 526 | */ 527 | var setBindData = !defineProperty ? noop : function(func, value) { 528 | descriptor.value = value; 529 | defineProperty(func, '__bindData__', descriptor); 530 | }; 531 | 532 | module.exports = setBindData; 533 | 534 | },{"lodash._isnative":11,"lodash.noop":12}],11:[function(require,module,exports){ 535 | module.exports=require(8) 536 | },{}],12:[function(require,module,exports){ 537 | module.exports=require(9) 538 | },{}],13:[function(require,module,exports){ 539 | /** 540 | * Lo-Dash 2.4.1 (Custom Build) 541 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 542 | * Copyright 2012-2013 The Dojo Foundation 543 | * Based on Underscore.js 1.5.2 544 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 545 | * Available under MIT license 546 | */ 547 | var objectTypes = require('lodash._objecttypes'); 548 | 549 | /** 550 | * Checks if `value` is the language type of Object. 551 | * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) 552 | * 553 | * @static 554 | * @memberOf _ 555 | * @category Objects 556 | * @param {*} value The value to check. 557 | * @returns {boolean} Returns `true` if the `value` is an object, else `false`. 558 | * @example 559 | * 560 | * _.isObject({}); 561 | * // => true 562 | * 563 | * _.isObject([1, 2, 3]); 564 | * // => true 565 | * 566 | * _.isObject(1); 567 | * // => false 568 | */ 569 | function isObject(value) { 570 | // check if the value is the ECMAScript language type of Object 571 | // http://es5.github.io/#x8 572 | // and avoid a V8 bug 573 | // http://code.google.com/p/v8/issues/detail?id=2291 574 | return !!(value && objectTypes[typeof value]); 575 | } 576 | 577 | module.exports = isObject; 578 | 579 | },{"lodash._objecttypes":14}],14:[function(require,module,exports){ 580 | /** 581 | * Lo-Dash 2.4.1 (Custom Build) 582 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 583 | * Copyright 2012-2013 The Dojo Foundation 584 | * Based on Underscore.js 1.5.2 585 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 586 | * Available under MIT license 587 | */ 588 | 589 | /** Used to determine if values are of the language type Object */ 590 | var objectTypes = { 591 | 'boolean': false, 592 | 'function': true, 593 | 'object': true, 594 | 'number': false, 595 | 'string': false, 596 | 'undefined': false 597 | }; 598 | 599 | module.exports = objectTypes; 600 | 601 | },{}],15:[function(require,module,exports){ 602 | /** 603 | * Lo-Dash 2.4.1 (Custom Build) 604 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 605 | * Copyright 2012-2013 The Dojo Foundation 606 | * Based on Underscore.js 1.5.2 607 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 608 | * Available under MIT license 609 | */ 610 | var baseCreate = require('lodash._basecreate'), 611 | isObject = require('lodash.isobject'), 612 | setBindData = require('lodash._setbinddata'), 613 | slice = require('lodash._slice'); 614 | 615 | /** 616 | * Used for `Array` method references. 617 | * 618 | * Normally `Array.prototype` would suffice, however, using an array literal 619 | * avoids issues in Narwhal. 620 | */ 621 | var arrayRef = []; 622 | 623 | /** Native method shortcuts */ 624 | var push = arrayRef.push; 625 | 626 | /** 627 | * The base implementation of `createWrapper` that creates the wrapper and 628 | * sets its meta data. 629 | * 630 | * @private 631 | * @param {Array} bindData The bind data array. 632 | * @returns {Function} Returns the new function. 633 | */ 634 | function baseCreateWrapper(bindData) { 635 | var func = bindData[0], 636 | bitmask = bindData[1], 637 | partialArgs = bindData[2], 638 | partialRightArgs = bindData[3], 639 | thisArg = bindData[4], 640 | arity = bindData[5]; 641 | 642 | var isBind = bitmask & 1, 643 | isBindKey = bitmask & 2, 644 | isCurry = bitmask & 4, 645 | isCurryBound = bitmask & 8, 646 | key = func; 647 | 648 | function bound() { 649 | var thisBinding = isBind ? thisArg : this; 650 | if (partialArgs) { 651 | var args = slice(partialArgs); 652 | push.apply(args, arguments); 653 | } 654 | if (partialRightArgs || isCurry) { 655 | args || (args = slice(arguments)); 656 | if (partialRightArgs) { 657 | push.apply(args, partialRightArgs); 658 | } 659 | if (isCurry && args.length < arity) { 660 | bitmask |= 16 & ~32; 661 | return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]); 662 | } 663 | } 664 | args || (args = arguments); 665 | if (isBindKey) { 666 | func = thisBinding[key]; 667 | } 668 | if (this instanceof bound) { 669 | thisBinding = baseCreate(func.prototype); 670 | var result = func.apply(thisBinding, args); 671 | return isObject(result) ? result : thisBinding; 672 | } 673 | return func.apply(thisBinding, args); 674 | } 675 | setBindData(bound, bindData); 676 | return bound; 677 | } 678 | 679 | module.exports = baseCreateWrapper; 680 | 681 | },{"lodash._basecreate":16,"lodash._setbinddata":19,"lodash._slice":24,"lodash.isobject":22}],16:[function(require,module,exports){ 682 | arguments[4][7][0].apply(exports,arguments) 683 | },{"lodash._isnative":17,"lodash.isobject":22,"lodash.noop":18}],17:[function(require,module,exports){ 684 | module.exports=require(8) 685 | },{}],18:[function(require,module,exports){ 686 | module.exports=require(9) 687 | },{}],19:[function(require,module,exports){ 688 | module.exports=require(10) 689 | },{"lodash._isnative":20,"lodash.noop":21}],20:[function(require,module,exports){ 690 | module.exports=require(8) 691 | },{}],21:[function(require,module,exports){ 692 | module.exports=require(9) 693 | },{}],22:[function(require,module,exports){ 694 | module.exports=require(13) 695 | },{"lodash._objecttypes":23}],23:[function(require,module,exports){ 696 | module.exports=require(14) 697 | },{}],24:[function(require,module,exports){ 698 | /** 699 | * Lo-Dash 2.4.1 (Custom Build) 700 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 701 | * Copyright 2012-2013 The Dojo Foundation 702 | * Based on Underscore.js 1.5.2 703 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 704 | * Available under MIT license 705 | */ 706 | 707 | /** 708 | * Slices the `collection` from the `start` index up to, but not including, 709 | * the `end` index. 710 | * 711 | * Note: This function is used instead of `Array#slice` to support node lists 712 | * in IE < 9 and to ensure dense arrays are returned. 713 | * 714 | * @private 715 | * @param {Array|Object|string} collection The collection to slice. 716 | * @param {number} start The start index. 717 | * @param {number} end The end index. 718 | * @returns {Array} Returns the new array. 719 | */ 720 | function slice(array, start, end) { 721 | start || (start = 0); 722 | if (typeof end == 'undefined') { 723 | end = array ? array.length : 0; 724 | } 725 | var index = -1, 726 | length = end - start || 0, 727 | result = Array(length < 0 ? 0 : length); 728 | 729 | while (++index < length) { 730 | result[index] = array[start + index]; 731 | } 732 | return result; 733 | } 734 | 735 | module.exports = slice; 736 | 737 | },{}],25:[function(require,module,exports){ 738 | /** 739 | * Lo-Dash 2.4.1 (Custom Build) 740 | * Build: `lodash modularize modern exports="npm" -o ./npm/` 741 | * Copyright 2012-2013 The Dojo Foundation 742 | * Based on Underscore.js 1.5.2 743 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 744 | * Available under MIT license 745 | */ 746 | 747 | /** 748 | * Checks if `value` is a function. 749 | * 750 | * @static 751 | * @memberOf _ 752 | * @category Objects 753 | * @param {*} value The value to check. 754 | * @returns {boolean} Returns `true` if the `value` is a function, else `false`. 755 | * @example 756 | * 757 | * _.isFunction(_); 758 | * // => true 759 | */ 760 | function isFunction(value) { 761 | return typeof value == 'function'; 762 | } 763 | 764 | module.exports = isFunction; 765 | 766 | },{}],26:[function(require,module,exports){ 767 | var curry = require('lodash.curry'); 768 | 769 | var BUILT_INS = { 'array': require('./instances/array') 770 | , 'function': require('./instances/function') 771 | , 'string': require('./instances/string') 772 | } 773 | 774 | var _groupsOf = curry(function(n, xs) { 775 | if(!xs.length) return []; 776 | return [xs.slice(0, n)].concat(_groupsOf(n, xs.slice(n, xs.length))); 777 | }); 778 | 779 | var _compose = curry(function(f,g,x) { return f(g(x)) }); 780 | 781 | var I = function(x){ return x; } 782 | 783 | // f . g . h == compose(f, g, h) 784 | var toAssociativeCommaInfix = function(fn) { 785 | return function() { 786 | var fns = [].slice.call(arguments) 787 | return function() { 788 | return _groupsOf(2, fns).reverse().map(function(g) { 789 | return (g.length > 1) ? fn.apply(this,g) : g[0]; 790 | }).reduce(function(x, f) { 791 | return [f.apply(f,x)]; 792 | }, arguments)[0]; 793 | }; 794 | }; 795 | }; 796 | 797 | var compose = toAssociativeCommaInfix(_compose); 798 | 799 | 800 | var pointy = {}; 801 | 802 | var id = function(x) { return x; } 803 | var K = function(x) { return function(){ return x; } } 804 | 805 | var map = curry(function(f, u) { 806 | return u.fmap ? u.fmap(f) : u.map(f); //sometimes map passes index so we use fmap if it has it. 807 | }); 808 | 809 | var ap = curry(function(a1, a2) { 810 | return a1.ap(a2); 811 | }); 812 | 813 | var liftA2 = curry(function(f, x, y) { 814 | return map(f,x).ap(y); 815 | }); 816 | 817 | var liftA3 = curry(function(f, x, y, z) { 818 | return map(f, x).ap(y).ap(z); 819 | }); 820 | 821 | var chain = curry(function(f, mv) { 822 | return mv.chain(f); 823 | }); 824 | 825 | var mjoin = function(mmv) { 826 | return chain(id, mmv); 827 | }; 828 | 829 | var concat = curry(function(x, y) { 830 | return x.concat(y); 831 | }); 832 | 833 | var mconcat = function(xs, empty) { 834 | return xs.length ? xs.reduce(concat) : empty(); 835 | }; 836 | 837 | var sequenceA = curry(function(point, fctr) { 838 | return fctr.traverse(id, point); 839 | }); 840 | 841 | var of = function(x) { 842 | return x.of; 843 | }; 844 | 845 | var traverse = curry(function(f, point, fctr) { 846 | return compose(sequenceA(point), map(f))(fctr); 847 | }); 848 | 849 | var foldMap = curry(function(f, fldable) { 850 | return fldable.reduce(function(acc, x) { 851 | var r = f(x); 852 | acc = acc || r.empty(); 853 | return acc.concat(r); 854 | }, null); 855 | }); 856 | 857 | var fold = foldMap(I); 858 | 859 | var toList = function(x) { 860 | return x.reduce(function(acc, y) { 861 | return [y].concat(acc); 862 | }, []); 863 | }; 864 | 865 | var expose = function(env) { 866 | var f; 867 | for (f in pointy) { 868 | if (f !== 'expose' && pointy.hasOwnProperty(f)) { 869 | env[f] = pointy[f]; 870 | } 871 | } 872 | } 873 | 874 | pointy.I = id; 875 | pointy.K = K; 876 | pointy.compose = compose; 877 | pointy.curry = curry; //lodash curry 878 | pointy.of = of; 879 | pointy.map = map; 880 | pointy.ap = ap; 881 | pointy.liftA2 = liftA2; 882 | pointy.liftA3 = liftA3; 883 | pointy.chain = chain; 884 | pointy.mjoin = mjoin; 885 | pointy.concat = concat; 886 | pointy.mappend = concat; 887 | pointy.mconcat = mconcat; 888 | pointy.sequenceA = sequenceA; 889 | pointy.traverse = traverse; 890 | pointy.foldMap = foldMap; 891 | pointy.fold = fold; 892 | pointy.toList = toList; 893 | pointy.expose = expose; 894 | 895 | module.exports = pointy; 896 | 897 | if(typeof window == "object") { 898 | pointfree = pointy; 899 | } 900 | 901 | },{"./instances/array":1,"./instances/function":2,"./instances/string":3,"lodash.curry":4}]},{},[26]) -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./pointfree'); -------------------------------------------------------------------------------- /instances/array.js: -------------------------------------------------------------------------------- 1 | var curry = require('lodash.curry'); 2 | 3 | var _flatten = function(xs) { 4 | return xs.reduce(function(a,b){return a.concat(b);}, []); 5 | }; 6 | 7 | var _fmap = function(f) { 8 | var xs = this; 9 | return xs.map(function(x) { return f(x); }); //avoid index 10 | }; 11 | 12 | Object.defineProperty(Array.prototype, 'fmap',{ 13 | value: _fmap, 14 | writable: true, 15 | configurable: true, 16 | enumerable: false 17 | }); 18 | 19 | var _empty = function() { return []; }; 20 | 21 | Object.defineProperty(Array.prototype, 'empty',{ 22 | value: _empty, 23 | writable: true, 24 | configurable: true, 25 | enumerable: false 26 | }); 27 | 28 | var _chain = function(f) { return _flatten(this.fmap(f)); }; 29 | 30 | Object.defineProperty(Array.prototype, 'chain',{ 31 | value: _chain, 32 | writable: true, 33 | configurable: true, 34 | enumerable: false 35 | }); 36 | 37 | var _of = function(x) { return [x]; }; 38 | 39 | Object.defineProperty(Array.prototype, 'of',{ 40 | value: _of, 41 | writable: true, 42 | configurable: true, 43 | enumerable: false 44 | }); 45 | 46 | var _ap = function(a2) { 47 | var a1 = this; 48 | return _flatten(a1.map(function(f){ 49 | return a2.map(function(a){ return f(a); }) 50 | })); 51 | }; 52 | 53 | Object.defineProperty(Array.prototype, 'ap',{ 54 | value: _ap, 55 | writable: true, 56 | configurable: true, 57 | enumerable: false 58 | }); 59 | 60 | var _traverse = function(f, point) { 61 | var cons_f = function(ys, x){ 62 | return f(x).map(function(x){ return function(y){ return y.concat(x); } }).ap(ys); 63 | } 64 | return this.reduce(cons_f, point([])); 65 | }; 66 | 67 | Object.defineProperty(Array.prototype, 'traverse',{ 68 | value: _traverse, 69 | writable: true, 70 | configurable: true, 71 | enumerable: false 72 | }); 73 | 74 | -------------------------------------------------------------------------------- /instances/const.js: -------------------------------------------------------------------------------- 1 | var ConstType = function(val) { 2 | this.val = val; 3 | } 4 | 5 | var getConst = function(c) { return c.val; }; 6 | 7 | var Const = function(val) { 8 | return new ConstType(val); 9 | }; 10 | 11 | 12 | ConstType.prototype.map = function(f) { 13 | return Const(this.val); 14 | }; 15 | 16 | // is const an applicative? 17 | // only if x is a monoid 18 | ConstType.prototype.of = function(x) { 19 | return Const(x.empty()); 20 | }; 21 | 22 | ConstType.prototype.ap = function(c2) { 23 | return Const(this.val.concat(c2.val)); 24 | }; 25 | 26 | Const.of = ConstType.prototype.of; 27 | 28 | // const is not a monad 29 | 30 | module.exports = {Const: Const, getConst: getConst} 31 | -------------------------------------------------------------------------------- /instances/function.js: -------------------------------------------------------------------------------- 1 | var _K = function(x) { return function(y) { return x; } }; 2 | 3 | var _map = function(g) { 4 | var f = this; 5 | return function(x) { return g(f(x)) }; 6 | }; 7 | 8 | Object.defineProperty(Function.prototype, 'map',{ 9 | value: _map, 10 | writable: true, 11 | configurable: true, 12 | enumerable: false 13 | }); 14 | 15 | var _concat = function(g) { 16 | var f = this; 17 | return function() { 18 | return f.apply(this, arguments).concat(g.apply(this, arguments)) 19 | } 20 | }; 21 | 22 | Object.defineProperty(Function.prototype, 'concat',{ 23 | value: _concat, 24 | writable: true, 25 | configurable: true, 26 | enumerable: false 27 | }); 28 | 29 | var _empty = function() { 30 | return _K({ concat: function(g) { return g.empty().concat(g); } }); 31 | }; 32 | 33 | Object.defineProperty(Function.prototype, 'empty',{ 34 | value: _empty, 35 | writable: true, 36 | configurable: true, 37 | enumerable: false 38 | }); 39 | 40 | var _chain = function(g) { 41 | var f = this; 42 | return function(x) { 43 | return g(f(x), x); 44 | }; 45 | }; 46 | 47 | Object.defineProperty(Function.prototype, 'chain',{ 48 | value: _chain, 49 | writable: true, 50 | configurable: true, 51 | enumerable: false 52 | }); 53 | 54 | var _of = _K; 55 | 56 | Object.defineProperty(Function.prototype, 'of',{ 57 | value: _of, 58 | writable: true, 59 | configurable: true, 60 | enumerable: false 61 | }); 62 | 63 | var _ap = function(g) { 64 | var f = this; 65 | return function(x) { 66 | return f(x)(g(x)); 67 | } 68 | }; 69 | 70 | Object.defineProperty(Function.prototype, 'ap',{ 71 | value: _ap, 72 | writable: true, 73 | configurable: true, 74 | enumerable: false 75 | }); 76 | -------------------------------------------------------------------------------- /instances/identity.js: -------------------------------------------------------------------------------- 1 | var Constructor = require('../util').Constructor; 2 | 3 | var Id = Constructor(function(a) { 4 | this.value = a; 5 | }); 6 | 7 | Id.prototype.concat = function(b) { 8 | return new Id(concat(this.value, b.value)); 9 | }; 10 | 11 | var runIdentity = function(i) { return i.value; }; 12 | 13 | Id.prototype.empty = function() { 14 | return new Id(this.value.empty()); 15 | }; 16 | 17 | Id.prototype.map = function(f) { 18 | return new Id(f(this.value)); 19 | }; 20 | 21 | Id.prototype.ap = function(b) { 22 | return new Id(this.value(b.value)); 23 | }; 24 | 25 | Id.prototype.chain = function(f) { 26 | return f(this.value); 27 | }; 28 | 29 | Id.prototype.of = function(a) { 30 | return new Id(a); 31 | }; 32 | 33 | module.exports = {Identity: Id, runIdentity: runIdentity}; 34 | -------------------------------------------------------------------------------- /instances/maybe.js: -------------------------------------------------------------------------------- 1 | var util = require('../util') 2 | , makeType = util.makeType 3 | , subClass = util.subClass 4 | , map = require('../pointfree').map 5 | ; 6 | 7 | var MaybeType = makeType() 8 | , Just = subClass(MaybeType) 9 | , Nothing = subClass(MaybeType) 10 | ; 11 | 12 | //+ notThere :: a -> Bool 13 | var notThere = function(val) { 14 | return (val === undefined || val === null); 15 | } 16 | 17 | var Maybe = function(x) { 18 | return notThere(x) ? Nothing() : Just(x); 19 | }; 20 | 21 | Maybe.Just = Just; 22 | Maybe.Nothing = Nothing; 23 | 24 | Nothing.prototype.concat = function(b) { 25 | return b; 26 | } 27 | Just.prototype.concat = function(b) { 28 | if(notThere(b.val)) return this; 29 | return Maybe(this.val.concat(b.val)); 30 | }; 31 | 32 | MaybeType.prototype.empty = function() { return Nothing(); } 33 | 34 | Nothing.prototype.map = function(f) { 35 | return Nothing(); 36 | } 37 | Just.prototype.map = function(f) { 38 | return Just(f(this.val)); 39 | } 40 | Maybe.of = Maybe; 41 | Nothing.prototype.of = function(x) { return Nothing(x) }; 42 | Just.prototype.of = function(x) { return Just(x) }; 43 | 44 | Nothing.prototype.ap = function(m) { 45 | return Nothing(); 46 | } 47 | Just.prototype.ap = function(m) { 48 | return map(this.val, m); 49 | } 50 | 51 | Nothing.prototype.chain = function(f) { 52 | return this; 53 | } 54 | Just.prototype.chain = function(f) { 55 | return f(this.val); 56 | } 57 | Nothing.prototype.traverse = function(f, point) { 58 | return point(Nothing()); 59 | } 60 | Just.prototype.traverse = function(f, point) { 61 | return f(this.val).map(Just); 62 | }; 63 | Nothing.prototype.reduce = function(f) { 64 | return f(null); 65 | }; 66 | Just.prototype.reduce = function(f, acc) { 67 | return f(acc, this.val); 68 | }; 69 | 70 | module.exports = Maybe; 71 | -------------------------------------------------------------------------------- /instances/monoids.js: -------------------------------------------------------------------------------- 1 | var Constructor = require('../util').Constructor; 2 | 3 | var inspect = function(x) { 4 | if(x==null || x==undefined) return "null"; 5 | return x.inspect ? x.inspect() : x; 6 | } 7 | 8 | var getResult = function(x) { return x.val; }; 9 | 10 | var Max = Constructor(function(val) { 11 | this.val = val; 12 | }); 13 | 14 | Max.prototype.empty = function() { return Max(Number.MIN_VALUE); }; 15 | 16 | Max.prototype.concat = function(s2) { return Max(this.val > s2.val ? this.val : s2.val); }; 17 | 18 | Max.prototype.inspect = function(f) { 19 | return 'Max('+inspect(this.val)+')'; 20 | }; 21 | 22 | var Min = Constructor(function(val) { 23 | this.val = val; 24 | }); 25 | 26 | 27 | Min.prototype.empty = function() { return Min(Number.MAX_VALUE); }; 28 | 29 | Min.prototype.concat = function(s2) { return Min(this.val < s2.val ? this.val : s2.val); }; 30 | 31 | Min.prototype.inspect = function(f) { 32 | return 'Min('+inspect(this.val)+')'; 33 | }; 34 | 35 | var Sum = Constructor(function(val) { 36 | this.val = val; 37 | }); 38 | 39 | Sum.prototype.empty = function() { return Sum(0); }; 40 | 41 | Sum.prototype.concat = function(s2) { return Sum(this.val + s2.val); }; 42 | 43 | Sum.prototype.inspect = function(f) { 44 | return 'Sum('+inspect(this.val)+')'; 45 | }; 46 | 47 | 48 | var Product = Constructor(function(val) { 49 | this.val = val; 50 | }); 51 | 52 | Product.prototype.empty = function() { return Product(1); }; 53 | 54 | Product.prototype.concat = function(s2) { return Product(this.val * s2.val); }; 55 | 56 | Product.prototype.inspect = function(f) { 57 | return 'Product('+inspect(this.val)+')'; 58 | }; 59 | 60 | 61 | var Any = Constructor(function(val) { 62 | this.val = val; 63 | }); 64 | 65 | Any.prototype.empty = function() { return Any(false); }; 66 | 67 | Any.prototype.concat = function(s2) { return Any(this.val || s2.val); }; 68 | 69 | Any.prototype.inspect = function(f) { 70 | return 'Any('+inspect(this.val)+')'; 71 | }; 72 | 73 | 74 | var All = Constructor(function(val) { 75 | this.val = val; 76 | }); 77 | 78 | All.prototype.empty = function() { return All(true); }; 79 | 80 | All.prototype.concat = function(s2) { return All(this.val && s2.val); }; 81 | 82 | All.prototype.inspect = function(f) { 83 | return 'All('+inspect(this.val)+')'; 84 | }; 85 | 86 | 87 | module.exports = {Max: Max, Min: Min, Sum: Sum, Product: Product, Any: Any, All: All, getResult: getResult} 88 | -------------------------------------------------------------------------------- /instances/string.js: -------------------------------------------------------------------------------- 1 | var _empty = function() { return ""; }; 2 | 3 | Object.defineProperty(String.prototype, 'empty',{ 4 | value: _empty, 5 | writable: true, 6 | configurable: true, 7 | enumerable: false 8 | }); 9 | -------------------------------------------------------------------------------- /instances/sum.js: -------------------------------------------------------------------------------- 1 | var Constructor = require('../util').Constructor; 2 | 3 | var Sum = Constructor(function(val) { 4 | this.val = val; 5 | }); 6 | 7 | var getSum = function(c) { return c.val; }; 8 | 9 | Sum.prototype.map = function(f) { 10 | return Sum(f(this.val)); 11 | }; 12 | 13 | Sum.prototype.empty = function() { return Sum(0); }; 14 | 15 | Sum.prototype.concat = function(s2) { return Sum(this.val + s2.val); }; 16 | 17 | Sum.prototype.of = function(x) { 18 | return Sum(x); 19 | }; 20 | 21 | Sum.prototype.ap = function(s2) { 22 | return Sum(ap(this.val, s2.val)); 23 | }; 24 | 25 | Sum.prototype.chaim = function(f) { 26 | return f(this.val); 27 | }; 28 | 29 | module.exports = {Sum: Sum, getSum: getSum} 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pointfree-fantasy", 3 | "version": "0.1.3", 4 | "description": "Point free wrappers for fantasy land js.", 5 | "main": "pointfree.js", 6 | "scripts": { 7 | "test": "mocha test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/DrBoolean/pointfree-fantasy.git" 12 | }, 13 | "keywords": [ 14 | "functional", 15 | "fantasy", 16 | "pointfree", 17 | "tacit" 18 | ], 19 | "author": "Loop/Recur", 20 | "license": "MIT", 21 | "dependencies": { 22 | "lodash.curry": "*" 23 | }, 24 | "devDependencies": { 25 | "mocha": "~1.9.0", 26 | "claire": "~0.4.1" 27 | }, 28 | "bugs": { 29 | "url": "https://github.com/DrBoolean/pointfree-fantasy/issues" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pointfree.js: -------------------------------------------------------------------------------- 1 | var curry = require('lodash.curry'); 2 | 3 | var BUILT_INS = { 'array': require('./instances/array') 4 | , 'function': require('./instances/function') 5 | , 'string': require('./instances/string') 6 | } 7 | 8 | var _groupsOf = curry(function(n, xs) { 9 | if(!xs.length) return []; 10 | return [xs.slice(0, n)].concat(_groupsOf(n, xs.slice(n, xs.length))); 11 | }); 12 | 13 | var _compose = curry(function(f,g,x) { return f(g(x)) }); 14 | 15 | var I = function(x){ return x; } 16 | 17 | // f . g . h == compose(f, g, h) 18 | var toAssociativeCommaInfix = function(fn) { 19 | return function() { 20 | var fns = [].slice.call(arguments) 21 | return function() { 22 | return _groupsOf(2, fns).reverse().map(function(g) { 23 | return (g.length > 1) ? fn.apply(this,g) : g[0]; 24 | }).reduce(function(x, f) { 25 | return [f.apply(f,x)]; 26 | }, arguments)[0]; 27 | }; 28 | }; 29 | }; 30 | 31 | var compose = toAssociativeCommaInfix(_compose); 32 | 33 | 34 | var pointy = {}; 35 | 36 | var id = function(x) { return x; } 37 | var K = function(x) { return function(){ return x; } } 38 | 39 | var map = curry(function(f, u) { 40 | if(u.fmap) return u.fmap(f); //sometimes map passes index so we use fmap if it has it. 41 | if(u.map) return u.map(f); 42 | return chain(compose(of(u), f), u); 43 | }); 44 | 45 | var ap = curry(function(a1, a2) { 46 | if(a1.ap) return a1.ap(a2); 47 | return chain(map(curry.placeholder, a2), a1); 48 | }); 49 | 50 | var liftA2 = curry(function(f, x, y) { 51 | return map(f,x).ap(y); 52 | }); 53 | 54 | var liftA3 = curry(function(f, x, y, z) { 55 | return map(f, x).ap(y).ap(z); 56 | }); 57 | 58 | var chain = curry(function(f, mv) { 59 | return mv.chain ? mv.chain(f) : mv.then(f); 60 | }); 61 | 62 | var mjoin = function(mmv) { 63 | return chain(id, mmv); 64 | }; 65 | 66 | var concat = curry(function(x, y) { 67 | return x.concat(y); 68 | }); 69 | 70 | var mconcat = function(xs, empty) { 71 | return xs.length ? xs.reduce(concat) : empty(); 72 | }; 73 | 74 | var sequenceA = curry(function(point, fctr) { 75 | return fctr.traverse(id, point); 76 | }); 77 | 78 | var of = function(x) { 79 | return x.of; 80 | }; 81 | 82 | var traverse = curry(function(f, point, fctr) { 83 | return compose(sequenceA(point), map(f))(fctr); 84 | }); 85 | 86 | var foldMap = curry(function(f, fldable) { 87 | return fldable.reduce(function(acc, x) { 88 | var r = f(x); 89 | acc = acc || r.empty(); 90 | return acc.concat(r); 91 | }, null); 92 | }); 93 | 94 | var fold = curry(function(f, g, x) { return x.fold(f, g) }) 95 | 96 | var toList = function(x) { 97 | return x.reduce(function(acc, y) { 98 | return [y].concat(acc); 99 | }, []); 100 | }; 101 | 102 | var expose = function(env) { 103 | var f; 104 | for (f in pointy) { 105 | if (f !== 'expose' && pointy.hasOwnProperty(f)) { 106 | env[f] = pointy[f]; 107 | } 108 | } 109 | } 110 | 111 | pointy.I = id; 112 | pointy.K = K; 113 | pointy.compose = compose; 114 | pointy.curry = curry; //lodash curry 115 | pointy.of = of; 116 | pointy.map = map; 117 | pointy.ap = ap; 118 | pointy.liftA2 = liftA2; 119 | pointy.liftA3 = liftA3; 120 | pointy.chain = chain; 121 | pointy.mjoin = mjoin; 122 | pointy.concat = concat; 123 | pointy.mappend = concat; 124 | pointy.mconcat = mconcat; 125 | pointy.sequenceA = sequenceA; 126 | pointy.sequence = sequenceA; 127 | pointy.traverse = traverse; 128 | pointy.foldMap = foldMap; 129 | pointy.fold = fold; 130 | pointy.toList = toList; 131 | pointy.expose = expose; 132 | 133 | module.exports = pointy; 134 | 135 | if(typeof window == "object") { 136 | pointfree = pointy; 137 | } 138 | -------------------------------------------------------------------------------- /test/array.js: -------------------------------------------------------------------------------- 1 | require('../pointfree').expose(global) 2 | 3 | 4 | var assert = require("assert") 5 | , quickCheckLaws = require('./helper').quickCheckLaws 6 | , curry = require('lodash.curry') 7 | , monoids = require('../instances/monoids') 8 | , claire = require('claire') 9 | , Maybe = require('../instances/maybe') 10 | , _ = claire.data 11 | , forAll = claire.forAll 12 | ; 13 | 14 | describe('Array', function(){ 15 | quickCheckLaws({ 'Semigroup': _.Array(_.Int) 16 | , 'Monoid': _.Array(_.Int) 17 | , 'Functor': _.Array(_.Int) 18 | , 'Applicative': _.Array(_.Int) 19 | , 'Monad': _.Array(_.Int) 20 | }); 21 | 22 | it('is traversable', function() { 23 | var f = function(x){ return Maybe(x); } 24 | var xs = [1,2]; 25 | assert.deepEqual(traverse(f, Maybe.of, xs), Maybe([1,2])); 26 | }) 27 | 28 | it('is foldable', function() { 29 | assert.deepEqual(foldMap(monoids.Sum, [1,2,3]), monoids.Sum(6)) 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/const.js: -------------------------------------------------------------------------------- 1 | require('../pointfree').expose(global) 2 | 3 | 4 | var Constant = require('../instances/const') 5 | , quickCheckLaws = require('./helper').quickCheckLaws 6 | , Const = Constant.Const 7 | , getConst = Constant.getConst 8 | , assert = require("assert") 9 | , curry = require('lodash.curry') 10 | , claire = require('claire') 11 | , _ = claire.data 12 | , forAll = claire.forAll 13 | ; 14 | 15 | var ConstGen = claire.transform(Const, _.Str); 16 | 17 | describe('Const', function(){ 18 | quickCheckLaws({ 'Functor': ConstGen }); 19 | 20 | describe('Functor', function(){ 21 | it('ignores map entirely', function(){ 22 | var someFn = function(x) { return x + 'this will not happen'; }; 23 | assert.deepEqual(map(someFn, Const('hi')), Const('hi')); 24 | }); 25 | }); 26 | 27 | describe('Applicative', function(){ 28 | it('concats values if value is a monoid', function() { 29 | assert.deepEqual(Const('1').ap(Const('2')), Const('12')); 30 | }); 31 | }); 32 | }); 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/function.js: -------------------------------------------------------------------------------- 1 | require('../pointfree').expose(global) 2 | 3 | 4 | var assert = require("assert") 5 | , quickCheckLaws = require('./helper').quickCheckLaws 6 | , curry = require('lodash.curry') 7 | , claire = require('claire') 8 | , _ = claire.data 9 | , asGenerator = claire.asGenerator 10 | , forAll = claire.forAll 11 | ; 12 | 13 | var _stubFn = function() { return function() { return _.Str.next().value }; }; 14 | var FunGen = asGenerator(_stubFn) 15 | 16 | 17 | describe('Function', function(){ 18 | var f = function(x) { return x + 'hello'} 19 | , g = function(y) { return y + 'world'} 20 | ; 21 | 22 | describe("Monoid", function() { 23 | it('runs the functions then concats the values together', function() { 24 | assert.equal(mappend(f, g)(' bla '), ' bla hello bla world') 25 | assert.equal(mconcat([f, g])(' bla '), ' bla hello bla world') 26 | }); 27 | }); 28 | 29 | describe("Functor", function() { 30 | it('composes the functions', function() { 31 | assert.equal(map(g, f)(' bla '), ' bla helloworld') 32 | }); 33 | }); 34 | 35 | describe("Applicative", function() { 36 | it('runs each function with the arg then passes the results on', function() { 37 | var h = curry(function(x,y) { return x.toUpperCase() + y.toLowerCase(); }) 38 | assert.equal(liftA2(h, f, g)(' bla '), ' BLA HELLO bla world') 39 | }); 40 | 41 | it('runs each function with the arg then passes the results on (with 3)', function() { 42 | var h = curry(function(x,y,z) { return x.toUpperCase() + y.toLowerCase() + z }) 43 | var i = function(z) { return z + 'last'}; 44 | assert.equal(liftA3(h, f, g, i)(' bla '), ' BLA HELLO bla world bla last') 45 | }); 46 | }); 47 | }); 48 | 49 | 50 | -------------------------------------------------------------------------------- /test/helper.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert") 2 | , curry = require('lodash.curry') 3 | , claire = require('claire') 4 | , _ = claire.data 5 | , forAll = claire.forAll 6 | ; 7 | 8 | var add = curry(function(x, y) { return x + y; }); 9 | 10 | var semigroupAssocTest = function(gen) { 11 | return forAll(gen, gen, gen).satisfy(function(a, b, c) { 12 | assert.deepEqual(mappend(mappend(a, b), c), mappend(a, mappend(b, c))); 13 | return true; 14 | }).asTest({times: 100}); 15 | }; 16 | 17 | var monoidIdentityTest = function(gen) { 18 | return forAll(gen).satisfy(function(m) { 19 | assert.deepEqual(mappend(m.empty(), m), mappend(m, m.empty())); 20 | return true; 21 | }).asTest({times: 100}) 22 | }; 23 | 24 | var functorIdentity = function(gen) { 25 | return forAll(gen).satisfy(function(m) { 26 | assert.deepEqual(map(I, m), I(m)); 27 | return true; 28 | }).asTest({times: 100}) 29 | }; 30 | 31 | var functorComp = function(gen) { 32 | return forAll(gen).satisfy(function(m) { 33 | var f = add('one') 34 | , g = add('two'); 35 | assert.deepEqual(map(compose(f, g), m), compose(map(f), map(g))(m)); 36 | return true; 37 | }).asTest({times: 100}) 38 | }; 39 | 40 | var applicativeIdentity = function(gen) { 41 | return forAll(gen).satisfy(function(m) { 42 | assert.deepEqual(ap(m.of(I), m), m); 43 | return true; 44 | }).asTest({times: 100}) 45 | }; 46 | 47 | var applicativeComp = function(gen) { 48 | return forAll(gen, gen).satisfy(function(m, w) { 49 | var f = m.of(add('one')) 50 | , g = m.of(add('two')) 51 | , _compose = curry(function(f,g,x) { return f(g(x)); }) 52 | ; 53 | assert.deepEqual(ap(ap(ap(m.of(_compose), f), g), w), ap(f, ap(g, w))); 54 | return true; 55 | }).asTest({times: 100}) 56 | }; 57 | 58 | var applicativeHomoMorph = function(gen) { 59 | return forAll(gen, _.Any).satisfy(function(m, x) { 60 | var f = function(y){ return [y]; } 61 | assert.deepEqual(ap(m.of(f), m.of(x)), m.of(f(x))); 62 | return true; 63 | }).asTest({times: 100}) 64 | }; 65 | 66 | var applicativeInterChange = function(gen) { 67 | return forAll(gen, _.Any).satisfy(function(m, x) { 68 | var u = m.of(function(x){ return [x]; }); 69 | assert.deepEqual(ap(u, m.of(x)), ap(m.of(function(f) { return f(x); }), u)); 70 | return true; 71 | }).asTest({times: 100}); 72 | }; 73 | 74 | var monadAssoc = function(gen) { 75 | return forAll(gen).satisfy(function(m) { 76 | var f = function(x){ return m.of(add('nest1', x))} 77 | , g = function(x){ return m.of(add('nest2', x))} 78 | , h = function(x){ return m.of(add('nest3', x))} 79 | , mcompose_ = curry(function(f, g, x) { return chain(f, g(x)); }) 80 | ; 81 | assert.deepEqual(mcompose_(f, mcompose_(g, h))(m), mcompose_(mcompose_(f, g), h)(m)); 82 | return true; 83 | }).asTest({times: 100}); 84 | }; 85 | 86 | var Laws = { 'Semigroup': [['associativity', semigroupAssocTest]] 87 | , 'Monoid': [['identity', monoidIdentityTest]] 88 | , 'Functor': [['identity', functorIdentity], ['composition', functorComp]] 89 | , 'Applicative': [ ['identity', applicativeIdentity] 90 | , ['composition', applicativeComp] 91 | , ['homomorphism', applicativeHomoMorph] 92 | , ['interchange', applicativeInterChange] 93 | ] 94 | , 'Monad': [['assoc', monadAssoc]] 95 | } 96 | 97 | module.exports.quickCheckLaws = function(laws) { 98 | Object.keys(laws).map(function(typeclass) { 99 | describe(typeclass, function() { 100 | Laws[typeclass].map(function(law) { 101 | it(law[0], law[1](laws[typeclass])); 102 | }); 103 | }); 104 | }); 105 | }; 106 | -------------------------------------------------------------------------------- /test/identity.js: -------------------------------------------------------------------------------- 1 | require('../pointfree').expose(global) 2 | 3 | 4 | var assert = require("assert") 5 | , quickCheckLaws = require('./helper').quickCheckLaws 6 | , curry = require('lodash.curry') 7 | , claire = require('claire') 8 | , _ = claire.data 9 | , asGenerator = claire.asGenerator 10 | , forAll = claire.forAll 11 | , Identity = require('../instances/identity').Identity 12 | ; 13 | 14 | var IdentityGen = claire.transform(Identity, _.Str); 15 | 16 | 17 | describe('Identity', function(){ 18 | quickCheckLaws({ 'Semigroup': IdentityGen 19 | , 'Monoid': IdentityGen 20 | , 'Functor': IdentityGen 21 | , 'Applicative': IdentityGen 22 | , 'Monad': IdentityGen 23 | }); 24 | }); 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/maybe.js: -------------------------------------------------------------------------------- 1 | require('../pointfree').expose(global) 2 | 3 | var Maybe = require('../instances/maybe') 4 | , Just = Maybe.Just 5 | , Nothing = Maybe.Nothing 6 | , assert = require("assert") 7 | , quickCheckLaws = require('./helper').quickCheckLaws 8 | , curry = require('lodash.curry') 9 | , claire = require('claire') 10 | , _ = claire.data 11 | , forAll = claire.forAll 12 | ; 13 | 14 | var pluck = curry(function(x, o) { return o[x]; }); 15 | 16 | var safeGet = curry(function(x, o) { return Maybe(o[x]); }); 17 | 18 | var add = curry(function(x, y) { return x + y; }); 19 | 20 | var user = {email: "sally@test.com", address: {street: {number: 23, name: "Winston"}}} 21 | 22 | var MaybeGen = claire.transform(Maybe, _.Any); 23 | var MaybeMonoidGen = claire.transform(Maybe, _.Str); 24 | 25 | describe('Maybe', function(){ 26 | quickCheckLaws({ 'Semigroup': MaybeMonoidGen 27 | , 'Monoid': MaybeMonoidGen 28 | , 'Functor': MaybeGen 29 | , 'Applicative': MaybeMonoidGen 30 | , 'Monad': MaybeGen 31 | }); 32 | 33 | describe('Monoid', function(){ 34 | it('ignores the nulls and combines the contained monoid', function() { 35 | assert.deepEqual(mconcat([Just("hi, "), Nothing(), Just("guy")]), Just("hi, guy")); 36 | }); 37 | }); 38 | 39 | describe('Functor', function(){ 40 | it('works with map', function(){ 41 | var getStreet = compose(map(add('my email is ')), safeGet('email')); 42 | assert.deepEqual(getStreet(user), Just('my email is sally@test.com')); 43 | }); 44 | }); 45 | 46 | describe('Applicative', function(){ 47 | it('runs if values are present', function() { 48 | assert.deepEqual(Just(add('yo ')).ap(Just('dawg')), Just('yo dawg')); 49 | }); 50 | 51 | it('fails on null', function() { 52 | assert.deepEqual(Just(add('yo ')).ap(Just('dawg')).ap(Nothing()), Nothing()); 53 | }); 54 | 55 | it('applys the function to the value within the context of Maybe', function() { 56 | assert.deepEqual(ap(Just(add(2)), Just(3)), Just(5)); 57 | }); 58 | 59 | it('lifts a function into the maybe context', function() { 60 | assert.deepEqual(liftA2(add, Just(2), Just(3)), Just(5)); 61 | }); 62 | }); 63 | 64 | describe('Monad', function(){ 65 | var flatSafeTraverseStreetName = compose( mjoin 66 | , map(safeGet('name')) 67 | , mjoin 68 | , map(safeGet('street')) 69 | , safeGet('address') 70 | ); 71 | 72 | it('flattens the nested maps', function(){ 73 | var user = {email: "sally@test.com", address: {street: {number: 23, name: "Winston"}}} 74 | assert.deepEqual(flatSafeTraverseStreetName(user), Just('Winston')); 75 | }) 76 | 77 | it('fails if null', function(){ 78 | var user = {email: "sally@test.com", address: null} 79 | assert.deepEqual(flatSafeTraverseStreetName(user), Nothing()); 80 | }) 81 | 82 | it('binds a value to the function', function(){ 83 | var result = chain(function(three){ 84 | return chain(function(four){ 85 | return Just(three + four); 86 | }, Just(4)) 87 | }, Just(3)); 88 | assert.deepEqual(result, Just(7)); 89 | }) 90 | }); 91 | describe('Other', function() { 92 | it('is traversable', function() { 93 | var f = function(x){ return [x]; } 94 | assert.deepEqual(traverse(f, Array.of, Just(1)), [Just(1)]); 95 | }) 96 | 97 | it('is foldable', function() { 98 | assert.deepEqual(foldMap(concat([1,2]), Just([3,4])), [1,2,3,4]) 99 | assert.deepEqual(toList(Just(3)), [3]) 100 | }); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /test/monoids.js: -------------------------------------------------------------------------------- 1 | require('../pointfree').expose(global) 2 | 3 | 4 | var assert = require("assert") 5 | , quickCheckLaws = require('./helper').quickCheckLaws 6 | , curry = require('lodash.curry') 7 | , claire = require('claire') 8 | , _ = claire.data 9 | , asGenerator = claire.asGenerator 10 | , monoids = require('../instances/monoids') 11 | , forAll = claire.forAll 12 | ; 13 | 14 | var _stubFn = function() { return function() { return _.Str.next().value }; }; 15 | var FunGen = asGenerator(_stubFn) 16 | 17 | describe('Monoids', function(){ 18 | it('gets the max', function() { 19 | assert.equal(monoids.getResult(mappend(monoids.Max(12), monoids.Max(20))), 20) 20 | }); 21 | 22 | it('gets the min', function() { 23 | assert.equal(monoids.getResult(mappend(monoids.Min(12), monoids.Min(20))), 12) 24 | }); 25 | 26 | it('gets the sum', function() { 27 | assert.equal(monoids.getResult(mappend(monoids.Sum(12), monoids.Sum(20))), 32) 28 | }); 29 | 30 | it('gets the product', function() { 31 | assert.equal(monoids.getResult(mappend(monoids.Product(2), monoids.Product(20))), 40) 32 | }); 33 | 34 | it('gets the disjunction', function() { 35 | assert.equal(monoids.getResult(mappend(monoids.Any(true), monoids.Any(false))), true) 36 | assert.equal(monoids.getResult(mappend(monoids.Any(false), monoids.Any(false))), false) 37 | }); 38 | 39 | it('gets the conjunction', function() { 40 | assert.equal(monoids.getResult(mconcat([monoids.All(true), monoids.All(false)])), false) 41 | assert.equal(monoids.getResult(mappend(monoids.All(true), monoids.All(true))), true) 42 | }); 43 | 44 | it('runs the functions then gets the conjuction', function() { 45 | var f = function(x) { return monoids.Any(x > 1); } 46 | , g = function(y) { return monoids.Any(y > 2); } 47 | ; 48 | 49 | // var t = compose(monoids.getResult, monoids.mconcat, fmap(monoids.All), mconcat([f, g])) 50 | // assert.equal(t(2, 3), false) 51 | assert.deepEqual(mconcat([f, g])(4, 6), monoids.Any(true)) 52 | assert.deepEqual(mconcat([f, g])(0, 0), monoids.Any(false)) 53 | }); 54 | 55 | }); 56 | 57 | 58 | -------------------------------------------------------------------------------- /test/string.js: -------------------------------------------------------------------------------- 1 | require('../pointfree').expose(global) 2 | 3 | 4 | var assert = require("assert") 5 | , quickCheckLaws = require('./helper').quickCheckLaws 6 | , curry = require('lodash.curry') 7 | , claire = require('claire') 8 | , _ = claire.data 9 | , forAll = claire.forAll 10 | ; 11 | 12 | describe('String', function(){ 13 | quickCheckLaws({ 'Semigroup': _.Str 14 | , 'Monoid': _.Str 15 | }); 16 | }); 17 | 18 | 19 | -------------------------------------------------------------------------------- /util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Constructor = function(f) { 4 | var x = function(){ 5 | if(!(this instanceof x)){ 6 | var inst = new x(); 7 | f.apply(inst, arguments); 8 | return inst; 9 | } 10 | f.apply(this, arguments); 11 | }; 12 | 13 | return x; 14 | }; 15 | exports.Constructor = Constructor; 16 | var makeType = function(f) { 17 | f = f || function(v){ this.val = v; } 18 | return Constructor(f); 19 | }; 20 | exports.makeType = makeType; 21 | 22 | var subClass = function(superclass, constructr) { 23 | var x = makeType(); 24 | x.prototype = new superclass(); 25 | x.prototype.constructor=constructr; 26 | return x; 27 | } 28 | exports.subClass = subClass; 29 | 30 | var K = function(x){return function(){return x;};}; 31 | exports.K = K;var I = function(x){return x;}; 32 | exports.I = I; --------------------------------------------------------------------------------