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

The rules for where blocks of mutually recursive definitions end are unclear and confusing #3032

Closed
lukaszcz opened this issue Sep 13, 2024 · 1 comment · Fixed by #3275
Closed

Comments

@lukaszcz
Copy link
Collaborator

We should reconsider them. Ideally, we wouldn't have any restrictions, but it seems difficult to implement with the current concept.

@lukaszcz lukaszcz added this to the 0.6.7 milestone Sep 13, 2024
@lukaszcz lukaszcz changed the title The rules for where blocks of mutually recursive definitions are unclear and confusing The rules for where blocks of mutually recursive definitions end are unclear and confusing Sep 13, 2024
@paulcadman paulcadman modified the milestones: 0.6.7, 0.6.8, 0.6.9 Nov 7, 2024
@paulcadman paulcadman modified the milestones: 0.6.9, Sanalejo Dec 2, 2024
@janmasrovira janmasrovira self-assigned this Jan 10, 2025
@janmasrovira
Copy link
Collaborator

janmasrovira commented Jan 10, 2025

currently we separate definitions in two categories:

  1. Non-definitions: import, open and local module.
  2. Definitions: The rest (type definitions, function definitions, etc.).

The idea behind this categorization is that Non-definitions have imperative-like semantics. I.e. an import or an open statement should only affect definitions that come after it. Whereas a function definition should be able to be forward referenced.

Currently local modules also fall in the Non-definition category, but I don't see a reason why it should be this way. In fact, I think that most confusions would be solved by moving local modules to the Definitions category.

The following example highlights the crux of the issue:

module Example1;

axiom a : Type;

type T :=
  mkT@{
    t : R;
  };

-- POINT A

-- x1 : T -> a := T.t1;

type R :=
  mkR@{
    r : T;
  };

-- POINT B

y1 : R -> a := R.r1;
  1. With the x1 definition commented out the example typechecks. The generated local modules for types T and R are put at POINT B, making it possible for the field t : R to forward reference R.
  2. However, if we uncomment the x1 definition then it fails to typecheck. The reason is that the generated local module for T is put at POINT A, making it impossible for t : R to reference R.

If we treated local modules as forward-referenceable definitions we wouldn't have this issue.

janmasrovira added a commit that referenced this issue Jan 28, 2025
This pr makes a number of changes to the Scoper. The most important
change is that local modules can be forward referenced. Below I list all
the changes.

- Closes #3032 

# Referencing local modules
Local modules can now be forward referenced. However, there is one
restriction:
1. When forward referencing a local module. The symbols that come from
an `open public` are **not** yet in scope, so they can't be referenced.
E.g.
   ```
   -- at this point, P.p is in scope, but P.m is not

   module P;
     axiom p : Type;

     open M public;
   end;

   -- at this point, both P.p and P.m are in scope

   module M;
     axiom m : Type;
   end;
   ```

# Forward reference passed an import, open or module statement
The following example was not allowed but now it is.
```
axiom A : B;
import M;
module S;
end;
axiom B : Type;
```

# Changes in operators and iteratos
The semantics of operator and iterator definitions have changed
slightly. Before, when the scoper found `syntax operator , pair`, it
would remember in the state that when the `,` symbol is defined in the
current syntax block, it should have the fixity `pair`. These had some
implications.
1. It was not possible to reassign/overwrite a fixity.
1. The `,` symbol in the `syntax operator , pair` is not highlighted and
it doesn't support going to definition.
2. We required special checks to throw an error when we had duplicate
operator/iterator definitions. We also had a check for when a symbol
used in an operator/iterator statement was not defined in the same
syntax block.

The new behaviour is as follows.
1. When the scoper finds `syntax operator , pair`, the symbol `,` is
scoped using the regular scoping rules - so `,` must be in scope. Then,
the scoper modifies the fixity for `,` in the current scope.
2. It is now possible to overwrite the fixity of a symbol. So this would
be possible.
```
syntax operator , pair;
p1 : Pair Nat Nat := 1, 2;
syntax operator , none;
p2 : Pair Nat Nat := , 1 2;
``` 
6. It is now possible to have qualified names in syntax/iterator
definitions. E.g. `syntax operator Pair., pair`.

Because of point 1) we might encounter some breaking changes.
1. ```
   syntax operator mkpair none;
   syntax alias mkpair := ,;
   ```
This is now invalid because `mkpair` is not in scope in the `syntax
operator`
   statement, because aliases cannot be forward referenced.
2. A `syntax operator op fix` will now throw an ambiguity error if `op`
is both defined in this module and imported from somewhere else.
# Qualified fixities
It is now possible to reference fixities using qualified names. E.g.
```
syntax operator , Fixities.pair;
```
# Pending tasks

- [x] Add positive test for local module forward referencing.
- [x] Add positive test for fixity overwriting.
- [x] Add positive test for qualified fixity.
- [x] Add positive test for qualified operator.
- [x] Add negative tests for local module forward referencing
restrictions.
- [x] Fix printing of operators in Core.
- [x] Cleanup the code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants