mezzanine
Version:
Fantasy land union types with pattern matching
363 lines (272 loc) • 10.8 kB
JavaScript
function _objectWithoutProperties(obj, keys) {var target = {};for (var i in obj) {if (keys.indexOf(i) >= 0) continue;if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;target[i] = obj[i];}return target;}
/**
* Union of types
*
* @module mezzanine/union
*/
import { zip, difference, isEmpty, values as getValues } from 'ramda';
import { /*Type, */typeContainer } from '../type';
import isOrthogonal from '../ortho';
import { rename } from '../decorators';
import { typeMark } from '../config';
import { isMezzanine } from '../type/fixtures';
import { addProperties } from '../utils/props';
import '../type/type-container';
import { curry2 } from '../utils/fp';
import { append } from '../utils/list';
import '../utils/props';
import { getInitialValue, applyStack } from '../virtual-stack';
/* eslint-disable */
//$FlowIssue
var staticFantasy = addProperties({
stackUpdate: Ctx =>
newStack => {
//$FlowIssue
var { typeName, desc, func } = Ctx;
return Union([typeName])(desc, func, newStack);
},
contramap: Ctx =>
function contramap(prependFunction) {
//$FlowIssue
var newStack = append(prependFunction, Ctx.stack);
//$FlowIssue
var NewRecord = Ctx.stackUpdate(newStack);
return NewRecord;
} });
var instanceFantasy = addProperties({
map: (ctx, Ctx) =>
function map(mapFunction) {
return Ctx(ctx.chain(mapFunction));
},
chain: (ctx, Ctx) =>
function chain(chainFunction) {
return chainFunction(ctx.toJSON());
} });
var instProps = addProperties({
case: (ctx, Ctx) =>
cases =>
Ctx.case(cases, ctx.value) });
var subclassReference = addProperties({
keys: (_, child) => child.keys,
isMono: (_, child) => child.isMono,
toJSON(_, child) {
if (typeof child.toJSON === 'function')
return child.toJSON();
return _.toJSON();
} });
function unrelevantCaseError(union, diff, data) {
console.error(union);
console.error(data);
throw new Error(`Unrelevant case types ${diff.toString()} on type ${union.typeName}`);
}
function caseSelector(
union,
subtypesMap,
uniqMark,
cases,
subtype) {
var {
_ = defaultCase } =
cases,realCases = _objectWithoutProperties(cases, ['_']);
var diff = difference(Object.keys(realCases), union.keys);
if (!isEmpty(diff))
unrelevantCaseError(union, diff, subtype);
if (isMezzanine(subtype) && subtype.ಠ_ಠ === uniqMark) {
var currentCase = realCases[subtype.type];
// console.log(currentCase)
if (typeof currentCase === 'function')
return currentCase(subtype.toJSON(), union);else
return _(subtype);
}
for (var _iterator = Object.keys(realCases), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {var _ref;if (_isArray) {if (_i >= _iterator.length) break;_ref = _iterator[_i++];} else {_i = _iterator.next();if (_i.done) break;_ref = _i.value;}var variant = _ref;
var childType = subtypesMap[variant];
var _currentCase = realCases[variant];
if (childType.is(subtype)) {
var finalValue = childType.ಠ_ಠ === subtype.ಠ_ಠ ?
subtype :
childType(subtype);
return _currentCase(finalValue.toJSON(), union);
}
}
return _(subtype);
}
//$FlowIssue
var caseFactory = (union, subtypesMap, uniqMark) =>
(cases, subtype) =>
caseSelector(union, subtypesMap, uniqMark, cases, subtype);
/**
* Make type union
*
* @param {string[]} [typeName]
* @returns
*
* @example
* Union`User`({ Account: String, Guest: {} })
*/
function Union([typeName]) {
return function UnionFabric(
desc,
funcBlob = {},
stack = [])
{
var canMatch = isOrthogonal(desc);
var uniqMark = Symbol(typeName);
var keys = Object.keys(desc);
var values = getValues(desc);
var subtypes = zip(keys, values);
var subtypesMap = {};
for (var _iterator2 = subtypes, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {var _ref2;if (_isArray2) {if (_i2 >= _iterator2.length) break;_ref2 = _iterator2[_i2++];} else {_i2 = _iterator2.next();if (_i2.done) break;_ref2 = _i2.value;}var [_key4, arg] = _ref2;
subtypesMap[_key4] = rename(_key4)(typeContainer(_key4, typeName, arg, {}));}
var caseWith = caseFactory(UnionClass, subtypesMap, uniqMark);
function* iterator() {
for (var _iterator3 = keys, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {var _ref3;if (_isArray3) {if (_i3 >= _iterator3.length) break;_ref3 = _iterator3[_i3++];} else {_i3 = _iterator3.next();if (_i3.done) break;_ref3 = _i3.value;}var _key = _ref3;
yield [_key, subtypesMap[_key]];}
}
var needTransform = stack.length !== 0;
function is(obj) {
var val = obj;
if (needTransform === true) {
var initial = getInitialValue(stack, val);
if (initial.succ === false) return false;
val = initial.val;
}
for (var _iterator4 = iterator(), _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) {var _ref4;if (_isArray4) {if (_i4 >= _iterator4.length) break;_ref4 = _iterator4[_i4++];} else {_i4 = _iterator4.next();if (_i4.done) break;_ref4 = _i4.value;}var [_key2, pattern] = _ref4;
if (pattern.is) {
if (pattern.is(val)) return true;
} else if (typeof pattern === 'function' && pattern(val))
return true;
}
return false;
}
var staticProps = addProperties({
funcs: {
value: funcBlob,
enumerable: false },
desc: {
value: desc,
enumerable: false },
name: {
value: typeName,
enumerable: false },
case: {
value: curry2(caseWith),
enumerable: false },
stack: {
value: stack,
enumerable: false } });
var mainProps = addProperties({
ಠ_ಠ: {
value: uniqMark,
enumerable: false },
//$FlowIssue
[typeMark]: {
get: () => true,
enumerable: false },
//$ FlowIssue
[Symbol.iterator]: () => iterator,
is: {
value: () => is,
inject: true,
enumerable: true },
typeName: {
value: typeName,
enumerable: true },
canMatch: {
value: canMatch,
enumerable: true },
types: {
value: keys,
enumerable: true } });
var userMeth = addProperties(funcBlob);
//$FlowIssue
function UnionClass(obj) {
//$FlowIssue
if (new.target !== UnionClass)
return new UnionClass(obj);
var arg = applyStack(stack, obj);
var data = arg && arg.value ?
arg.value :
arg;
var matched = false;
for (var _iterator5 = iterator(), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) {var _ref5;if (_isArray5) {if (_i5 >= _iterator5.length) break;_ref5 = _iterator5[_i5++];} else {_i5 = _iterator5.next();if (_i5.done) break;_ref5 = _i5.value;}var [_key3, pattern] = _ref5;
if (pattern.is(data)) {
var fin = pattern(data);
Object.assign(this, fin);
if (fin.isMono)
this.value = fin.value;
subclassReference(this, fin);
matched = true;
break;
}
}
if (matched === false)
unmatchError(UnionClass, data);
instProps(this, UnionClass);
mainProps(this, UnionClass);
instanceFantasy(this, UnionClass);
userMeth(this, UnionClass);
}
staticProps(UnionClass, UnionClass);
mainProps(UnionClass, UnionClass);
staticFantasy(UnionClass, UnionClass);
Object.assign(UnionClass, subtypesMap);
// console.log(UnionClass)
return UnionClass;
};
}
//$FlowIssue
function unmatchError(union, data) {
console.error(union);
console.error(data);
throw new TypeError('Unmatched pattern');
}
var defaultCase = instance => {
throw new Error(`Unmatched case on union ${instance.typeName}`);
};
export default Union;
// const Boy = Type`Boy`({
// name: String,
// alive: Boolean
// }, {
// mutableKill: (ctx) => () => { ctx.alive = false }
// })
// mutable function
// const Fred = Boy({ name: 'Fred', alive: true })
// console.log(Fred)
// Fred.mutableKill()
// console.log(Fred)
// const Brothers = Type`Brothers`({
// elder : Boy,
// younger: Boy
// })
// const Childs = Union`Childs`({
// Single: Boy,
// Couple: Brothers,
// })
// const rawData = { name: 'Fred' }
// // Fred.equals(Boy({ name: 'Fred', alive: true })); /*?*/
// // Fred.equals(rawData) /*?*/
// //transforms
// // Fred.map(({ name }) => ({ name: name+'dy' }))/*?*/
// deep patterns
// const family1 = Childs({ name: 'John', alive: true })
// const family2 = Childs({
// elder : Fred,
// younger: { name: 'Bob', alive: true }
// })
// console.log(family2)
// family2 /*?*/
// const onlyOne = family2.map((data) =>( console.log(data), data, family2));
// onlyOne
// family1.type; /*?*/
// family2.type; /*?*/
// onlyOne.type; /*?*/
// [...family1].map(([key,value])=>key)/*?*/
// //pattern-switch
// const actions = {
// Single:(child) => child.name,
// Couple:({ elder, younger }) => [elder.name, younger.name]
// }
// console.log(family2.case(actions))
// console.log(family1.case(actions))
//# sourceMappingURL=union.js.map