Skip to content

Commit 1731d1d

Browse files
committed
uniquify variable names in split
1 parent 1dbb8eb commit 1731d1d

File tree

4 files changed

+54
-20
lines changed

4 files changed

+54
-20
lines changed

docs/source/interactive.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Narya has a limited ability to infer the shape of a term to solve a hole with fr
8080

8181
If none of these cases apply, an error results.
8282

83-
Note that the term generated by a split is not guaranteed to typecheck. For example, if the goal is an indexed datatype with one constructor, splitting will generate an application of that constructor to holes; but if the indices depend at all on the arguments of the constructor, this will not typecheck since Narya will be unable to tell whether the indices agree with those of the goal. More mundanely, if a datatype constructor is declared with multiple arguments of the same name such as ``data [ constr. (x:A) (x:A) ]`` (I don't know why you would do that, but it's allowed with the second ``x`` shadowing the first), splitting on a term of that type will generate a match such as ``match u [ constr. x x ↦ ? ]``, which fails to typecheck because the pattern variables in each branch of a match are required to be distinct.
83+
Note that the term generated by a split is not guaranteed to typecheck. For example, if the goal is an indexed datatype with one constructor, splitting will generate an application of that constructor to holes; but if the indices depend at all on the arguments of the constructor, this will not typecheck since Narya will be unable to tell whether the indices agree with those of the goal.
8484

8585
You can also split on a hole directly in interactive mode, identifying a hole by its number as in ``split 0 ≔ M`` to split on a term ``M``, or ``split 0 ≔ _`` to split on the goal type. This will print the proposed term with the label "hole could be solved by"; you can then copy it, edit it as desired, and supply it to a ``solve`` command.
8686

lib/parser/command.ml

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,20 +1033,33 @@ let execute ~(action_taken : unit -> unit) ~(get_file : string -> Scope.trie) (c
10331033
let hole li ri = locate_opt None (Hole { li; ri; num = ref (-1); ws = []; contents = None }) in
10341034
let ehole = hole No.Interval.entire No.Interval.entire in
10351035
let ctx = Norm.eval_ctx termctx in
1036-
(* I don't think we actually use this, but it's "correct" to put it in. *)
10371036
let _, names = Names.uniquify_vars vars in
10381037
let term =
10391038
match data.tms with
10401039
| [ (_, Wrap { value = Placeholder _; _ }) ] -> (
10411040
let ety = Norm.eval_term (Ctx.env ctx) ty in
10421041
match View.view_type ety "split" with
10431042
| Canonical (_, Pi (_, doms, _), _, _) ->
1043+
let dim = CubeOf.dim doms in
10441044
let cube, mapsto, notn =
1045-
match D.compare_zero (CubeOf.dim doms) with
1045+
match D.compare_zero dim with
10461046
| Zero -> (`Normal, Token.Mapsto, Builtins.abs)
10471047
| Pos _ -> (`Cube, Token.DblMapsto, Builtins.cubeabs) in
1048-
let xs = Domvars.get_pi_vars ctx cube Emp ety in
1049-
(* TODO: Should generate real variable names. *)
1048+
(* Uniquify the variable names relative to the context *)
1049+
let module NameState = Monad.State (struct
1050+
type t = Names.wrapped
1051+
end) in
1052+
let module M = Mbwd.Monadic (NameState) in
1053+
let xs, _ =
1054+
M.mmapM
1055+
(fun [ x ] ->
1056+
let open Monad.Ops (NameState) in
1057+
let* (Wrap names) = NameState.get in
1058+
let x, names = Names.add_cube dim names x in
1059+
let* () = NameState.put (Wrap names) in
1060+
return x)
1061+
[ Domvars.get_pi_vars ctx cube Emp ety ]
1062+
(Wrap names) in
10501063
let vars =
10511064
unparse_abs
10521065
(Bwd.map (fun x -> (x, `Explicit)) xs)
@@ -1131,19 +1144,33 @@ let execute ~(action_taken : unit -> unit) ~(get_file : string -> Scope.trie) (c
11311144
(let open Monad.Ops (Monad.List) in
11321145
let* wscomma, Wrap tm = data.tms in
11331146
[ Token (Op ",", (wscomma, None)); Term tm ]) in
1134-
let module S = Monad.State (Bool) in
1135-
let module LS = Monad.ListT (S) in
1136-
let open Monad.Ops (LS) in
1137-
let rec constr_args : type a p ap.
1138-
?acc:unparser Bwd.t -> (a, p, ap) Term.Telescope.t -> unparser Bwd.t =
1139-
fun ?(acc = Emp) -> function
1140-
| Emp -> acc
1141-
| Ext (x, _, args) ->
1142-
constr_args ~acc:(Snoc (acc, { unparse = (fun _ _ -> unparse_var x) })) args
1147+
(* We have a list of terms and we need to produce a multiple match. We use a list monad to produce all possible combinations of constructors for all the types of all the terms, with a boolean state outside the list to track, whether it contains any higher-dimensional matches and thus should use a ⤇ instead of a ↦. And we use a Names state *inside* the list to uniquify variables separately in each branch. *)
1148+
let module HigherBranch = Monad.State (Bool) in
1149+
let module Branches = Monad.ListT (HigherBranch) in
1150+
let module NameBranches =
1151+
Monad.StateT
1152+
(Branches)
1153+
(struct
1154+
type t = Names.wrapped
1155+
end)
11431156
in
1157+
let open Monad.Ops (NameBranches) in
1158+
let rec constr_args : type a p ap n k.
1159+
n Names.t ->
1160+
k D.t ->
1161+
?acc:unparser Bwd.t ->
1162+
(a, p, ap) Term.Telescope.t ->
1163+
unparser Bwd.t * Names.wrapped =
1164+
fun names dim ?(acc = Emp) -> function
1165+
| Emp -> (acc, Wrap names)
1166+
| Ext (x, _, args) ->
1167+
let x, names = Names.add_cube dim names x in
1168+
constr_args names dim
1169+
~acc:(Snoc (acc, { unparse = (fun _ _ -> unparse_var x) }))
1170+
args in
11441171
let rec go = function
11451172
| [] ->
1146-
let* higher = LS.lift S.get in
1173+
let* higher = NameBranches.stateless (Branches.lift HigherBranch.get) in
11471174
let mapsto = if higher then Token.DblMapsto else Mapsto in
11481175
return [ tok mapsto; Term ehole ]
11491176
| (_, Wrap tm) :: tms -> (
@@ -1155,18 +1182,23 @@ let execute ~(action_taken : unit -> unit) ~(get_file : string -> Scope.trie) (c
11551182
let* () =
11561183
match D.compare_zero dim with
11571184
| Zero -> return ()
1158-
| Pos _ -> LS.lift (S.put true) in
1159-
let* c, Dataconstr { args; _ } = S.return (Bwd.to_list constrs) in
1185+
| Pos _ ->
1186+
NameBranches.stateless (Branches.lift (HigherBranch.put true)) in
1187+
let* c, Dataconstr { args; _ } =
1188+
NameBranches.stateless (HigherBranch.return (Bwd.to_list constrs)) in
1189+
let* (Wrap names) = NameBranches.get in
1190+
let cargs, newnames = constr_args names dim args in
1191+
let* () = NameBranches.put newnames in
11601192
let first =
11611193
Term
1162-
(unparse_spine names (`Constr c) (constr_args args) No.Interval.entire
1194+
(unparse_spine names (`Constr c) cargs No.Interval.entire
11631195
No.Interval.entire) in
11641196
let* rest = go tms in
11651197
if List.length rest = 2 then return (first :: rest)
11661198
else return (first :: tok (Op ",") :: rest)
11671199
| _ -> fatal (Invalid_split (`Term, "non-datatype")))
11681200
| _ -> fatal (Nonsynthesizing "splitting term")) in
1169-
let lines = fst (go data.tms false) in
1201+
let lines, _ = go data.tms (Wrap names) false in
11701202
locate_opt None
11711203
@@ outfix ~notn:Builtins.implicit_mtch
11721204
~inner:
@@ -1175,7 +1207,7 @@ let execute ~(action_taken : unit -> unit) ~(get_file : string -> Scope.trie) (c
11751207
Emp
11761208
<@ comma_tms
11771209
<: tok LBracket
1178-
<@ List.flatten (List.map (fun line -> tok (Op "|") :: line) lines),
1210+
<@ List.flatten (List.map (fun (line, _) -> tok (Op "|") :: line) lines),
11791211
Left (RBracket, ([], None)) )) in
11801212
let buf = Buffer.create 10 in
11811213
let s = Display.get () in

lib/parser/names.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type 'b ctx =
1616

1717
(* We also store a set of which variables already exist, so that we can be sure of creating a new unused one. *)
1818
type 'b t = { ctx : 'b ctx; used : StringSet.t }
19+
type wrapped = Wrap : 'n t -> wrapped
1920

2021
let empty : emp t = { ctx = Emp; used = StringSet.empty }
2122

lib/parser/names.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ open Core
55
open Term
66

77
type 'n t
8+
type wrapped = Wrap : 'n t -> wrapped
89

910
val empty : emp t
1011
val remove : 'b t -> ('a, 'n, 'b) Tbwd.insert -> 'a t

0 commit comments

Comments
 (0)