diff --git a/lib/versioning/merge.js b/lib/versioning/merge.js index ae80a03..7ef70ba 100644 --- a/lib/versioning/merge.js +++ b/lib/versioning/merge.js @@ -1,5 +1,5 @@ -import { emptySet, add, length, forEach, has, remove } from "../structures/set.js"; -import { getDepth, Slot } from "./value_slot.js"; +import { emptySet, add, length, forEach, union, has } from "../structures/set.js"; +import { getDepth, Slot, Value, Write } from "./value_slot.js"; import { compareSlot } from "./compare.js"; import { makeTypeParser } from "../parser/type_parser.js"; import { newDynamic } from "../primitives/dynamic.js"; @@ -20,10 +20,6 @@ export const findLCA = slotA => slotB => { } }; -// returns -// {slotA} if slotA is younger -// {slotB} if slotB is younger -// {slotA, slotB} if they are concurrent (conflicting) export const merge = slotA => slotB => { const lca = findLCA(slotA)(slotB); if (compareSlot(lca)(slotA) === 0) { @@ -38,7 +34,7 @@ export const merge = slotA => slotB => { }; export const mergeN = slots => { - let result = slots; + let toDelete = emptySetOfSlots; forEach(slots)(slotA => { forEach(slots)(slotB => { // compare all non-identical pairs @@ -48,18 +44,26 @@ export const mergeN = slots => { // if in the pair, one is an ancestor of the other, // only keep the other if (has(m)(slotA)) { - result = remove(result)(slotB); + toDelete = add(toDelete)(slotB); } } } }); }); + let result = emptySetOfSlots; + forEach(slots)(slot => { + if (!has(toDelete)(slot)) { + result = add(result)(slot); + } + }) return result; }; const mkType = makeTypeParser({ extraPrimitives: [ - ["Slot" , Slot], + ["Value", Value], + ["Slot" , Slot ], + ["Write", Write], ], }); diff --git a/lib/versioning/static/compare.js b/lib/versioning/static/compare.js deleted file mode 100644 index bfb8429..0000000 --- a/lib/versioning/static/compare.js +++ /dev/null @@ -1,55 +0,0 @@ -import { compareSymbols, compareInts } from "../../compare/primitives.js"; -import { compareFunctions } from "../../compare/structures.js"; -import { makeTypeParser } from "../../parser/type_parser.js"; -import { newDynamic } from "../../primitives/dynamic.js"; -import { makeEnumCompareFn } from "../../structures/enum.js"; -import { makeStructCompareFn } from "../../structures/struct.js"; -import { Slot, Transformation, Value, Write } from "./value_slot.js"; - - -// comparison functions are mutually recursive... - -export const compareSlot = compareElems => makeEnumCompareFn([ - { l: "new", r: () => compareSymbols }, - { l: "write", r: () => compareWrite(compareElems) }, -]); - -export const compareWrite = compareElems => makeStructCompareFn([ - { l: "slot", r: () => compareSlot(compareElems) }, - { l: "value", r: () => compareValue(compareElems) }, - { l: "depth", r: () => compareInts }, -]); - -export const compareTransformation = compareInputs => compareOutputs => makeStructCompareFn([ - { l: "input", r: () => compareValue(compareInputs) }, - { l: "fn", r: () => compareValue(compareFunctions(compareInputs)(compareOutputs)) }, - { l: "output", r: () => compareOutputs }, -]); - -export const compareValue = compareElems => makeEnumCompareFn([ - { l: "literal", r: () => compareElems }, - { l: "read", r: () => compareWrite(compareElems) }, - { l: "transform", r: () => - compareTransformation - (_a => _b => 0) // <- we cheat here, because we don't statically know the input type of the transformation. - (compareElems) }, -]); - -const mkType = makeTypeParser({ - extraBracketOperators: [ - [':=', ['=:', Slot]], - ['*=', ['=*', Value]], - ['*:=', ['=:*', Write]], - ], - extraInfixOperators: [ - ['===>', Transformation], - ], -}); - -export const ModuleVersioningCompare = [ - // comparison - ["compareSlot" , newDynamic(compareSlot )(mkType("(a -> a -> Ordering) -> :=a=: -> :=a=: -> Ordering"))], - ["compareValue", newDynamic(compareValue)(mkType("(a -> a -> Ordering) -> *=a=* -> *=a=* -> Ordering"))], - ["compareWrite", newDynamic(compareWrite)(mkType("(a -> a -> Ordering) -> *:=a=:* -> *:=a=:* -> Ordering"))], - ["compareTransformation", newDynamic(compareTransformation)(mkType("(a -> a -> Ordering) -> (b -> b -> Ordering) -> (a ===> b) -> (a ===> b) -> Ordering"))], -]; diff --git a/lib/versioning/static/merge.js b/lib/versioning/static/merge.js deleted file mode 100644 index 1243f7b..0000000 --- a/lib/versioning/static/merge.js +++ /dev/null @@ -1,69 +0,0 @@ -import { emptySet, add, length, forEach, has, remove } from "../../structures/set.js"; -import { getDepth, Slot } from "./value_slot.js"; -import { compareSlot } from "../compare.js"; -import { makeTypeParser } from "../../parser/type_parser.js"; -import { newDynamic } from "../../primitives/dynamic.js"; - -const emptySetOfSlots = emptySet(compareSlot); - -export const findLCA = slotA => slotB => { - if (compareSlot(slotA)(slotB) === 0) { - return slotA; - } - if (getDepth(slotA) > getDepth(slotB)) { - // we can assume that slotA.variant === "write" - return findLCA(slotA.value.slot)(slotB); - } - else { - // we can assume that slotB.variant === "write" - return findLCA(slotA)(slotB.value.slot); - } -}; - -// returns -// {slotA} if slotA is younger -// {slotB} if slotB is younger -// {slotA, slotB} if they are concurrent (conflicting) -export const merge = slotA => slotB => { - const lca = findLCA(slotA)(slotB); - if (compareSlot(lca)(slotA) === 0) { - return add(emptySetOfSlots)(slotB); - } - if (compareSlot(lca)(slotB) === 0) { - return add(emptySetOfSlots)(slotA); - } - const setWithA = add(emptySetOfSlots)(slotA); - const setWithAandB = add(setWithA)(slotB); - return setWithAandB; -}; - -export const mergeN = slots => { - let result = slots; - forEach(slots)(slotA => { - forEach(slots)(slotB => { - // compare all non-identical pairs - if (compareSlot(slotA)(slotB) !== 0) { - const m = merge(slotA)(slotB); - if (length(m) === 1) { - // if in the pair, one is an ancestor of the other, - // only keep the other - if (has(m)(slotA)) { - result = remove(result)(slotB); - } - } - } - }); - }); - return result; -}; - -const mkType = makeTypeParser({ - extraBracketOperators: [ - [':=', ['=:', Slot]], - ], -}); - -export const ModuleStaticMerge = [ - ["merge" , newDynamic(merge)(mkType(":=a=: -> :=a=: -> {:=a=:}"))], - ["mergeN", newDynamic(merge)(mkType("{:=a=:} -> {:=a=:}" ))], -]; diff --git a/lib/versioning/static/value_slot.js b/lib/versioning/static/value_slot.js deleted file mode 100644 index 7e680ab..0000000 --- a/lib/versioning/static/value_slot.js +++ /dev/null @@ -1,134 +0,0 @@ -import { makeGeneric } from "../../generics/generics.js"; -import { makeTypeConstructor } from "../../meta/type_constructor.js"; -import { makeTypeParser } from "../../parser/type_parser.js"; -import { apply, newDynamic } from "../../primitives/dynamic.js"; -import { Int, UUID } from "../../primitives/primitive_types.js"; -import { makeModuleEnum } from "../../structures/enum.types.js"; -import { makeModuleStruct } from "../../structures/struct.types.js"; -import { fnType } from "../../structures/type_constructors.types.js"; - -// Value and Slot each take 1 type parameter -export const Slot = makeTypeConstructor("Slot__318d1c1a9336c141336c461c6a3207b0")(1); -export const Value = makeTypeConstructor("Value__23fc00a2db1374bd3dc1a0ad2d8517ab")(1); - -// Transformation takes 2 type parameters -export const Transformation = makeTypeConstructor("Transformation__9eac70d7020c7c45d5a16f3f14b13083")(2); - -// A Write-operation writes a value to a slot. The inner type of the Value and Slot must match. -export const Write = makeTypeConstructor("Write__abaef8ddb5c167b5d2cedac111fcefd3")(1); - -// Enum: Value -const [ - // constructors - [, {i: newValueLiteral}], - [, {i: newValueRead}], - [, {i: newValueTransform}], - // match function - [, {i: matchValue}], -] = makeGeneric((innerType, a) => makeModuleEnum(Value(_=>innerType))([ - {l: "literal" , r: innerType}, - {l: "read" , r: Write(_=>innerType)}, // <- a 'read' reads the result of a Write (to a Slot) - {l: "transform", r: Transformation(_=>a)(_=>innerType)}, // <- result of a function call -])); - -// Enum: Slot -const [ - // constructors - [, {i: newSlotNew}], - [, {i: newSlotWrite}], - // match function - [, {i: matchSlot}], -] = makeGeneric(innerType => makeModuleEnum(Slot(_=>innerType))([ - {l: "new" , r: UUID }, - {l: "write", r: Write}, -])); - -// Struct: Transformation (i.e., a function call) -const [ - // constructor - [, {i: newTransformation}], - // getters - // [, {i: getInput}], - // [, {i: getFn}], - // [, {i: getOutput}], -] = makeGeneric((inType, outType) => makeModuleStruct(Transformation)([ - {l: "input" , r: Value(_=>inType) }, // <- the input is a value (e.g., could be the result of a Transformation itself) - {l: "fn" , r: Value(_=>fnType(_=>inType)(_=>outType)) }, // <- the function is also a value (e.g., could also be result of a transformation, e.g., when currying) - {l: "output", r: outType}, // <- the result -])); - -// Struct: Write -const [ - // constructor - [, {i: newWrite}], - // getters - // [, {i: getDepth}], - // [, {i: getSlot}], - // [, {i: getValue}], -] = makeGeneric(innerType => makeModuleStruct(Write(_=>innerType))([ - {l: "depth", r: Int }, // <- depth increases merge performance, and also comes first in the struct (this way, it is the first value compared upon comparison) - {l: "slot" , r: Slot(_=>innerType) }, - {l: "value", r: Value(_=>innerType) }, -])); - -// shorthands -export const getDepth = slot => - matchSlot(slot) - (_slotNew => 0n) - (slotWrite => slotWrite.depth); - -// get the value from a Value -export const getVal = value => - matchValue(value) - (literal => literal) - (read => read.value.value) - (transform => transform.output) - -export const write = slot => value => - newSlotWrite( - newWrite - (getDepth(slot)+1n) - (slot) - (value) - ); - -export const transform = inValue => fnValue => - newValueTransform - (newTransformation - (inValue) - (fnValue) - (apply(getVal(fnValue))(getVal(inValue))) // <- call function - ); - -export const read = slot => - matchSlot(slot) - (_slotNew => { throw new Error("cannot read empty slot"); }) - (write => write.value); - -export const newSlot = newSlotNew; -export const toSlot = newSlotWrite; -export const newLiteral = newValueLiteral; - -const mkType = makeTypeParser({ - extraBracketOperators: [ - [':=', ['=:', Slot]], - ['*=', ['=*', Value]], - ], - extraInfixOperators: [ - ['===>', Transformation], - ], -}); - -export const ModuleStaticVersioning = [ - // slots - ["newSlot" , newDynamic(newSlot )(mkType("UUID -> :=a=:" ))], - ["write" , newDynamic(write )(mkType(":=a=: -> *=a=* -> :=a=:"))], - ["getDepth" , newDynamic(getDepth )(mkType(":=a=: -> Int" ))], - - // values - ["newLiteral", newDynamic(newLiteral)(mkType("a -> *=a=*" ))], - ["read" , newDynamic(read )(mkType(":=a=: -> *=a=*" ))], - ["transform" , newDynamic(transform )(mkType("*=a=* -> *=(a->b)=* -> *=b=*"))], -]; - - diff --git a/lib/versioning/value_slot.js b/lib/versioning/value_slot.js index dbbc64d..3784ae8 100644 --- a/lib/versioning/value_slot.js +++ b/lib/versioning/value_slot.js @@ -20,8 +20,8 @@ const [ [, {i: matchValue}], ] = makeModuleEnum(Value)([ {l: "literal" , r: Dynamic}, - {l: "read" , r: Write}, // <- a 'read' reads the result of a Write (to a Slot) - {l: "transform", r: Transformation}, // <- result of a function call + {l: "read" , r: Write}, // a 'read' reads the result of a Write + {l: "transform", r: Transformation}, ]); // Enum: Slot @@ -36,7 +36,7 @@ const [ {l: "write", r: Write}, ]); -// Struct: Transformation (i.e., a function call) +// Struct: Transformation const [ // constructor [, {i: newTransformation}], @@ -45,9 +45,9 @@ const [ // [, {i: getFn}], // [, {i: getOutput}], ] = makeModuleStruct(Transformation)([ - {l: "input" , r: Value }, // <- the input is a value (e.g., could be the result of a Transformation itself) - {l: "fn" , r: Value }, // <- the function is also a value (e.g., could also be result of a transformation, e.g., when currying) - {l: "output", r: Dynamic}, // <- the result + {l: "input" , r: Value }, + {l: "fn" , r: Value }, + {l: "output", r: Dynamic}, ]); // Struct: Write