-
Notifications
You must be signed in to change notification settings - Fork 372
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
Macro processing updates and fixes #1682
Conversation
a3c26a5
to
dde28d7
Compare
Sounds pretty exciting. Want to add some tests? |
Doing just that — plus further necessary additions. I'm working somewhat incrementally on this one, so you folks can see/be involved in the process. |
The changes introduced here might also help address the following issues:
(Moved from initial comment) |
At a high level, how does calling |
When When that This form of namespace/module resolution is intentionally restrictive; for instance, it doesn't propagate into sub-expressions or apply to anything else other than macro expansion. This can easily be changed; however, I wanted to start by implementing just enough to make non-trivial macro work a little more tolerable. |
Interesting; thanks for the explanation. As the system gets more mature, we'll probably also want to change the order of checks so that the macro's defining module is checked first. Otherwise, a macro user can inadvertently shadow one of the macro's subroutines. |
That's a simple change to make, so I'll do it now before it causes confusion down the line. |
4b8b706
to
4b51635
Compare
Macro expansion now checks the macro's module's namespace first, then the "local" namespace. Finally, I rewrote the tests so that they use |
Nice. Are you done with this PR or are you planning more changes? |
Not yet; just noticed that the tag macros don't resolve in the correct order (didn't update the code in that place), so I need to fix that and add another test. Otherwise, I have some AST/source line numbering fixes to make. For example, On a related note, I think the two aforementioned changes could/should have their own PRs, though, so I'll end this one here (unless something more related to macros comes up). |
4b51635
to
2bbeeb8
Compare
All right, updated the last commit with the correct macro resolution order (for tags) and corresponding tests. |
Are these regressions introduced by this PR, or preexisting issues? |
Those are pre-existing issues. |
I tried the test I suggested at the top of #1268 with Python 3.6.6. Running
Likewise on master (in fact, that code I wrote hasn't worked, as written, since several releases ago). On master, running it again makes no difference, but with your PR, running it again prints:
Only one So I'm not sure that your PR is fault given that the code was already broken—all your PR has done is make it a bit less broken—but perhaps it's something to think about? |
The example in #1268 should work, and it does in Python 2.7, but not Python 3.x. I'm looking at it now. |
facc364
to
7211d6a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The substantive changes here are fantastic and long overdue, but let's separate out some of the other stuff.
7211d6a
to
f1ef679
Compare
37893f1
to
67e9d6a
Compare
There have been several attempts. I think #1166 is the latest. |
@Kodiologist, anything else needed for this one? |
Sorry, I'd assumed you were working on writing |
Turning |
It looks like you've undone the separation of deleting |
An explanation for those changes is given here. Otherwise, More importantly, why would we want to be so granular? Is there something special about |
Putting the functions in
Because the two things being accomplished in the combined commit are independent. One is deleting dead code. Another is moving code from one place to another, changing only what's necessary to support the move. With each change as its own commit, it's clear what's going on. On the other hand, putting both the move and deletion into one commit makes it not so obvious that one function doesn't end up surviving the move. This is the same reason why indentation changes to a large body of code should in a separate commit from functional changes to a small portion of that code. The changes are independent, and the reader will know to look at the functional-change commit much more closely than the indentation commit. |
|
I'm with you there, to be clear.
I don't see this. You could as easily delete The only reason that I deleted |
Because you could delete
I don't see how its irrelevance is a reason for an unnecessary commit. The opposite seems much more appropriate: because the function isn't used, there's no reason to emphasise its removal in the Git commit history! Also, decoupling If the purpose of the separate commit is simply logging, and nothing particularly functional, then it's best to mention the removal explicitly in the commit message. Still, I don't see why |
On the contrary, you could keep the function while getting rid of its original file, as I did originally.
I don't see that, either. Suppose you spun off the first change of a single character in this PR into its own commit, making it the new first commit of the PR. I'd ask you to squash it into the following commit, because on its own, the one-character change would make no sense. The change only makes sense as part of the larger change in the commit it was split off from.
Commits exist to group related changes together. If two changes are irrelevant to each other, then in general, they should be in different commits.
The purpose is not function but the legibility of history. Many sequences of commits can lead to the same final code, and are hence functionally identical, but some are more intelligible than others.
It's better to indicate a sequence of changes in the code itself, with a sequence of commits, than in prose, with the commit message. |
Just to be clear, you're arguing for situation A over B, no? For a repository with the following file in its index: ;; some_file.hy
(defn func-1 []
(print 1))
(defn func-2 []
(print 2))
(defn func-3 []
(print 3)) Situation ACommit 1--- a/some_file.hy
+++ b/some_file.hy
@@ -1,8 +1,5 @@
;; some_file.hy
-(defn func-1 []
- (print 1))
- Commit 2--- a/some_file.hy
+++ b/some_file.hy
@@ -1,7 +1,4 @@
;; some_file.hy
-(defn func-2 []
- (print 2))
- Commit 3deleted file mode 100644
index c2ff364..0000000
--- a/some_file.hy
+++ /dev/null
@@ -1,4 +0,0 @@
-;; some_file.hy
-
-(defn func-3 []
- (print 3)) Situation BCommit 1deleted file mode 100644
index 14ce434..0000000
--- a/some_file.hy
+++ /dev/null
@@ -1,10 +0,0 @@
-;; some_file.hy
-
-(defn func-1 []
- (print 1))
-
-(defn func-2 []
- (print 2))
-
-(defn func-3 []
- (print 3)) |
Maybe in some cases? I guess it depends on the actual situation. If an entire file is dead code, for example, then I could certainly imagine it making sense to delete the whole file in one commit. #1189 is an example I did. |
Similarly, in this case, if we were just deleting |
This function wasn't being used anywhere.
It's compatibility code, and there's not a lot of it, and having a module with the same name as a standard module can be a bit troublesome.
This commit adds just enough namespacing to resolve a macro first in the macro's defining module's namespace (i.e. the module assigned to the `HyASTCompiler`), then in the namespace/module it's evaluated in. Namespacing is accomplished by adding a `module` attribute to `HySymbol`, so that `HyExpression`s can be checked for this definition namespace attribute and their car symbol resolved per the above. As well, a couple tests have been added that cover - the loading of module-level macros - e.g. that only macros defined in the `require`d module are added - the AST generated for `require` - using macros loaded from modules imported via bytecode - the non-local macro namespace resolution described above - a `require`d macro that uses a macro `require` exclusively in its module-level namespace - and that (second-degree `require`d) macros can reference variables within their module-level namespaces. Closes hylang#1268, closes hylang#1650, closes hylang#1416.
f7e0202
to
aa9182d
Compare
All right, I may not understand it, but I've recreated that squashed commit and added it back. |
I guess the core problem is that Git doesn't have a notion of moving code. It can only track additions and deletions. So when you move code, make sure that in the commit where moving happens, only moving happens. When you want to make less superficial changes to that moved code (which is common, because you probably moved it because you're making related changes), do it in another commit, whether before or after the move. |
Woohoo! |
This PR changes the way macros are loaded and managed within modules/namespaces, their scope of evaluation, and how
require
statements are treated during the module bytecode loading process.Alongside those changes, a general refactoring was started that moves toward a
HyASTCompiler
that can operate in two fashions: 1) compilation that occurs within a module and allows macros to interact with the compiler (as they are currently configured to do), and 2) compilation that does not require a concrete module context but does not allow true macro-compiler interaction. The latter case could drastically simplify some of the module importing work-arounds that currently exist in this PR (e.g. when interacting withrunpy
).It addresses/closes the following issues:
require
doesn't compile to anything, which interacts badly with bytecode #1268require
and macro dependencies #1650Example
Before
After