-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from fantasyland/davidchambers/es5
support AMD, CommonJS, and direct browser use without transpilation
- Loading branch information
Showing
2 changed files
with
167 additions
and
149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,14 +20,15 @@ | |
"cli-table": "0.3.1", | ||
"colors": "1.1.2", | ||
"daggy": "1.0.0", | ||
"eslint": "3.19.x", | ||
"fantasy-combinators": "0.0.x", | ||
"sanctuary-style": "0.5.x", | ||
"standard": "8.6.0", | ||
"tap": "10.0.0", | ||
"xyz": "2.1.x" | ||
}, | ||
"scripts": { | ||
"lint": "standard bench/*.js src/*.js test/*.js", | ||
"lint:fix": "npm run lint -- --fix", | ||
"lint": "eslint --config node_modules/sanctuary-style/eslint-es3.json --env es3 --global define --global module --global require --global self -- src/daggy.js && standard bench/*.js test/*.js", | ||
"release-major": "xyz --repo [email protected]:fantasyland/daggy.git --increment major", | ||
"release-minor": "xyz --repo [email protected]:fantasyland/daggy.git --increment minor", | ||
"release-patch": "xyz --repo [email protected]:fantasyland/daggy.git --increment patch", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,157 +1,174 @@ | ||
const { toString } = require('sanctuary-type-classes') | ||
const type = require('sanctuary-type-identifiers') | ||
|
||
// Names of prop used to store: | ||
// * name of variant of a sum type | ||
const TAG = '@@tag' | ||
// * array of arguments used to create a value (to speed up `cata`) | ||
const VALUES = '@@values' | ||
// * `@@type` of it's returned results | ||
const TYPE = '@@type' | ||
// * `@@type` of variant constructor's returned results | ||
const RET_TYPE = '@@ret_type' | ||
|
||
const tagged = (typeName, fields) => { | ||
const proto = {} | ||
proto.toString = tagged$toString | ||
// this way we avoid named function | ||
const typeRep = makeConstructor(fields, proto) | ||
typeRep.toString = typeRepToString | ||
typeRep.prototype = proto | ||
typeRep.is = isType | ||
typeRep[TYPE] = typeName | ||
proto.constructor = typeRep | ||
return typeRep | ||
} | ||
|
||
const taggedSum = (typeName, constructors) => { | ||
const proto = {} | ||
proto.cata = sum$cata | ||
proto.toString = sum$toString | ||
const typeRep = { | ||
toString: typeRepToString, | ||
prototype: proto, | ||
is: isType, | ||
[TYPE]: typeName | ||
(function(f) { | ||
|
||
'use strict'; | ||
|
||
if (typeof module === 'object' && typeof module.exports === 'object') { | ||
module.exports = f(require('sanctuary-type-classes'), | ||
require('sanctuary-type-identifiers')); | ||
} else if (typeof define === 'function' && define.amd != null) { | ||
define(['sanctuary-type-classes', | ||
'sanctuary-type-identifiers'], | ||
f); | ||
} else { | ||
self.daggy = f(self.sanctuaryTypeClasses, | ||
self.sanctuaryTypeIdentifiers); | ||
} | ||
proto.constructor = typeRep | ||
Object.keys(constructors).forEach(tag => { | ||
const fields = constructors[tag] | ||
const tagProto = Object.create(proto) | ||
defProp(tagProto, TAG, tag) | ||
if (fields.length === 0) { | ||
typeRep[tag] = makeValue(fields, tagProto, [], 0) | ||
typeRep[tag].is = sum$isUnit | ||
return | ||
|
||
}(function(Z, type) { | ||
|
||
'use strict'; | ||
|
||
// Names of prop used to store: | ||
// * name of variant of a sum type | ||
var TAG = '@@tag'; | ||
// * array of arguments used to create a value (to speed up `cata`) | ||
var VALUES = '@@values'; | ||
// * `@@type` of its returned results | ||
var TYPE = '@@type'; | ||
// * `@@type` of variant constructor's returned results | ||
var RET_TYPE = '@@ret_type'; | ||
|
||
function tagged(typeName, fields) { | ||
var proto = {toString: tagged$toString}; | ||
// this way we avoid named function | ||
var typeRep = makeConstructor(fields, proto); | ||
typeRep.toString = typeRepToString; | ||
typeRep.prototype = proto; | ||
typeRep.is = isType; | ||
typeRep[TYPE] = typeName; | ||
proto.constructor = typeRep; | ||
return typeRep; | ||
} | ||
|
||
function taggedSum(typeName, constructors) { | ||
var proto = {cata: sum$cata, toString: sum$toString}; | ||
var typeRep = proto.constructor = { | ||
toString: typeRepToString, | ||
prototype: proto, | ||
is: isType, | ||
'@@type': typeName | ||
}; | ||
Object.keys(constructors).forEach(function(tag) { | ||
var fields = constructors[tag]; | ||
var tagProto = Object.create(proto); | ||
defProp(tagProto, TAG, tag); | ||
if (fields.length === 0) { | ||
typeRep[tag] = makeValue(fields, tagProto, [], 0); | ||
typeRep[tag].is = sum$isUnit; | ||
return; | ||
} | ||
typeRep[tag] = makeConstructor(fields, tagProto); | ||
typeRep[tag].is = sum$isVariant; | ||
typeRep[tag][TAG] = tag; | ||
typeRep[tag][RET_TYPE] = typeName; | ||
typeRep[tag].toString = sum$ctrToString; | ||
}); | ||
return typeRep; | ||
} | ||
|
||
function sum$cata(fs) { | ||
var tag = this[TAG]; | ||
if (!fs[tag]) { | ||
throw new TypeError("Constructors given to cata didn't include: " + tag); | ||
} | ||
typeRep[tag] = makeConstructor(fields, tagProto) | ||
typeRep[tag].is = sum$isVariant | ||
typeRep[tag][TAG] = tag | ||
typeRep[tag][RET_TYPE] = typeName | ||
typeRep[tag].toString = sum$ctrToString | ||
}) | ||
return typeRep | ||
} | ||
|
||
const sum$cata = function (fs) { | ||
const tag = this[TAG] | ||
if (!fs[tag]) { | ||
throw new TypeError("Constructors given to cata didn't include: " + tag) | ||
return fs[tag].apply(fs, this[VALUES]); | ||
} | ||
|
||
function sum$ctrToString() { | ||
return this[RET_TYPE] + '.' + this[TAG]; | ||
} | ||
|
||
function sum$toString() { | ||
return this.constructor[TYPE] + '.' + | ||
this[TAG] + arrToString(this[VALUES]); | ||
} | ||
|
||
function typeRepToString() { | ||
return this[TYPE]; | ||
} | ||
|
||
function tagged$toString() { | ||
return this.constructor[TYPE] + arrToString(this[VALUES]); | ||
} | ||
return fs[tag].apply(fs, this[VALUES]) | ||
} | ||
|
||
const sum$ctrToString = function () { | ||
return `${this[RET_TYPE]}.${this[TAG]}` | ||
} | ||
|
||
const sum$toString = function () { | ||
return `${this.constructor[TYPE]}.${this[TAG]}${arrToString(this[VALUES])}` | ||
} | ||
|
||
const typeRepToString = function () { | ||
return this[TYPE] | ||
} | ||
|
||
const tagged$toString = function () { | ||
return `${this.constructor[TYPE]}${arrToString(this[VALUES])}` | ||
} | ||
|
||
const sum$isVariant = function (val) { | ||
return Boolean(val) && | ||
this[TAG] === val[TAG] && | ||
this[RET_TYPE] === type(val) | ||
} | ||
|
||
const sum$isUnit = function (val) { | ||
return this === val || Boolean(val) && | ||
this[TAG] === val[TAG] && | ||
type(this) === type(val) | ||
} | ||
|
||
const isType = function (val) { | ||
return this[TYPE] === type(val) | ||
} | ||
|
||
const makeValue = (fields, proto, values, argumentsLength) => { | ||
if (argumentsLength !== fields.length) { | ||
throw new TypeError(`Expected ${fields.length} arguments, got ${argumentsLength}`) | ||
|
||
function sum$isVariant(val) { | ||
return Boolean(val) && | ||
this[TAG] === val[TAG] && | ||
this[RET_TYPE] === type(val); | ||
} | ||
|
||
function sum$isUnit(val) { | ||
return this === val || Boolean(val) && | ||
this[TAG] === val[TAG] && | ||
type(this) === type(val); | ||
} | ||
const obj = Object.create(proto) | ||
defProp(obj, VALUES, values) | ||
for (let idx = 0; idx < fields.length; idx++) { | ||
obj[fields[idx]] = values[idx] | ||
|
||
function isType(val) { | ||
return this[TYPE] === type(val); | ||
} | ||
return obj | ||
} | ||
|
||
// adopted version of withValue from https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty | ||
const defProp = (obj, prop, val) => { | ||
var desc = defProp.desc || ( | ||
defProp.desc = { | ||
enumerable: false, | ||
writable: false, | ||
configurable: false, | ||
value: null | ||
|
||
function makeValue(fields, proto, values, argumentsLength) { | ||
if (argumentsLength !== fields.length) { | ||
throw new TypeError( | ||
'Expected ' + fields.length + ' arguments, got ' + argumentsLength | ||
); | ||
} | ||
var obj = Object.create(proto); | ||
defProp(obj, VALUES, values); | ||
for (var idx = 0; idx < fields.length; idx += 1) { | ||
obj[fields[idx]] = values[idx]; | ||
} | ||
) | ||
desc.value = val | ||
Object.defineProperty(obj, prop, desc) | ||
} | ||
|
||
// optimised version of `arr.map(toString).join(', ')` | ||
const arrToString = (arr) => { | ||
if (arr.length === 0) { | ||
return '' | ||
return obj; | ||
} | ||
let str = '(' + toString(arr[0]) | ||
for (var i = 1; i < arr.length; i++) { | ||
str = str + ', ' + toString(arr[i]) | ||
|
||
// adopted version of withValue from https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty | ||
function defProp(obj, prop, val) { | ||
var desc = defProp.desc || ( | ||
defProp.desc = { | ||
enumerable: false, | ||
writable: false, | ||
configurable: false, | ||
value: null | ||
} | ||
); | ||
desc.value = val; | ||
Object.defineProperty(obj, prop, desc); | ||
} | ||
return str + ')' | ||
} | ||
|
||
const makeConstructor = (fields, proto) => { | ||
switch (fields.length) { | ||
case 1: return function (a) { return makeValue(fields, proto, [a], arguments.length) } | ||
case 2: return function (a, b) { return makeValue(fields, proto, [a, b], arguments.length) } | ||
case 3: return function (a, b, c) { return makeValue(fields, proto, [a, b, c], arguments.length) } | ||
case 4: return function (a, b, c, d) { return makeValue(fields, proto, [a, b, c, d], arguments.length) } | ||
case 5: return function (a, b, c, d, e) { return makeValue(fields, proto, [a, b, c, d, e], arguments.length) } | ||
case 6: return function (a, b, c, d, e, f) { return makeValue(fields, proto, [a, b, c, d, e, f], arguments.length) } | ||
case 7: return function (a, b, c, d, e, f, g) { return makeValue(fields, proto, [a, b, c, d, e, f, g], arguments.length) } | ||
case 8: return function (a, b, c, d, e, f, g, h) { return makeValue(fields, proto, [a, b, c, d, e, f, g, h], arguments.length) } | ||
case 9: return function (a, b, c, d, e, f, g, h, i) { return makeValue(fields, proto, [a, b, c, d, e, f, g, h, i], arguments.length) } | ||
case 10: return function (a, b, c, d, e, f, g, h, i, j) { return makeValue(fields, proto, [a, b, c, d, e, f, g, h, i, j], arguments.length) } | ||
default: return Object.defineProperty( | ||
function () { return makeValue(fields, proto, arguments, arguments.length) }, | ||
'length', | ||
{ value: fields.length } | ||
) | ||
|
||
// optimised version of `arr.map(toString).join(', ')` | ||
function arrToString(arr) { | ||
if (arr.length === 0) return ''; | ||
var str = '(' + Z.toString(arr[0]); | ||
for (var idx = 1; idx < arr.length; idx += 1) { | ||
str = str + ', ' + Z.toString(arr[idx]); | ||
} | ||
return str + ')'; | ||
} | ||
|
||
function makeConstructor(fields, proto) { | ||
switch (fields.length) { | ||
/* eslint-disable max-len */ | ||
case 1: return function(a) { return makeValue(fields, proto, [a], arguments.length); }; | ||
case 2: return function(a, b) { return makeValue(fields, proto, [a, b], arguments.length); }; | ||
case 3: return function(a, b, c) { return makeValue(fields, proto, [a, b, c], arguments.length); }; | ||
case 4: return function(a, b, c, d) { return makeValue(fields, proto, [a, b, c, d], arguments.length); }; | ||
case 5: return function(a, b, c, d, e) { return makeValue(fields, proto, [a, b, c, d, e], arguments.length); }; | ||
case 6: return function(a, b, c, d, e, f) { return makeValue(fields, proto, [a, b, c, d, e, f], arguments.length); }; | ||
case 7: return function(a, b, c, d, e, f, g) { return makeValue(fields, proto, [a, b, c, d, e, f, g], arguments.length); }; | ||
case 8: return function(a, b, c, d, e, f, g, h) { return makeValue(fields, proto, [a, b, c, d, e, f, g, h], arguments.length); }; | ||
case 9: return function(a, b, c, d, e, f, g, h, i) { return makeValue(fields, proto, [a, b, c, d, e, f, g, h, i], arguments.length); }; | ||
case 10: return function(a, b, c, d, e, f, g, h, i, j) { return makeValue(fields, proto, [a, b, c, d, e, f, g, h, i, j], arguments.length); }; | ||
/* eslint-enable max-len */ | ||
default: return Object.defineProperty( | ||
function() { | ||
return makeValue(fields, proto, arguments, arguments.length); | ||
}, | ||
'length', | ||
{value: fields.length} | ||
); | ||
} | ||
} | ||
} | ||
|
||
module.exports = { | ||
taggedSum, | ||
tagged | ||
} | ||
return {tagged: tagged, taggedSum: taggedSum}; | ||
|
||
})); |