-
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
Give require
the same features as import
#1142
Conversation
You can now do (require foo), (require [foo [a b c]]), (require [foo [*]]), and (require [foo :as bar]). The first and last forms get you macros named foo.a, foo.b, etc. or bar.a, bar.b, etc., respectively. The second form only gets the macros in the list. Implements hylang#1118 and perhaps partly addresses hylang#277. N.B. The new meaning of (require foo) will cause all existing code that uses macros to break. Simply replace these forms with (require [foo [*]]) to get your code working again. There's a bit of a hack involved in the forms (require foo) or (require [foo :as bar]). When you call (foo.a ...) or (bar.a ...), Hy doesn't actually look inside modules. Instead, these (require ...) forms give the macros names that have periods in them, which happens to work fine with the way Hy finds and interprets macro calls.
require(entry, self.module_name) | ||
for entry in expression[1:]: | ||
if isinstance(entry, HySymbol): | ||
# e.g., (require foo) |
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.
REALLY minor nitpick, but this would be a little easier to read without the e.g.
, since I think it's obvious that it's an example.
|
||
|
||
@macro("qplah") | ||
def tmac(*tree): | ||
return HyList(tree) | ||
return HyList((HyInteger(8), ) + tree) |
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.
???
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.
So the difference between the two test macros is clearer, and both actually do something, so we can catch a bug in which the macro isn't being called and its argument is returned unaltered.
__import__(entry) | ||
require(entry, self.module_name, all_names=True, | ||
prefix=entry) | ||
elif (isinstance(entry, HyList) and len(entry) == 2 |
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.
This branch and the two that follow repeat a lot of code and don't handle stuff like (require [x [* *]])
correctly.
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.
(1) Regarding repetition, I don't think there's enough of substance to factor out, because each branch is small but requires small differences; can you specify what kind of refactoring you'd like to see? (2) What should (require [x [* *]])
do? Import a macro named *
twice? *
is a terrible name for a macro.
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.
@Kodiologist Probably be an error, though (import [x [* *]])
works for some weird reason.
for entry in expression: | ||
__import__(entry) # Import it fo' them macros. | ||
require(entry, self.module_name) | ||
for entry in expression[1:]: |
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.
It seems kind of odd that require
and import
handle a pretty much identical syntax in completely different ways. Would it somehow be possible to somehow merge the two?
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.
They use the same syntax but do pretty different things because import
actually emits code and require
just changes the state of the compiler. I think trying to unify the implementations wouldn't make the compiler code much nicer.
FWIW, I really like the idea behind this change, but this is quite literally going to one of the biggest breaking changes that have been done... |
This is good change in my opinion. It will be a bit of a pain to fix all of existing code, but it'll be worth it. |
@kirbyfan64 Are you happy with the changes I've made? Regarding the "e.g." in the comments, zackmdavis thumbed your comment down, and I guess I agree with him, although it's not very important in any case. |
@Kodiologist Whoops, forgot about this. |
@hylang/core Does this count as two acks now, since @tuturto said he agrees with the changes, or does someone else have to actually hit the "Approve" button? |
@Kodiologist Actually, no, I take that back: you forgot to update the docs! |
Okay, I'll take a stab at the docs now that everybody's happy with the feature itself. |
Done. |
parameter specifying the module which macros should be imported. Multiple | ||
modules can be imported with a single ``require``. | ||
``require`` is used to import macros from one or more given modules. It allows | ||
parameters in all the same formats as ``import``. The ``require`` form itself |
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.
If I read the code correctly, following doesn't work:
(require [foo [bar :as baz]])
but similar does work for import
though
(import [foo [bar :as baz]])
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.
Embarassingly, I had no idea that from foo import bar as baz
is legal Python, let alone supported in Hy. I'll add that form for require
.
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.
Done.
(require [mymodule [*]]) | ||
(foo 1) | ||
|
||
Macros that call macros |
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.
This is good addition. I got bitten by this multiple times, until I learned it hard way.
parameters in all the same formats as ``import``. The ``require`` form itself | ||
produces no code in the final program: its effect is purely at compile-time, for | ||
the benefit of macro expansion. Specifically, ``require`` imports each named | ||
module and then makes available each requested macro in the current module. |
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.
Should this just be worded "makes each requested macro available"?
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.
Yes, you're right.
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.
Looking good. Thanks for the hard work ♥
One implication that I didn't spot, but that was pointed out to me is that if we have a module with several macros that depend on each other, we either need to make sure all needed macros are actually required. This means that client code has to require them explicitly or with *, or that the macros themselves have to require them. This can make code somewhat ugly. What do you think, how big problem this is? |
You're welcome. :)
That's just the same problem as I described in "Macros that call macros", right? I think having the macros themselves require their dependencies is the right thing to do, as I said in the documentation. But you can reduce repetitive code by writing a function like:
which you'd use like:
And in general, actually, if you're writing some code specifically to be a subroutine for one or more macros, it's more convenient and less error-prone to write it as a function than a macro. |
It's just something to be aware of I guess. Since this has two acks and no open issues I'm aware of, I'll merge this into master. |
You can now do
(require foo)
,(require [foo [a b c]])
,(require [foo [*]])
, and(require [foo :as bar])
. The first and last forms get you macros namedfoo.a
,foo.b
, etc. orbar.a
,bar.b
, etc., respectively. The second form only gets the macros in the list.Implements #1118 and perhaps partly addresses #277.
N.B. The new meaning of
(require foo)
will cause all existing code that uses macros to break. Simply replace these forms with(require [foo [*]])
to get your code working again.There's a bit of a hack involved in the forms
(require foo)
and(require [foo :as bar])
. When you call(foo.a ...)
or(bar.a ...)
, Hy doesn't actually look inside modules. Instead, these(require ...)
forms give the macros names that have periods in them, which happens to work fine with the way Hy finds and interprets macro calls.If this all looks good, I'll update the documentation accordingly.