add enum type (generalization of sum-type)
This commit is contained in:
parent
8653bb99c6
commit
0b262daf7f
6 changed files with 102 additions and 7 deletions
|
|
@ -8,9 +8,10 @@ const SymbolDouble = Symbol('Double');
|
|||
const SymbolByte = Symbol('Byte');
|
||||
const SymbolChar = Symbol('Char');
|
||||
const SymbolUnit = Symbol('Unit');
|
||||
const SymbolBottom = Symbol('⊥');
|
||||
const SymbolSymbol = Symbol('Symbol');
|
||||
const SymbolType = Symbol('Type');
|
||||
const symbolAny = Symbol('Any');
|
||||
const symbolAny = Symbol('Any');
|
||||
const SymbolGenericType = Symbol('GenericType');
|
||||
|
||||
export const Int = makeTypeConstructor(SymbolInt)(0);
|
||||
|
|
@ -22,6 +23,9 @@ export const Char = makeTypeConstructor(SymbolChar)(0);
|
|||
// Unit type has only 1 instance, the empty tuple.
|
||||
export const Unit = makeTypeConstructor(SymbolUnit)(0);
|
||||
|
||||
// Bottom type has no instances.
|
||||
export const Bottom = makeTypeConstructor(SymbolBottom)(0);
|
||||
|
||||
export const SymbolT = makeTypeConstructor(SymbolSymbol)(0);
|
||||
|
||||
// Types are typed by Any
|
||||
|
|
@ -39,6 +43,7 @@ export const ModuleSymbols = {l:[
|
|||
{i: SymbolByte , t: SymbolT},
|
||||
{i: SymbolChar , t: SymbolT},
|
||||
{i: SymbolUnit , t: SymbolT},
|
||||
{i: SymbolBottom, t: SymbolT},
|
||||
{i: SymbolSymbol, t: SymbolT},
|
||||
{i: SymbolType , t: SymbolT},
|
||||
{i: SymbolGenericType, t: SymbolT},
|
||||
|
|
|
|||
88
structures/enum.js
Normal file
88
structures/enum.js
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
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 myEnum = enumType(variants);
|
||||
|
||||
console.log(prettyT(myEnum));
|
||||
|
||||
const [newPrice, newPrices, newNotFound] = makeConstructors(variants);
|
||||
|
||||
console.log(newPrice(10));
|
||||
console.log(newPrices([20,30]));
|
||||
console.log(newNotFound(unit));
|
||||
|
||||
const myMatchFn = makeMatchFn(variants);
|
||||
|
||||
const matchVariant = x => myMatchFn(x)
|
||||
(price => `Price: ${price}`)
|
||||
(prices => `Prices: ${prices}`)
|
||||
(() => "Not found!");
|
||||
|
||||
console.log(matchVariant(newPrice(10)));
|
||||
console.log(matchVariant(newPrices([20,30])));
|
||||
console.log(matchVariant(newNotFound(unit)));
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
// Product-type (also called: pair, tuple)
|
||||
|
||||
// A Product-type always has only two fields, called "left" and "right".
|
||||
// Product-types of more fields (called Structs) can be constructed by nesting Product-types.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import { 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 { fnType, prodType } from "./types.js";
|
||||
|
||||
function capitalizeFirstLetter(val) {
|
||||
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
||||
}
|
||||
|
||||
// 'fields' is an array of (name: string, type: Type) pairs.
|
||||
// e.g.:
|
||||
// [{l: "x", r: Double}, {l: "y", r: Double}]
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ export const constructorRight = right => ({t: "R", v: right});
|
|||
// sum-type -> (leftType -> resultType, rightType -> resultType) -> resultType
|
||||
export const match = sum => handlers =>
|
||||
sum.t === "L"
|
||||
? handlers.left(sum.v)
|
||||
: handlers.right(sum.v);
|
||||
? handlers.l(sum.v)
|
||||
: handlers.r(sum.v);
|
||||
|
||||
export const ModuleSum = {l:[
|
||||
// binary type constructor
|
||||
|
|
|
|||
|
|
@ -42,4 +42,8 @@ export function zip(a, b) {
|
|||
return a.map((k, i) => [k, b[i]]);
|
||||
}
|
||||
|
||||
export function capitalizeFirstLetter(val) {
|
||||
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue