├── .gitignore ├── .babelrc ├── src ├── comonad.js ├── alternative.js ├── category.js ├── monad.js ├── semigroupoid.js ├── contravariant.js ├── plus.js ├── biapplicative.js ├── alt.js ├── monoid.js ├── semigroup.js ├── applicative.js ├── functor.js ├── bifunctor.js ├── chain.js ├── extend.js ├── setoid.js ├── chainRec.js ├── foldable.js ├── biapply.js ├── profunctor.js ├── apply.js ├── traversable.js └── ord.js ├── README.md ├── LICENSE └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | index.js 3 | *.swp 4 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "env", { 4 | "targets": { 5 | "node": "current" 6 | }, 7 | "useBuiltIns": true 8 | }] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/comonad.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { protocol, implements } from 'sweet-interfaces'; 4 | import { Extend } from './functor'; 5 | 6 | protocol Comonad extends Extend { 7 | // extract :: Comonad w => w a ~> () -> a 8 | extract; 9 | } 10 | 11 | const extract = comonad => comonad[Comonad.extract](); 12 | 13 | export { Comonad, extract }; 14 | -------------------------------------------------------------------------------- /src/alternative.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Plus } from './plus'; 5 | import { Applicative } from './applicative'; 6 | 7 | protocol Alternative extends Applicative, Plus {} 8 | 9 | Reflect.implement(Array, Alternative); 10 | Reflect.implement(Object, Alternative); 11 | 12 | export { Alternative }; 13 | -------------------------------------------------------------------------------- /src/category.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Semigroupoid } from './semigroupoid'; 5 | 6 | protocol Category extends Semigroupoid { 7 | static id; 8 | } 9 | 10 | Function[Category.id] = x => x; 11 | Reflect.implement(Function, Category); 12 | 13 | const id = typeRep => typeRep[Category.id]; 14 | 15 | export { Category, id }; 16 | -------------------------------------------------------------------------------- /src/monad.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { protocol, implements } from 'sweet-interfaces'; 4 | import { Functor } from './functor'; 5 | import { Chain } from './chain'; 6 | 7 | protocol Monad extends Applicative, Chain { 8 | [Functor.map](f) { 9 | return this[Chain.chain](a => this.constructor[Applicative.of](f(a))); 10 | } 11 | } 12 | 13 | Reflect.implement(Array, Monad); 14 | Reflect.implement(Function, Monad); 15 | 16 | export { Monad }; 17 | -------------------------------------------------------------------------------- /src/semigroupoid.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | 5 | protocol Semigroupoid { 6 | // compose :: Semigroupoid c => c i j ~> c j k -> c i k 7 | compose; 8 | } 9 | 10 | Function.prototype[Semigroupoid.compose] = function compose(g) { 11 | return x => g(this(x)); 12 | }; 13 | Reflect.implement(Function, Semigroupoid); 14 | 15 | const compose = (f, g) => g[Semigroupoid.compose](f); 16 | 17 | export { Semigroupoid, compose }; 18 | -------------------------------------------------------------------------------- /src/contravariant.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | 5 | protocol Contravariant { 6 | // contramap :: Contravariant f => f a ~> (b -> a) -> f b 7 | contramap; 8 | } 9 | 10 | Function.prototype[Contravariant.contramap] = function contramap(f) { 11 | return x => this(f(x)); 12 | }; 13 | Reflect.implement(Function, Contravariant); 14 | 15 | const contramap = (f, contravariant) => contravariant[Contravariant.contramap](f); 16 | 17 | export { Contravariant, contramap }; 18 | -------------------------------------------------------------------------------- /src/plus.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Alt } from './alt'; 5 | import { Monoid } from './monoid'; 6 | 7 | protocol Plus extends Alt { 8 | // zero :: Plus f => () -> f a 9 | static zero; 10 | } 11 | 12 | Array[Plus.zero] = Array[Monoid.empty]; 13 | Reflect.implement(Array, Plus); 14 | 15 | Object[Plus.zero] = Object[Monoid.empty]; 16 | Reflect.implement(Object, Plus); 17 | 18 | const zero = typeRep => typeRep[Plus.zero](); 19 | 20 | export { Plus, zero }; 21 | -------------------------------------------------------------------------------- /src/biapplicative.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Bipply } from './biapply'; 5 | import { Bifunctor } from './bifunctor'; 6 | 7 | protocol Applicative extends Apply { 8 | // biof :: Biapplicative w => a -> b -> w a b 9 | static biof; 10 | [Bifunctor.bimap](f, g) { 11 | return this[Biapply.biap](this.constructor[Biapplicative.biof](f, g)); 12 | } 13 | } 14 | 15 | const biof = typeRep => (a, b) => typeRep[Biapplicative.biof](a, b); 16 | 17 | export { Biapplicative, biof }; 18 | -------------------------------------------------------------------------------- /src/alt.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Functor } from './functor'; 5 | import { Semigroup } from './semigroup'; 6 | 7 | protocol Alt extends Functor { 8 | // alt :: Alt f => f a ~> f a -> f a 9 | alt; 10 | } 11 | 12 | Array.prototype[Alt.alt] = Array.prototype[Semigroup.concat]; 13 | Reflect.implement(Array, Alt); 14 | 15 | Object.prototype[Alt.alt] = Object.prototype[Semigroup.concat]; 16 | Reflect.implement(Object, Alt); 17 | 18 | const alt = (a, b) => a[Alt.alt](b); 19 | 20 | export { Alt, alt }; 21 | -------------------------------------------------------------------------------- /src/monoid.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Semigroup } from './semigroup'; 5 | 6 | protocol Monoid extends Semigroup { 7 | // empty :: Monoid m => () -> m 8 | static empty; 9 | } 10 | 11 | String[Monoid.empty] = () => ""; 12 | Reflect.implement(String, Monoid); 13 | 14 | Array[Monoid.empty] = () => []; 15 | Reflect.implement(Array, Monoid); 16 | 17 | Object[Monoid.empty] = () => ({}); 18 | Reflect.implement(Object, Monoid); 19 | 20 | const empty = typeRep => typeRep[Monoid.empty](); 21 | 22 | export { Monoid, empty }; 23 | -------------------------------------------------------------------------------- /src/semigroup.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | 5 | protocol Semigroup { 6 | // concat :: Semigroup a => a ~> a -> a 7 | concat(b) { 8 | return this.concat(b); 9 | } 10 | } 11 | 12 | Reflect.implement(String, Semigroup); 13 | Reflect.implement(Array, Semigroup); 14 | 15 | Object.prototype[Semigroup.concat] = function concat(b) { 16 | const result = {}; 17 | let k; 18 | for (k in this) result[k] = this[k]; 19 | for (k in b) result[k] = b[k]; 20 | return result; 21 | }; 22 | Reflect.implement(Object, Semigroup); 23 | 24 | const concat = (a, b) => a[Semigroup.concat](b); 25 | 26 | export { Semigroup, concat }; 27 | -------------------------------------------------------------------------------- /src/applicative.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Apply } from './apply'; 5 | import { Functor } from './functor'; 6 | 7 | protocol Applicative extends Apply { 8 | // of :: Applicative f => a -> f a 9 | static of; 10 | [Functor.map](f) { 11 | return this[Apply.ap](this.constructor[Applicative.of](f)); 12 | } 13 | } 14 | 15 | Array[Applicative.of] = function of(a) { 16 | return [a]; 17 | }; 18 | Reflect.implement(Array, Applicative); 19 | 20 | Function[Applicative.of] = x => _ => x; 21 | Reflect.implement(Function, Applicative); 22 | 23 | const of = typeRep => v => typeRep[Applicative.of](v); 24 | 25 | export { Applicative, of }; 26 | -------------------------------------------------------------------------------- /src/functor.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | 5 | protocol Functor { 6 | // map :: Functor f => f a ~> (a -> b) -> f b 7 | map; 8 | } 9 | 10 | Array.prototype[Functor.map] = function map(f) { 11 | return this.map(f); 12 | }; 13 | Reflect.implement(Array, Functor); 14 | 15 | Object.prototype[Functor.map] = function map(f) { 16 | var result = {}; 17 | for (var k in this) result[k] = f(this[k]); 18 | return result; 19 | }; 20 | Reflect.implement(Object, Functor); 21 | 22 | Function.prototype[Functor.map] = function map(f) { 23 | return x => f(this(x)); 24 | }; 25 | Reflect.implement(Function, Functor); 26 | 27 | const map = (f, functor) => functor[Functor.map](f); 28 | 29 | export { Functor, map }; 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fantasy Interfaces 2 | 3 | Fantasy Land 4 | 5 | JavaScript "native" interfaces for Fantasy Land. 6 | 7 | It uses [sweet-interfaces](https://github.com/disnet/sweet-interfaces) to implement an extended [Fantasy Land](https://github.com/fantasyland/fantasy-land) specification. 8 | 9 | See the [ECMAScript Interfaces Proposal](https://github.com/michaelficarra/ecmascript-interfaces-proposal) for details. 10 | 11 | 12 | ## Attributions 13 | 14 | Sanctuary 15 | 16 | Most of the code for native types was adapted from the [Sanctuary](https://github.com/sanctuary-js/sanctuary-type-classes) project. 17 | -------------------------------------------------------------------------------- /src/bifunctor.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { protocol, implements } from 'sweet-interfaces'; 4 | import { Functor } from './functor'; 5 | import { id } from './category'; 6 | 7 | const identity = id(Function); 8 | 9 | protocol Bifunctor extends Functor { 10 | // bimap :: Bifunctor f => f a c ~> (a -> b, c -> d) -> f b d 11 | bimap(f, g) { 12 | return this[Bifunctor.first](f)[Bifunctor.second](g); 13 | } 14 | 15 | // first :: Bifunctor f => f a c ~> (a -> b) -> f b c 16 | first(f) { 17 | return this[Bifunctor.bimap](f, identity); 18 | } 19 | 20 | // second :: Bifunctor f => f a b ~> (b -> c) -> f a c 21 | second(f) { 22 | return this[Bifunctor.bimap](identity, f); 23 | } 24 | 25 | [Functor.map](f) { return this[Bifunctor.bimap](identity, f); } 26 | } 27 | 28 | const bimap = (f, g, bifunctor) => bifunctor[Bifunctor.bimap](f, g); 29 | const first = (f, bifunctor) => bifunctor[Bifunctor.first](f); 30 | const second = (f, bifunctor) => bifunctor[Bifunctor.second](f); 31 | 32 | export { 33 | Bifunctor, 34 | bimap, 35 | first, 36 | second, 37 | }; 38 | -------------------------------------------------------------------------------- /src/chain.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { protocol, implements } from 'sweet-interfaces'; 4 | import { Functor } from './functor'; 5 | import { Apply } from './apply'; 6 | import { Category } from './category'; 7 | 8 | protocol Chain extends Apply { 9 | // chain :: Chain m => m a ~> (a -> m b) -> m b 10 | chain(f) { 11 | return this[Chain.join]()[Functor.map](f); 12 | } 13 | 14 | // join :: Chain m => m (m a) ~> m a 15 | join() { 16 | return this[Chain.chain](Function[Category.id]); 17 | } 18 | 19 | [Apply.ap](m) { 20 | return m[Chain.chain](f => this[Functor.map](f)); 21 | } 22 | } 23 | 24 | Array.prototype[Chain.chain] = function chain(f) { 25 | const result = []; 26 | this.forEach(x => Array.prototype.push.apply(result, f(x))); 27 | return result; 28 | }; 29 | Reflect.implement(Array, Chain); 30 | 31 | Function.prototype[Chain.chain] = function chain(f) { 32 | return x => f(this(x))(x); 33 | }; 34 | Reflect.implement(Function, Chain); 35 | 36 | const chain = (f, ch) => ch[Chain.chain](f); 37 | const join = ch => ch[Chain.join](); 38 | 39 | export { 40 | Chain, 41 | chain, 42 | join 43 | }; 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Gabe Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fantasy-interfaces", 3 | "version": "0.0.1", 4 | "description": "JavaScript \"native\" interfaces for Fantasy Land", 5 | "main": "index.js", 6 | "scripts": { 7 | "prebuild": "mkdir -p tmp dist", 8 | "build": "for file in src/*; do sjs \"$file\" --out-dir tmp/; done && babel tmp/ --out-dir dist/", 9 | "postbuild": "rm -rf tmp", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/gabejohnson/fantasy-interfaces.git" 15 | }, 16 | "keywords": [ 17 | "interfaces", 18 | "macros", 19 | "sweet.js", 20 | "fantasy-land", 21 | "algebraic" 22 | ], 23 | "author": "Gabe Johnson ", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/gabejohnson/fantasy-interfaces/issues" 27 | }, 28 | "homepage": "https://github.com/gabejohnson/fantasy-interfaces#readme", 29 | "dependencies": { 30 | "@sweet-js/cli": "^3.0.13", 31 | "sweet-interfaces": "^0.4.0" 32 | }, 33 | "devDependencies": { 34 | "babel-cli": "^6.24.1", 35 | "babel-preset-env": "^1.6.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/extend.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { protocol, implements } from 'sweet-interfaces'; 4 | import { Functor } from './functor'; 5 | import { Category } from './category'; 6 | 7 | protocol Extend extends Functor { 8 | // extend :: Extend w => w a ~> (w a -> b) -> w b 9 | extend(f) { 10 | return this[Extend.duplicate]()[Functor.map](f); 11 | } 12 | 13 | // duplicate :: Extend w => w a ~> w (w a) 14 | duplicate() { 15 | return this[Extend.extend](Function[Category.id]); 16 | } 17 | } 18 | 19 | Array.prototype[Extend.extend] = function extend(f) { 20 | return this.map((_, idx, xs) => f(xs.slice(idx))); 21 | }; 22 | Array.prototype[Extend.duplicate] = function duplicate() { 23 | return this.map((_, idx, xs) => xs.slice(idx)); 24 | }; 25 | Reflect.implement(Array, Extend); 26 | 27 | Function.prototype[Extend.extend] = function extend(f) { 28 | return x => f(y => this(x.concat(y))); 29 | }; 30 | Function.prototype[Extend.duplicate] = function duplicate() { 31 | return x => y => this(x.concat(y)); 32 | }; 33 | Reflect.implement(Function, Extend); 34 | 35 | const extend = (f, ext) => ext[Extend.extend](f); 36 | const duplicate = ext => ext[Extend.duplicate](); 37 | 38 | export { 39 | Extend, 40 | extend, 41 | duplicate 42 | }; 43 | -------------------------------------------------------------------------------- /src/setoid.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | 5 | protocol Setoid { 6 | // equals :: Setoid a => a ~> a -> Boolean 7 | equals(b) { return this.valueOf() === b.valueOf(); } 8 | } 9 | 10 | Reflect.implement(Boolean, Setoid); 11 | 12 | Number.prototype[Setoid.equals] = function equals(b) { 13 | return this === b || isNaN(this) && isNaN(b); 14 | }; 15 | Reflect.implement(Number, Setoid); 16 | 17 | Reflect.implement(Date, Setoid); 18 | 19 | Reflect.implement(String, Setoid); 20 | 21 | Error.prototype[Setoid.equals] = function equals(b) { 22 | return this.name[equals](b.name) && this.message[equals](b.message); 23 | }; 24 | Reflect.implement(Error, Setoid); 25 | 26 | Array.prototype[Setoid.equals] = function equals(b) { 27 | if (b.length !== this.length) return false; 28 | for (var idx = 0; idx < this.length; idx += 1) { 29 | if (!this[idx][equals](b[idx])) return false; 30 | } 31 | return true; 32 | }; 33 | Reflect.implement(Array, Setoid); 34 | 35 | Object.prototype[Setoid.equals] = function equals(b) { 36 | const keys = Object.keys(this).sort(); 37 | return keys[equals](Object.keys(b).sort()) && 38 | keys.every(k => this[k][equals](other[k])); 39 | }; 40 | Reflect.implement(Object, Setoid); 41 | 42 | const equals = (a, b) => a[Setoid.equals](b); 43 | 44 | export { Setoid, equals }; 45 | -------------------------------------------------------------------------------- /src/chainRec.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Chain } from './chain'; 5 | 6 | protocol ChainRec extends Chain { 7 | // chainRec :: ChainRec m => ((a -> c, b -> c, a) -> m c, a) -> m b 8 | static chainRec; 9 | } 10 | 11 | const iterate = done => value => ({ value, done }); 12 | const cont = iterate(false); 13 | const stop = iterate(true); 14 | 15 | Array[ChainRec.chainRec] = function chainRec(f, x) { 16 | const remaining = [x]; 17 | const complete = []; 18 | while (remaining.length > 0) { 19 | const xs = f(cont, stop, remaining.shift()); 20 | const additional = []; 21 | let value, done; 22 | for (let idx = 0; idx < xs.length; idx += 1) { 23 | ({ value, done } = xs[idx]); 24 | (done ? complete : additional).push(value); 25 | } 26 | Array.prototype.unshift.apply(remaining, additional); 27 | } 28 | return complete; 29 | }; 30 | Reflect.implement(Array, ChainRec); 31 | 32 | Function[ChainRec.chainRec] = function chainRec(f, x) { 33 | return a => { 34 | let { value, done } = cont(x); 35 | while (!done) ({ value, done } = f(cont, stop, value)(a)); 36 | return value; 37 | }; 38 | }; 39 | Reflect.implement(Function, ChainRec); 40 | 41 | const chainRec = typeRep => (f, chain) => typeRep[ChainRec.chainRec](f, chain); 42 | 43 | export { ChainRec, chainRec }; 44 | -------------------------------------------------------------------------------- /src/foldable.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { protocol, implements } from 'sweet-interfaces'; 4 | 5 | const flip = f => (a, b) => f(b, a); 6 | 7 | protocol Foldable { 8 | // reduce :: Foldable f => f a ~> ((b, a) -> b, b) -> b 9 | reduce(f, init) { 10 | this[Foldable.reduceRight](flip(f), init); 11 | } 12 | 13 | // reduceRight :: Foldable f => f a ~> ((a, b) -> b, b) -> b 14 | reduceRight(f, init) { 15 | this[Foldable.reduce](flip(f), init); 16 | } 17 | 18 | // reduceMap :: Foldable f, Monoid m => f a ~> (TypeRep m, (a -> m)) -> m 19 | reduceMap(typeRep, f) { 20 | return this[Foldable.reduce]((acc, a) => f(a)[Semigroup.concat](acc), typeRep[Monoid.empty]()); 21 | } 22 | } 23 | 24 | Array.prototype[Foldable.reduce] = function reduce(f, init) { 25 | return this.reduce((acc, x) => f(acc, x), init); 26 | }; 27 | Reflect.implement(Array, Foldable); 28 | 29 | Object.prototype[Foldable.reduce] = function reduce(f, init) { 30 | return Object 31 | .keys(this) 32 | .sort() 33 | .reduce((acc, k) => f(acc, this[k]), init); 34 | }; 35 | Reflect.implement(Object, Foldable); 36 | 37 | const reduce = (f, init, foldable) => foldable[Foldable.reduce](f, init); 38 | const reduceRight = (f, init, foldable) => foldable[Foldable.reduceRight](f, init); 39 | const reduceMap = (typeRep, f, foldable) => foldable[Foldable.reduceMap](typeRep, f); 40 | 41 | export { 42 | Foldable 43 | reduce, 44 | reduceRight, 45 | reduceMap 46 | }; 47 | -------------------------------------------------------------------------------- /src/biapply.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Bifunctor } from './bifunctor'; 5 | import { id } from './category'; 6 | 7 | const identity = id(Function); 8 | const constant = k => () => k; 9 | 10 | protocol Biapply extends Bifunctor { 11 | // biap :: Biapply w => w a c ~> w (a -> b) (c -> d) -> w b d 12 | biap(w) { 13 | this.constructor[Biapply.bilift](identity, identity, w, this); 14 | } 15 | 16 | // bilift :: Biapply w => (a -> b -> ... -> c) -> (d -> e -> ... -> f) -> w a d -> w b e -> ... -> w c f 17 | static bilift(f, g, a, ...bs) { 18 | const result = a[Bifunctor.bimap](f, g); 19 | return bs.reduce((f, a) => a[Biapply.biap](f), result); 20 | } 21 | 22 | // biapFirst :: Biapply w => w a b ~> w c d -> w a b 23 | biapFirst(y) { 24 | this.constructor[Biapply.bilift](constant, constant, this, y); 25 | } 26 | 27 | // biapSecond :: Apply f => f a ~> f b -> f b 28 | biapSecond(y) { 29 | this.constructor[Biapply.bilift](constant(identity), constant(identity), this, y); 30 | } 31 | } 32 | 33 | const biap = (other, biapply) => biapply[Biapply.biap](other); 34 | const biapFirst = (other, biapply) => biapply[Biapply.biapFirst](other); 35 | const biapSecond = (other, biapply) => biapply[Biapply.biapSecond](other); 36 | const bilift = typeRep => (f, g, a, ...bs) => typeRep[Biapply.bilift](f, g, a, ...bs); 37 | export { 38 | Biapply, 39 | biap, 40 | biapFirst, 41 | biapSecond, 42 | bilift 43 | }; 44 | -------------------------------------------------------------------------------- /src/profunctor.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { protocol, implements } from 'sweet-interfaces'; 4 | import { Functor } from './functor'; 5 | import { id } from './category'; 6 | 7 | const identity = id(Function); 8 | 9 | protocol Profunctor extends Functor { 10 | // promap :: Profunctor p => p b c ~> (a -> b, c -> d) -> p a d 11 | promap(f, g) { 12 | return this[Profunctor.lmap](f)[Profunctor.rmap](g); 13 | } 14 | 15 | // lmap :: Profunctor p => p b c ~> (a -> b) -> p a c 16 | lmap(f) { 17 | return this[Profunctor.promap](f, identity); 18 | } 19 | 20 | // rmap :: Profunctor p => p a b ~> (b -> c) -> p a c 21 | rmap(f) { 22 | return this[Profunctor.promap](identity, f); 23 | } 24 | 25 | // arr :: (Category p, Profunctor p) => (a -> b) -> p a b 26 | static arr(f) { 27 | return f[Profunctor.rmap](identity); 28 | } 29 | 30 | [Functor.map](f) { 31 | return this[Profunctor.promap](identity, f); 32 | } 33 | } 34 | 35 | Function.prototype[Profunctor.promap] = function promap(f, g) { 36 | return x => g(this(f(x))); 37 | }; 38 | Function.prototype[Profunctor.lmap] = Function.prototype[Contravariant.contramap]; 39 | Function.prototype[Profunctor.rmap] = Function.prototype[Functor.map]; 40 | 41 | Reflect.implement(Function, Profunctor); 42 | 43 | const promap = (f, g, profunctor) => profunctor[Profunctor.promap](f, g); 44 | const lmap = (f, profunctor) => profunctor[Profunctor.lmap](f); 45 | const rmap = (f, profunctor) => profunctor[Profunctor.rmap](f); 46 | const arr = typeRep => f => typeRep[Profunctor.arr](f); 47 | 48 | export { 49 | Profunctor, 50 | promap, 51 | lmap, 52 | rmap, 53 | arr 54 | }; 55 | -------------------------------------------------------------------------------- /src/apply.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Functor } from './functor'; 5 | import { id } from './category'; 6 | 7 | const constant = k => () => k; 8 | 9 | protocol Apply extends Functor { 10 | // ap :: Apply f => f a ~> f (a -> b) -> f b 11 | ap(f) { 12 | this.constructor[Apply.lift](x => x, f, this); 13 | } 14 | 15 | // lift :: Apply f => (a -> b -> ... -> c) -> f a -> f b -> ... -> f c 16 | static lift(f, a, ...bs) { 17 | const result = a[Functor.map](f); 18 | return bs.reduce((f, a) => a[Apply.ap](f), result); 19 | } 20 | 21 | // apFirst :: Apply f => f a ~> f b -> f a 22 | apFirst(y) { 23 | this.constructor[Apply.lift](constant, this, y); 24 | } 25 | 26 | // apSecond :: Apply f => f a ~> f b -> f b 27 | apSecond(y) { 28 | this.constructor[Apply.lift](constant(id(Function)), this, y); 29 | } 30 | } 31 | 32 | Array.prototype[Apply.ap] = function ap(fs) { 33 | const result = []; 34 | for (let idx = 0; idx < fs.length; idx += 1) { 35 | for (let idx2 = 0; idx2 < this.length; idx2 += 1) { 36 | result.push(fs[idx](this[idx2])); 37 | } 38 | } 39 | return result; 40 | }; 41 | Reflect.implement(Array, Apply); 42 | 43 | Object.prototype[Apply.ap] = function ap(b) { 44 | var result = {}; 45 | for (let k in this) if (k in b) result[k] = b[k](this[k]); 46 | return result; 47 | }; 48 | Reflect.implement(Object, Apply); 49 | 50 | Function.prototype[Apply.ap] = function ap(f) { 51 | return x => f(x)(this(x)); 52 | }; 53 | Reflect.implement(Function, Apply); 54 | 55 | const ap = (f, other) => other[Apply.ap](f); 56 | const apFirst = (a, b) => b[Apply.apFirst](a); 57 | const apSecond = (a, b) => b[Apply.apSecond](a); 58 | const lift = typeRep => (f, a, ...bs) => typeRep[Apply.lift](f, a, ...bs); 59 | export { 60 | Apply, 61 | ap, 62 | apFirst, 63 | apSecond, 64 | lift 65 | }; 66 | -------------------------------------------------------------------------------- /src/traversable.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { protocol, implements } from 'sweet-interfaces'; 4 | import { Functor } from './functor'; 5 | import { Apply, lift } from './apply'; 6 | import { Applicative } from './applicative'; 7 | import { Foldable } from './foldable'; 8 | import { id } from './category'; 9 | 10 | const identity = id(Function); 11 | 12 | protocol Traversable extends Functor, Foldable { 13 | // traverse :: Applicative f, Traversable t => t a ~> (TypeRep f, a -> f b) -> f (t b) 14 | traverse(typeRep, f) { 15 | return this[Functor.map](f).sequence(typeRep); 16 | } 17 | 18 | // sequence :: Applicative f, Traversable t => t (f a) ~> TypeRep f -> f (t a) 19 | sequence(typeRep) { 20 | return this.traverse(typeRep, identity); 21 | } 22 | } 23 | 24 | const concat = a => b => a[Semigroup.concat](b); 25 | const pair = x => y => [x, y]; 26 | 27 | Array.prototype[Traversable.traverse] = function traverse(typeRep, f) { 28 | const xs = this; 29 | function go(idx, n) { 30 | switch (n) { 31 | case 0: return typeRep[Applicative.of]([]); 32 | case 2: return lift(typeRep, pair, f(xs[idx]), f(xs[idx + 1])); 33 | default: 34 | const m = Math.floor(n / 4) * 2; 35 | return lift(typeRep, concat, go(idx, m), go(idx + m, n - m)); 36 | } 37 | } 38 | return this.length % 2 === 1 ? 39 | lift(concat, f(this[0])[Functor.map](Array[Applicative.of]), go(1, this.length - 1)) : 40 | go(0, this.length); 41 | }; 42 | Reflect.implement(Array, Traversable); 43 | 44 | Object.prototype[Traversable.traverse] = function traverse(typeRep, f) { 45 | return Object 46 | .keys(this) 47 | .reduce((applicative, k) => lift(o => v => (o[k] = v, o), applicative, f(this[k])) 48 | , typeRep[Applicative.of]({})); 49 | }; 50 | Reflect.implement(Object, Traversable); 51 | 52 | const traverse = (typeRep, f, traversable) => traversable[Traversable.traverse](typeRep, f); 53 | const sequence = (typeRep, traversable) => traversable[Traversable.sequence](typeRep); 54 | 55 | export { 56 | Traversable, 57 | traverse, 58 | sequence 59 | }; 60 | -------------------------------------------------------------------------------- /src/ord.js: -------------------------------------------------------------------------------- 1 | 'lang sweet.js'; 2 | 3 | import { implements, protocol } from 'sweet-interfaces'; 4 | import { Setoid } from './setoid'; 5 | 6 | protocol Ord extends Setoid { 7 | // lte :: Ord a, b => a ~> b -> Boolean 8 | lte(b) { return this.valueOf() <= b.valueOf(); } 9 | 10 | // lt :: Ord a, b => a ~> b -> Boolean 11 | lt(b) { return this[Ord.lte](b) && !this[Setoid.equals](b); } 12 | 13 | // gt :: Ord a, b => a ~> b -> Boolean 14 | gt(b) { return !this[Ord.lte](b); } 15 | 16 | // gte :: Ord a, b => a ~> b -> Boolean 17 | gte(b) { return !this[Ord.lt](b); } 18 | 19 | [Setoid.equals](b) { 20 | return this[Ord.lte](b) && b[Ord.lte](this); 21 | } 22 | } 23 | 24 | Boolean.prototype[Ord.lte] = function lte(b) { 25 | return this === false || b === true; 26 | }; 27 | Reflect.implement(Boolean, Ord); 28 | 29 | Number.prototype[Ord.lte] = function lte(b) { 30 | return this <= b || isNaN(this) && isNaN(b); 31 | }; 32 | Reflect.implement(Number, Ord); 33 | 34 | Reflect.implement(Date, Ord); 35 | 36 | Reflect.implement(String, Ord); 37 | 38 | Error.prototype[Ord.lte] = function lte(b) { 39 | return this.name[lte](b.name) && this.message[lte](b.message); 40 | }; 41 | Reflect.implement(Error, Ord); 42 | 43 | Array.prototype[Ord.lte] = function lte(b) { 44 | for (let idx = 0; true; idx += 1) { 45 | if (idx === this.length) return true; 46 | if (idx === b.length) return false; 47 | if (this[idx][Setoid.equals](b[idx])) return this[idx][lte](b[idx]); 48 | } 49 | }; 50 | Reflect.implement(Array, Setoid); 51 | 52 | Object.prototype[Ord.lte] = function lte(b) { 53 | const theseKeys = Object.keys(this).sort(); 54 | const otherKeys = Object.keys(b).sort(); 55 | while (true) { 56 | if (theseKeys.length === 0) return true; 57 | if (otherKeys.length === 0) return false; 58 | let k = theseKeys.shift(); 59 | let z = otherKeys.shift(); 60 | if (k < z) return true; 61 | if (k > z) return false; 62 | if (!this[k][Setoid.equals](other[k])) return this[k][lte](b[k]); 63 | } 64 | }; 65 | Reflect.implement(Object, Ord); 66 | 67 | const lte = (a, b) => a[Ord.lte](b); 68 | const lt = (a, b) => a[Ord.lt](b); 69 | const gt = (a, b) => a[Ord.gt](b); 70 | const gte = (a, b) => a[Ord.gte](b); 71 | 72 | export { 73 | Ord, 74 | lte, 75 | lt, 76 | gt, 77 | gte 78 | }; 79 | --------------------------------------------------------------------------------