var assert = require('./assert'); var isTypeName = require('./isTypeName'); var isFunction = require('./isFunction'); var getTypeName = require('./getTypeName'); var isIdentity = require('./isIdentity'); var isObject = require('./isObject'); var create = require('./create'); var is = require('./is'); function getDefaultName(domain, codomain) { return '{[key: ' + getTypeName(domain) + ']: ' + getTypeName(codomain) + '}'; } function dict(domain, codomain, name) { if (process.env.NODE_ENV !== 'production') { assert(isFunction(domain), function () { return 'Invalid argument domain ' + assert.stringify(domain) + ' supplied to dict(domain, codomain, [name]) combinator (expected a type)'; }); assert(isFunction(codomain), function () { return 'Invalid argument codomain ' + assert.stringify(codomain) + ' supplied to dict(domain, codomain, [name]) combinator (expected a type)'; }); assert(isTypeName(name), function () { return 'Invalid argument name ' + assert.stringify(name) + ' supplied to dict(domain, codomain, [name]) combinator (expected a string)'; }); } var displayName = name || getDefaultName(domain, codomain); var domainNameCache = getTypeName(domain); var codomainNameCache = getTypeName(codomain); var identity = isIdentity(domain) && isIdentity(codomain); function Dict(value, path) { if (process.env.NODE_ENV === 'production') { if (identity) { return value; // just trust the input if elements must not be hydrated } } if (process.env.NODE_ENV !== 'production') { path = path || [displayName]; assert(isObject(value), function () { return 'Invalid value ' + assert.stringify(value) + ' supplied to ' + path.join('/'); }); } var idempotent = true; // will remain true if I can reutilise the input var ret = {}; // make a temporary copy, will be discarded if idempotent remains true for (var k in value) { if (value.hasOwnProperty(k)) { k = create(domain, k, ( process.env.NODE_ENV !== 'production' ? path.concat(domainNameCache) : null )); var actual = value[k]; var instance = create(codomain, actual, ( process.env.NODE_ENV !== 'production' ? path.concat(k + ': ' + codomainNameCache) : null )); idempotent = idempotent && ( actual === instance ); ret[k] = instance; } } if (idempotent) { // implements idempotency ret = value; } if (process.env.NODE_ENV !== 'production') { Object.freeze(ret); } return ret; } Dict.meta = { kind: 'dict', domain: domain, codomain: codomain, name: name, identity: identity }; Dict.displayName = displayName; Dict.is = function (x) { if (!isObject(x)) { return false; } for (var k in x) { if (x.hasOwnProperty(k)) { if (!is(k, domain) || !is(x[k], codomain)) { return false; } } } return true; }; Dict.update = function (instance, patch) { return Dict(assert.update(instance, patch)); }; return Dict; } dict.getDefaultName = getDefaultName; module.exports = dict;