Open alleystoughton opened 1 month ago
It looks like the order on path either first separates by length (so a longer long name will always be ordered after a shorter one regardless of the relative ordering of their first elements), or always orders Self
(or Top
, or "empty") first.
Independently of this explanation is the question of whether this is desired or not. I don't think there is any way for us to do anything that both:
Here, 1 is clearly desirable, and 2 slightly less clearly so. But the fact that the order doesn't change just because you import GlobOddity1
is certainly behaviour: I don't want to have to revisit a bunch of indexing just because I decided to bring some symbols into my local namespace.)
So, likely, the best we can do is 1) pick something that makes sense, and 2) document it.
Now the question 1 of whether the current ordering on paths makes sense is good. I think it does if you think "local first". We could debate this, but we could also explain it.
And beyond simply documenting that explanation, one way to perhaps improve this in the tool itself is to always print the name we used in deciding the order, or perhaps print out comments clarifying long names that were used in ordering, but shortened by imports. (This might require allowing comments in the actual AST...)
Globals [# = 0]:
Prog. variables [# = 6]:
My.x : int
My.y : bool
Yours.x : int
Yours.y : bool
(* GlobOddity1.Make *)
Make.x : int
Make.y : bool
Thanks, François. We're in the position of trying to machine-generate EC code that uses the glob order. All I really care about is being able to predict it. (So for us, the output of print glob
isn't much of a help, except in understanding the underlying model.)
IMO, the solution is #615
I'm going to reopen this, because the current solution results in a typing anomaly. The reason is that the ordering used for paths treats longer paths as greater, but this isn't stable when multiple files are involved. More deeply, because the type path
is left-recursive, it encourages this ordering.
The following example has three files, GlobOddity1.ec
, GlobOddity2.ec
and GlobOddity3.ec
.
Here is GlobOddity1
:
module type T = {
proc f() : unit
}.
module M(X : T) : T = {
var x : int
proc f() : unit = {
X.f();
}
}.
This checks fine.
Here is GlobOddity2.ec
:
require import GlobOddity1.
module N = {
var x : bool
proc f() : unit = {
x <- true;
}
}.
print glob M(N).
(*
Globals [# = 0]:
Prog. variables [# = 2]:
N.x : bool -- really Top.N.x
M.x : int -- really Top.GlobOddity1.M.x
the second is longer, so greater, even though M comes before N
*)
(* so this typechecks: *)
op f (g : glob M(N)) : bool = g.`1.
And here is GlobOddity3.ec
:
require GlobOddity1 GlobOddity2.
This prints out
Globals [# = 0]:
Prog. variables [# = 2]:
M.x : int
N.x : bool
(note that the order has changed!) and then gives the error
In external theory GlobOddity2 [./globOddity2.ec: line 25 (30-34)]:
This expression has type
int
but is expected to have type
bool
(line 25 is the definition of f
, which no longer typechecks because the order of the glob
has changed).
The order changes because now M.x
is really Top.GlobOddity1.M.x
and N.x
is really Top.GlobOddity2.N.x
; the path lengths are the same, and so now M
comes before N
.
You may want to close this saying the solution is records, but I wanted to point out the problem with the current implementation.
There is indeed again an instability. The problem is that in Emacs, the name of the current theory is not the same (because we don't have the current filename). Maybe the order of declaration could be used, but this is a more intrusive fix.
I'm having trouble understanding the new glob order in a case involving multiple files.
The first file,
GlobOddity1
, isAnd the second is
If instead I make the entire example be in a single file, I get
Is this buggy behavior, or if not, what is the model for why the two file version produces apparently out of order results?