UNPKG

mezzanine

Version:

Fantasy land union types with pattern matching

363 lines (272 loc) 10.8 kB
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