Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Symbols.t by Var.t in binders and substitutions #976

Merged
merged 5 commits into from
Nov 27, 2023

Conversation

Halbaroth
Copy link
Collaborator

Alt-Ergo embeds variables into symbols then expressions. It's a good design but at many places in the codebase, we use symboles where variables are clearly sufficient.

For instance, we shouldn't use symbols in substitutions or binders because we always bind a variable to an expression or substitute a variable by an expression.

Let's use the simpler type Var.t as much as possible.

This commit should be safe because the comparison functions of Symbols.t and Var.t agree on variables.

Comment on lines +62 to +64
let underscore =
Random.self_init ();
of_string @@ Format.sprintf "_%d" (Random.int 1_000_000)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, I was not aware we were doing that — this does not seem safe at all! We should use a counter instead (there are no guarantees that two consecutive Random.int calls won't return the same value, which would be problematic as forall x, y : int. x + y would become forall _1, _1: int. _1 + _1 which… is not the same at all.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #977

Copy link
Collaborator Author

@Halbaroth Halbaroth Nov 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ah :) you don't know the best part of our mighty codebase I see :)
This snippet is pure gold. I shared it one year ago on zulip, best joke of my short software developer career.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! But what I said above is not true — underscore is a global variable, not a function. OK, that makes things better — there can "just" be conflicts with a variable named _n for some n that changes each time Alt-Ergo is run. We should just call this "._" instead, no?

@@ -116,12 +116,13 @@ let rec make_term up_qv quant_basename t =

| TTlet (binders, t2) ->
let binders =
List.rev_map (fun (s, t1) -> s, mk_term t1)
List.rev_map (fun (s, t1) ->
match s with Sy.Var v -> v, mk_term t1 | _ -> assert false)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this invariant really always true? In that case, could we replace the type of binders by a list of Var.ts?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I'm checking it right now. I forgot to do it before publishing the PR. I hope the legacy parser enforce this invariant (the new one does).

Copy link
Collaborator Author

@Halbaroth Halbaroth Nov 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, before submitting this PR, I tried to change the type of the binders in the legacy typechecker. This will requires to change many types, mostly because the typechecker aggregates the symbols in a context, so embed variables into symbols makes the design simpler here. I decided to keep the code as before. The only place where we produce a new TTlet term is here:

| PPlet(l, t2) ->
let _ =
List.fold_left (fun z (sy,_) ->
if Util.SS.mem sy z then
Errors.typing_error (DuplicatePattern sy) loc;
Util.SS.add sy z
)Util.SS.empty l
in
let rev_l = List.rev_map (fun (sy, t) -> sy, type_term env t) l in
let env =
List.fold_left
(fun env (sy, te1) ->
let ty1 = Ty.shorten te1.c.tt_ty in
let fvar s = Symbols.var @@ Var.of_string s in
Env.add env [sy] fvar ty1
)env rev_l
in
let te2 = type_term env t2 in
let ty2 = Ty.shorten te2.c.tt_ty in
let l = List.rev_map (fun (sy, t) -> fst (Env.find env sy), t) rev_l in
Options.tool_req 1 (append_type "TR-Typing-Let type" ty2);
TTlet(l, te2), ty2

As you can see line 926, we store new variables in the environment of the typechecker and we retrieve them at the line 932 to produce the list of binders. Thus, the binders are always variables (embedded in symbols).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest you add to the assert false a TODO stating that we could actually simplify but it would require to update some legacy code which was out of the scope of the PR adding this assert false (and a reference to typechecker.ml).

let fresh_sy = Sy.fresh_skolem ~is_var:true "Pur-Ite" in
mk_term fresh_sy [] t.ty , add_let fresh_sy t lets
let fresh_var =
match Sy.fresh_skolem ~is_var:true "Pur-Ite" with
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is better to add a Symbols.fresh_skolem_name instead of creating a var and destroying it with an assert false (or simple export the SkolemId from symbols)? I wonder if the current fresh_skolem function is still relevant: is there still cases in your PR where fresh_skolem is called with without creating a var?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll check. Good catch.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function Symbols.fresh_skolem isn't necessary anymore. I create a two functions fresh_skolem_string and fresh_skolem_name. It would be better to extract the module of identifiers from the Symbols module and create a function fresk_skolem in Var but it's a small detail.

@@ -116,12 +116,13 @@ let rec make_term up_qv quant_basename t =

| TTlet (binders, t2) ->
let binders =
List.rev_map (fun (s, t1) -> s, mk_term t1)
List.rev_map (fun (s, t1) ->
match s with Sy.Var v -> v, mk_term t1 | _ -> assert false)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest you add to the assert false a TODO stating that we could actually simplify but it would require to update some legacy code which was out of the scope of the PR adding this assert false (and a reference to typechecker.ml).

@Halbaroth Halbaroth enabled auto-merge (squash) November 27, 2023 12:46
@Halbaroth Halbaroth disabled auto-merge November 27, 2023 13:22
Alt-Ergo embeds variables into symbols then expressions. It's a good
design but at many places in the codebase, we use symboles where
variables are clearly sufficient.

For instance, we shouldn't use symbols in substitutions or binders
because we always bind a variable to an expression or substitute a
variable by an expression.

Let's use the simpler type `Var.t` as much as possible.

This commit should be safe because the comparison functions of
`Symbols.t` and `Var.t` agree on variables.
We need to create fresh variables without embed them in symbols as
we now store binders in variable maps.
@Halbaroth Halbaroth merged commit 4b89c12 into OCamlPro:next Nov 27, 2023
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants