UNPKG

useless

Version:

Use Less. Do More. JavaScript on steroids.

145 lines (106 loc) 6.91 kB
"use strict"; const _ = require ('underscore') _.hasTypeMatch = true /* Type matching for arbitrary complex structures (TODO: test) ======================================================================== */ Meta.globalTag ('required') Meta.globalTag ('atom') $global.const ('$any', _.identity) _.deferTest (['type', 'type matching'], function () { $assert (_.omitTypeMismatches ( { '*': $any, foo: $required ('number'), bar: $required ('number') }, { baz: 'x', foo: 42, bar: 'foo' }), { }) $assert (_.omitTypeMismatches ( { foo: { '*': $any } }, { foo: { bar: 42, baz: 'qux' } }), { foo: { bar: 42, baz: 'qux' } }) $assert (_.omitTypeMismatches ( { foo: { bar: $required(42), '*': $any } }, { foo: { bar: 'foo', baz: 'qux' } }), { }) $assert (_.omitTypeMismatches ( [{ foo: $required ('number'), bar: 'number' }], [{ foo: 42, bar: 42 }, { foo: 24, }, { bar: 42 }]), [{ foo: 42, bar: 42 }, { foo: 24 }]) $assert (_.omitTypeMismatches ({ '*': 'number' }, { foo: 42, bar: 42 }), { foo: 42, bar: 42 }) $assert (_.omitTypeMismatches ({ foo: $any }, { foo: 0 }), { foo: 0 }) // there was a bug (any zero value was omitted) $assert (_.decideType ([]), []) $assert (_.decideType (42), 'number') $assert (_.decideType (_.identity), 'function') $assert (_.decideType ([{ foo: 1 }, { foo: 2 }]), [{ foo: 'number' }]) $assert (_.decideType ([{ foo: 1 }, { bar: 2 }]), []) $assert (_.decideType ( { foo: { bar: 1 }, foo: { baz: [] } }), { foo: { bar: 'number' }, foo: { baz: [] } }) $assert (_.decideType ( { foo: { bar: 1 }, foo: { bar: 2 } }), { foo: { bar: 'number' } }) $assert (_.decideType ( { foo: { bar: 1 }, bar: { bar: 2 } }), { '*': { bar: 'number' } }) if (_.hasOOP) { var Type = $prototype () $assert (_.decideType ({ x: new Type () }), { x: Type }) } }, function () { _.isMeta = function (x) { return (x === $any) || $atom.is (x) || $required.is (x) } var zip = function (type, value, pred) { var required = Meta.unwrapAll (_.filter2 (type, $required.is)) var match = _.nonempty (_.zip2 (Meta.unwrapAll (type), value, pred)) if (_.isEmpty (required)) { return match } else { var requiredMatch = _.nonempty (_.zip2 (required, value, pred)) var allSatisfied = _.values2 (required).length === _.values2 (requiredMatch).length return allSatisfied ? match : _.coerceToEmpty (value) } } var hyperMatch = _.hyperOperator (_.binary, function (type_, value, pred) { var type = Meta.unwrap (type_) if (_.isArray (type)) { // matches [ItemType] → [item, item, ..., N] if (_.isArray (value)) { return zip (_.times (value.length, _.constant (type[0])), value, pred) } else { return undefined } } else if (_.isStrictlyObject (type) && type['*']) { // matches { *: .. } → { a: .., b: .., c: .. } if (_.isStrictlyObject (value)) { return zip (_.extend ( _.map2 (value, _.constant (type['*'])), _.omit (type, '*')), value, pred) } else { return undefined } } else { return zip (type_, value, pred) } }) var typeMatchesValue = function (c, v) { var contract = Meta.unwrap (c) return (contract === $any) || ((contract === undefined) && (v === undefined)) || (_.isFunction (contract) && ( _.isPrototypeConstructor (contract) ? _.isTypeOf (contract, v) : // constructor type (contract (v) === true))) || // test predicate (typeof v === contract) || // plain JS type (v === contract) } // constant match _.mismatches = function (op, contract, value) { return hyperMatch (contract, value, function (contract, v) { return op (contract, v) ? undefined : contract }) } _.omitMismatches = function (op, contract, value) { return hyperMatch (contract, value, function (contract, v) { return op (contract, v) ? v : undefined }) } _.typeMismatches = _.partial (_.mismatches, typeMatchesValue) _.omitTypeMismatches = _.partial (_.omitMismatches, typeMatchesValue) _.valueMismatches = _.partial (_.mismatches, function (a, b) { return (a === $any) || (b === $any) || (a === b) }) var unifyType = function (value) { if (_.isArray (value)) { return _.nonempty ([_.reduce (value.slice (1), function (a, b) { return _.undiff (a, b) }, _.first (value) || undefined)]) } else if (_.isStrictlyObject (value)) { var pairs = _.pairs (value) var unite = _.map ( _.reduce (pairs.slice (1), function (a, b) { return _.undiff (a, b) }, _.first (pairs) || [undefined, undefined]), _.nonempty) return (_.isEmpty (unite) || _.isEmpty (unite[1])) ? value : _.fromPairs ([[unite[0] || '*', unite[1]]]) } else { return value } } _.decideType = function (value) { var operator = _.hyperOperator (_.unary, function (value, pred) { if (value && value.constructor && value.constructor.$definition) { return value.constructor } return unifyType (_.map2 (value, pred)) }) return operator (value, function (value) { if (_.isPrototypeInstance (value)) { return value.constructor } else { return _.isEmptyArray (value) ? value : (typeof value) } }) } }) // TODO: fix hyperOperator to remove additional check for []