function makeProduct (name, fields = [], associatedSumType = makeSum([]), methods = {}) {
const construct = (...args) => {
const constructedObj = typeof args[0] === 'object' &&
fieldsListsEqual(Object.keys(args[0]), fields)
? args[0]
: fields.reduce((prod, field, i) => {
prod[field] = args[i];
return prod;
}, {});
let instance = {
tag: name,
...constructedObj
};
let methodDescriptors = {};
for (let methodName in methods) {
const methodDesc = methods[methodName];
const method = methodDesc.method(associatedSumType);
const finalMethodForArity = {
0: () =>
method(instance),
1: (...args0) =>
method(...args0)(instance),
2: (...args0) => (...args1) =>
method(...args0)(...args1)(instance),
3: (...args0) => (...args1) => (...args2) =>
method(...args0)(...args1)(...args2)(instance),
4: (...args0) => (...args1) => (...args2) => (...args3) =>
method(...args0)(...args1)(...args2)(...args3)(instance),
5: (...args0) => (...args1) => (...args2) => (...args3) => (...args4) =>
method(...args0)(...args1)(...args2)(...args3)(...args4)(instance)
};
const params = methodDesc.arityOrParams;
const finalMethodForParams = {
0: () =>
`() => method(instance)`,
1: () =>
`(...${params[0]}) => method(...${params[0]})(instance)`,
2: () =>
`(...${params[0]}) => (...${params[1]}) => method(...${params[0]})(...${params[1]})(instance)`,
3: () =>
`(...${params[0]}) => (...${params[1]}) => (...${params[2]}) => method(...${params[0]})(...${params[1]})(...${params[2]})(instance)`,
4: () =>
`(...${params[0]}) => (...${params[1]}) => (...${params[2]}) => (...${params[3]}) => method(...${params[0]})(...${params[1]})(...${params[2]})(...${params[3]})(instance)`,
5: () =>
`(...${params[0]}) => (...${params[1]}) => (...${params[2]}) => (...${params[3]}) => (...${params[4]}) => method(...${params[0]})(...${params[1]})(...${params[2]})(...${params[3]})(...${params[4]})(instance)`
};
methodDescriptors[methodName] = {
enumerable: false,
value: typeof methodDesc.arityOrParams === 'number'
? finalMethodForArity[methodDesc.arityOrParams]
: eval(finalMethodForParams[methodDesc.arityOrParams.length]())
};
}
const match = typeOps.match(associatedSumType.variants);
console.log(methodDescriptors);
Object.defineProperties(instance, {
match: { enumerable: false,
value: (matchObj) => {
return match(matchObj)(instance);
}
},
unsafeFast_match: { enumerable: false,
value: (matchObj) => {
return matchObj[instance.tag](instance)
}},
variants: { enumerable: false,
value: associatedSumType.variants},
instanceId: { enumerable: false,
value: DOM.uid('instance_of_product_type_' + name)},
...methodDescriptors
});
return Object.freeze(instance);
};
const makeProductInstance = construct;
Object.defineProperty(makeProductInstance, "name", { value: name });
Object.defineProperties(
makeProductInstance
, { adtType: { enumerable: false
, value: 'product' }
, associateSumType: { enumerable: false
, value: (sumType) => makeProduct(name, fields, sumType, methods) }
, withMethod: { enumerable: false
, value: (methodName, arityOrParams, methodF) =>
makeProduct(name,
fields,
associatedSumType,
{ ...methods
, [methodName]: { method: Algebra => methodF
, arityOrParams }})}
, withMethodRec: { enumerable: false
, value: (methodName, arityOrParams, typeToMethodF) =>
makeProduct(name,
fields,
associatedSumType,
{ ...methods
, [methodName]: { method: typeToMethodF
, arityOrParams}})
}
, methods: { enumerable: false
, value: methods }
, fields: { enumerable: false
, value: fields }
});
return Object.freeze(makeProductInstance);
}