diff --git a/index.html b/index.html index eae7148..9716a53 100644 --- a/index.html +++ b/index.html @@ -671,7 +671,7 @@ (Eq a c) -------------- eq-a-comp - (Eq "a" (Foo Bar)) + (Eq "a" Foo) ------------- eq-a-c diff --git a/src/AtomDef.res b/src/AtomDef.res new file mode 100644 index 0000000..c41cd07 --- /dev/null +++ b/src/AtomDef.res @@ -0,0 +1,168 @@ +// type level stuff to enable well-typed coercions +type typeTag<_> = .. +type rec eq<_, _> = Refl: eq<'a, 'a> +// coercion represents a coercion from some type 'a to t +type rec coercion<_> = Coercion(typeTag<'a>, 'a => option<'b>): coercion<'b> + +module type ATOM = { + type t + type subst = Map.t + let tagEq: typeTag<'a> => option> + let tag: typeTag + let unify: (t, t, ~gen: ref=?) => Seq.t + let prettyPrint: (t, ~scope: array) => string + let parse: (string, ~scope: array, ~gen: ref=?) => result<(t, string), string> + let substitute: (t, subst) => t + let upshift: (t, int, ~from: int=?) => t + // used for when trying to substitute a variable of the wrong type + let lowerVar: int => option + let lowerSchematic: (int, array) => option + let substDeBruijn: (t, array>, ~from: int=?) => t + let concrete: t => bool +} + +module type COERCIBLE_ATOM = { + include ATOM + let coercions: array> +} + +module MakeCoercible = ( + Atom: ATOM, + Coercions: { + let coercions: array> + }, +): (COERCIBLE_ATOM with type t = Atom.t) => { + include Atom + let coercions = Coercions.coercions +} + +exception RawVarOrSchematic +module CombineAtom = (Left: COERCIBLE_ATOM, Right: COERCIBLE_ATOM): { + type base = + | Left(Left.t) + | Right(Right.t) + // neither of the below should appear organically. they're purely so we can lower + // substitutions to both Left.t and Right.t + | Var({idx: int}) + | Schematic({schematic: int, allowed: array}) + include COERCIBLE_ATOM with type t = base + let match: (t, Left.t => 'a, Right.t => 'a) => 'a +} => { + type base = + | Left(Left.t) + | Right(Right.t) + | Var({idx: int}) + | Schematic({schematic: int, allowed: array}) + type t = base + type subst = Map.t + type gen = ref + type typeTag<_> += Tag: typeTag + let tag = Tag + let tagEq = (type a, tag: typeTag): option> => + switch tag { + | Tag => Some(Refl) + | _ => None + } + let coercions = { + let left = + Left.coercions->Array.map((Coercion((tag, f))) => Coercion(( + tag, + x => f(x)->Option.map(v => Left(v)), + ))) + let right = + Right.coercions->Array.map((Coercion((tag, f))) => Coercion(( + tag, + x => f(x)->Option.map(v => Right(v)), + ))) + Array.concat(left, right) + } + let match = (t, leftBranch: Left.t => 'a, rightBranch: Right.t => 'a): 'a => + switch t { + | Left(s) => leftBranch(s) + | Right(s) => rightBranch(s) + | _ => throw(RawVarOrSchematic) + } + let parse = (s, ~scope, ~gen: option=?) => { + Left.parse(s, ~scope, ~gen?) + ->Result.map(((r, rest)) => (Left(r), rest)) + ->Util.Result.or(() => + Right.parse(s, ~scope, ~gen?)->Result.map(((r, rest)) => (Right(r), rest)) + ) + } + let prettyPrint = (s, ~scope) => + s->match(left => Left.prettyPrint(left, ~scope), right => Right.prettyPrint(right, ~scope)) + let unify = (s1, s2, ~gen=?) => + switch (s1, s2) { + | (Left(s1), Left(s2)) => + Left.unify(s1, s2, ~gen?)->Seq.map(subst => subst->Util.mapMapValues(v => Left(v))) + | (Right(s1), Right(s2)) => + Right.unify(s1, s2, ~gen?)->Seq.map(subst => subst->Util.mapMapValues(v => Right(v))) + | (_, _) => Seq.empty + } + let coerceLeft = (left: Left.t): option => + Array.findMap(Right.coercions, (Coercion((tag, f))) => + switch Left.tagEq(tag) { + | Some(Refl) => f(left) + | None => None + } + ) + let coerceRight = (right: Right.t): option => { + Array.findMap(Left.coercions, (Coercion((tag, f))) => + switch Right.tagEq(tag) { + | Some(Refl) => f(right) + | None => None + } + ) + } + let substitute = (s, subst: subst) => { + s->match( + left => { + let leftSubs = + subst->Util.Map.filterMap((_, v) => v->match(left => Some(left), coerceRight)) + Left(Left.substitute(left, leftSubs)) + }, + right => { + let rightSubs = + subst->Util.Map.filterMap((_, v) => v->match(coerceLeft, right => Some(right))) + Right(Right.substitute(right, rightSubs)) + }, + ) + } + let upshift = (s, amount: int, ~from=?) => + s->match( + left => Left(left->Left.upshift(amount, ~from?)), + right => Right(right->Right.upshift(amount, ~from?)), + ) + let lowerVar = idx => Some(Var({idx: idx})) + let lowerSchematic = (schematic, allowed) => Some(Schematic({schematic, allowed})) + let substDeBruijn = (s, substs: array>, ~from=?) => + s->match( + left => { + let leftSubs = substs->Array.map(o => + o->Option.flatMap(s => + switch s { + | Left(s) => Some(s) + | Var({idx}) => Left.lowerVar(idx) + | Schematic({schematic, allowed}) => Left.lowerSchematic(schematic, allowed) + | Right(s) => coerceRight(s) + } + ) + ) + Left(Left.substDeBruijn(left, leftSubs, ~from?)) + }, + right => { + let rightSubs = substs->Array.map(o => + o->Option.flatMap(s => + switch s { + | Right(s) => Some(s) + | Var({idx}) => Right.lowerVar(idx) + | Schematic({schematic, allowed}) => Right.lowerSchematic(schematic, allowed) + | Left(s) => coerceLeft(s) + } + ) + ) + Right(Right.substDeBruijn(right, rightSubs, ~from?)) + }, + ) + let concrete = s => s->match(Left.concrete, Right.concrete) +} diff --git a/src/Coercible.res b/src/Coercible.res new file mode 100644 index 0000000..33ed493 --- /dev/null +++ b/src/Coercible.res @@ -0,0 +1,15 @@ +open AtomDef + +module StringA = MakeCoercible( + StringA.Atom, + { + let coercions = [Coercion((Symbolic.Atom.tag, s => Some([StringA.String(s)])))] + }, +) + +module Symbolic = MakeCoercible( + Symbolic.Atom, + { + let coercions = [] + }, +) diff --git a/src/CombinedAtom.res b/src/CombinedAtom.res index 95f3d12..2e7a6da 100644 --- a/src/CombinedAtom.res +++ b/src/CombinedAtom.res @@ -1,7 +1,7 @@ module type ATOM = SExpFunc.ATOM exception RawVarOrSchematic -module MakeAtom = (Left: ATOM, Right: ATOM): { +module MakeAtom = (Left: AtomDef.COERCIBLE_ATOM, Right: AtomDef.COERCIBLE_ATOM): { type base = | Left(Left.t) | Right(Right.t) @@ -11,100 +11,13 @@ module MakeAtom = (Left: ATOM, Right: ATOM): { | Schematic({schematic: int, allowed: array}) include ATOM with type t = base let match: (t, Left.t => 'a, Right.t => 'a) => 'a -} => { - type base = - | Left(Left.t) - | Right(Right.t) - | Var({idx: int}) - | Schematic({schematic: int, allowed: array}) - type t = base - type subst = Map.t - type gen = ref - let match = (t, leftBranch: Left.t => 'a, rightBranch: Right.t => 'a): 'a => - switch t { - | Left(s) => leftBranch(s) - | Right(s) => rightBranch(s) - | _ => throw(RawVarOrSchematic) - } - let parse = (s, ~scope, ~gen: option=?) => { - Left.parse(s, ~scope, ~gen?) - ->Result.map(((r, rest)) => (Left(r), rest)) - ->Util.Result.or(() => - Right.parse(s, ~scope, ~gen?)->Result.map(((r, rest)) => (Right(r), rest)) - ) - } - let prettyPrint = (s, ~scope) => - s->match(left => Left.prettyPrint(left, ~scope), right => Right.prettyPrint(right, ~scope)) - let unify = (s1, s2, ~gen=?) => - switch (s1, s2) { - | (Left(s1), Left(s2)) => - Left.unify(s1, s2, ~gen?)->Seq.map(subst => subst->Util.mapMapValues(v => Left(v))) - | (Right(s1), Right(s2)) => - Right.unify(s1, s2, ~gen?)->Seq.map(subst => subst->Util.mapMapValues(v => Right(v))) - | (_, _) => Seq.empty - } - let substitute = (s, subst: subst) => { - s->match( - left => { - let leftSubs = subst->Util.Map.filterMap((_, v) => - switch v { - | Left(s) => Some(s) - | _ => None - } - ) - Left(Left.substitute(left, leftSubs)) - }, - right => { - let rightSubs = subst->Util.Map.filterMap((_, v) => - switch v { - | Right(s) => Some(s) - | _ => None - } - ) - Right(Right.substitute(right, rightSubs)) - }, - ) - } - let upshift = (s, amount: int, ~from=?) => - s->match( - left => Left(left->Left.upshift(amount, ~from?)), - right => Right(right->Right.upshift(amount, ~from?)), - ) - let lowerVar = idx => Some(Var({idx: idx})) - let lowerSchematic = (schematic, allowed) => Some(Schematic({schematic, allowed})) - let substDeBruijn = (s, substs: array>, ~from=?) => - s->match( - left => { - let leftSubs = substs->Array.map(s => - switch s { - | Some(Left(s)) => Some(s) - | Some(Var({idx})) => Left.lowerVar(idx) - | Some(Schematic({schematic, allowed})) => Left.lowerSchematic(schematic, allowed) - | _ => None - } - ) - Left(Left.substDeBruijn(left, leftSubs, ~from?)) - }, - right => { - let rightSubs = substs->Array.map(s => - switch s { - | Some(Right(s)) => Some(s) - | Some(Var({idx})) => Right.lowerVar(idx) - | Some(Schematic({schematic, allowed})) => Right.lowerSchematic(schematic, allowed) - | _ => None - } - ) - Right(Right.substDeBruijn(right, rightSubs, ~from?)) - }, - ) - let concrete = s => s->match(Left.concrete, Right.concrete) -} +} => AtomDef.CombineAtom(Left, Right) module type ATOM_VIEW = SExpViewFunc.ATOM_VIEW module MakeAtomView = ( - Left: ATOM, + Left: AtomDef.COERCIBLE_ATOM, LeftView: ATOM_VIEW with module Atom := Left, - Right: ATOM, + Right: AtomDef.COERCIBLE_ATOM, RightView: ATOM_VIEW with module Atom := Right, Combined: module type of MakeAtom(Left, Right), ): { @@ -119,9 +32,9 @@ module MakeAtomView = ( } module MakeAtomAndView = ( - Left: ATOM, + Left: AtomDef.COERCIBLE_ATOM, LeftView: ATOM_VIEW with module Atom := Left, - Right: ATOM, + Right: AtomDef.COERCIBLE_ATOM, RightView: ATOM_VIEW with module Atom := Right, ) => { module Atom = MakeAtom(Left, Right) diff --git a/src/SExpFunc.res b/src/SExpFunc.res index 6672150..3a3aba2 100644 --- a/src/SExpFunc.res +++ b/src/SExpFunc.res @@ -1,19 +1,6 @@ exception SubstNotCompatible(string) -module type ATOM = { - type t - type subst = Map.t - let unify: (t, t, ~gen: ref=?) => Seq.t - let prettyPrint: (t, ~scope: array) => string - let parse: (string, ~scope: array, ~gen: ref=?) => result<(t, string), string> - let substitute: (t, subst) => t - let upshift: (t, int, ~from: int=?) => t - // used for when trying to substitute a variable of the wrong type - let lowerVar: int => option - let lowerSchematic: (int, array) => option - let substDeBruijn: (t, array>, ~from: int=?) => t - let concrete: t => bool -} +module type ATOM = AtomDef.ATOM module IntCmp = Belt.Id.MakeComparable({ type t = int diff --git a/src/Scratch.res b/src/Scratch.res index 9232766..94ea949 100644 --- a/src/Scratch.res +++ b/src/Scratch.res @@ -43,9 +43,9 @@ module TheoremS = Editable.TextArea(Theorem.Make(HOTerm, HOTerm, HOTermJView, DL module ConfS = ConfigBlock.Make(HOTerm, HOTerm) module StringSymbol = CombinedAtom.MakeAtomAndView( - StringA.Atom, + Coercible.StringA, StringA.AtomView, - Symbolic.Atom, + Coercible.Symbolic, Symbolic.AtomView, ) module StringSExp = SExpFunc.Make(StringSymbol.Atom) diff --git a/src/StringA.res b/src/StringA.res index 440c747..23c7128 100644 --- a/src/StringA.res +++ b/src/StringA.res @@ -12,8 +12,16 @@ type meta = string type schematic = int module Atom = { + open AtomDef type t = t type subst = Map.t + type typeTag<_> += Tag: typeTag + let tag = Tag + let tagEq = (type a, tag: typeTag): option> => + switch tag { + | Tag => Some(Refl) + | _ => None + } let substitute = (term: t, subst: subst) => Array.flatMap(term, piece => { switch piece { @@ -482,12 +490,6 @@ module AtomView = { } - let makeMeta = (str: string) => - - {React.string(str)} - {React.string(".")} - - let parenthesise = f => [ {React.string("(")} , diff --git a/src/StringA.resi b/src/StringA.resi index 6a92125..a6492c6 100644 --- a/src/StringA.resi +++ b/src/StringA.resi @@ -4,5 +4,5 @@ type rec piece = | Schematic({schematic: int, allowed: array}) type t = array -module Atom: SExpFunc.ATOM with type t = t +module Atom: AtomDef.ATOM with type t = t module AtomView: SExpViewFunc.ATOM_VIEW with module Atom := Atom diff --git a/src/StringAxiomSet.res b/src/StringAxiomSet.res index eda40f1..f455d97 100644 --- a/src/StringAxiomSet.res +++ b/src/StringAxiomSet.res @@ -1,9 +1,9 @@ open Component module StringSymbol = CombinedAtom.MakeAtomAndView( - StringA.Atom, + Coercible.StringA, StringA.AtomView, - Symbolic.Atom, + Coercible.Symbolic, Symbolic.AtomView, ) module StringSExp = SExpFunc.Make(StringSymbol.Atom) diff --git a/src/Symbolic.res b/src/Symbolic.res index 7ffba16..47a9827 100644 --- a/src/Symbolic.res +++ b/src/Symbolic.res @@ -1,7 +1,15 @@ type t = string module Atom = { + open AtomDef type t = string type subst = Map.t + type typeTag<_> += Tag: typeTag + let tag = Tag + let tagEq = (type a, tag: typeTag): option> => + switch tag { + | Tag => Some(Refl) + | _ => None + } let unify = (a, b, ~gen as _=?) => if a == b { Seq.once(Map.make()) diff --git a/src/Symbolic.resi b/src/Symbolic.resi index 9e9279f..1a2ca55 100644 --- a/src/Symbolic.resi +++ b/src/Symbolic.resi @@ -1,3 +1,3 @@ type t = string -module Atom: SExpFunc.ATOM with type t = t +module Atom: AtomDef.ATOM with type t = t module AtomView: SExpViewFunc.ATOM_VIEW with module Atom := Atom