102 lines
3.2 KiB
JavaScript
102 lines
3.2 KiB
JavaScript
import { makeCompareFn } from "../compare/registry.js";
|
|
import { Bottom, Int, Unit } from "../primitives/types.js";
|
|
import { unit } from "../primitives/unit.js";
|
|
import { capitalizeFirstLetter } from "../util/util.js";
|
|
import { constructorProduct, getLeft, getRight } from "./product.js";
|
|
import { constructorLeft, constructorRight, match } from "./sum.js";
|
|
import { lsType, prettyT, sumType } from "./types.js";
|
|
|
|
// 'variants' is an array of (name: string, type: Type) pairs.
|
|
// e.g., the list of variants:
|
|
// [ { l: "price" , r: Int },
|
|
// { l: "prices" , r: [Int] },
|
|
// { l: "not_found", r: Unit } ]
|
|
// results in the type:
|
|
// (Int | ([Int] | (Unit | ⊥)))
|
|
export const enumType = variants => {
|
|
if (variants.length === 0) {
|
|
return Bottom; // empty enum is equal to Bottom-type (cannot be instantiated)
|
|
}
|
|
const [variant, ...rest] = variants;
|
|
const variantType = getRight(variant);
|
|
return sumType(variantType)(enumType(rest));
|
|
};
|
|
|
|
const eatParameters = (numParams, result) => {
|
|
if (numParams === 0) {
|
|
return result;
|
|
}
|
|
else return () => eatParameters(numParams-1, result);
|
|
}
|
|
|
|
export const makeMatchFn = variants => {
|
|
if (variants.length === 0) {
|
|
return undefined;
|
|
}
|
|
const [_, ...remainingVariants] = variants;
|
|
return sum => handler => {
|
|
return (
|
|
match(sum)(constructorProduct
|
|
(leftValue => eatParameters(remainingVariants.length, handler(leftValue)))
|
|
(rightValue => makeMatchFn(remainingVariants)(rightValue))
|
|
));
|
|
};
|
|
};
|
|
|
|
export const makeConstructors = variants => {
|
|
if (variants.length === 0) {
|
|
return [];
|
|
}
|
|
const [variant, ...remainingVariants] = variants;
|
|
const name = getLeft(variant);
|
|
const ctorName = `new${capitalizeFirstLetter(name)}`;
|
|
const constructor = { [ctorName]: val => constructorLeft(val) }[ctorName];
|
|
return [
|
|
constructor,
|
|
...makeConstructors(remainingVariants).map(ctor =>
|
|
({[ctor.name]: val => constructorRight(ctor(val))}[ctor.name])),
|
|
];
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const variants = [
|
|
constructorProduct("price")(Int),
|
|
constructorProduct("prices")(lsType(Int)),
|
|
constructorProduct("not_found")(Unit),
|
|
];
|
|
|
|
const myEnumType = enumType(variants);
|
|
|
|
console.log("observe the type that was generated:");
|
|
console.log(prettyT(myEnumType));
|
|
|
|
const [newPrice, newPrices, newNotFound] = makeConstructors(variants);
|
|
|
|
const price = newPrice(10);
|
|
const prices = newPrices({l:[20,30]});
|
|
const notFound = newNotFound(unit);
|
|
|
|
console.log("observe the encoding of different variant instances:");
|
|
console.log(price);
|
|
console.log(prices);
|
|
console.log(notFound);
|
|
|
|
const myEnumToString = x => makeMatchFn(variants)(x)
|
|
(price => `Price: ${price}`)
|
|
(prices => `Prices: ${prices.l}`)
|
|
(() => "Not found!");
|
|
|
|
console.log("observe the generated match function in action:")
|
|
console.log(myEnumToString(price));
|
|
console.log(myEnumToString(prices));
|
|
console.log(myEnumToString(notFound));
|
|
|
|
const compareMyEnum = makeCompareFn(myEnumType);
|
|
|
|
console.log(compareMyEnum(price)(prices)); // smaller
|
|
console.log(compareMyEnum(prices)(price)); // bigger
|
|
console.log(compareMyEnum(notFound)(price)); // bigger
|
|
console.log(compareMyEnum(prices)(prices)); // equal
|
|
console.log(compareMyEnum(newPrice(5))(newPrice(6))); // smaller
|