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

Give require the same features as import #1142

Merged
merged 5 commits into from
Nov 3, 2016

Conversation

Kodiologist
Copy link
Member

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 #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.

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)
Copy link
Contributor

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)
Copy link
Contributor

Choose a reason for hiding this comment

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

???

Copy link
Member Author

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
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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:]:
Copy link
Contributor

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?

Copy link
Member Author

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.

@refi64
Copy link
Contributor

refi64 commented Oct 27, 2016

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...

@tuturto
Copy link
Contributor

tuturto commented Oct 29, 2016

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.

@Kodiologist
Copy link
Member Author

@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.

@refi64
Copy link
Contributor

refi64 commented Oct 31, 2016

@Kodiologist Whoops, forgot about this.

@refi64
Copy link
Contributor

refi64 commented Oct 31, 2016

@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?

@refi64
Copy link
Contributor

refi64 commented Oct 31, 2016

@Kodiologist Actually, no, I take that back: you forgot to update the docs!

@Kodiologist
Copy link
Member Author

Okay, I'll take a stab at the docs now that everybody's happy with the feature itself.

@Kodiologist
Copy link
Member Author

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
Copy link
Contributor

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]])

Copy link
Member Author

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.

Copy link
Member Author

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
Copy link
Contributor

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.
Copy link
Contributor

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"?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, you're right.

Copy link
Contributor

@tuturto tuturto left a 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 ♥

@tuturto
Copy link
Contributor

tuturto commented Nov 2, 2016

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?

@Kodiologist
Copy link
Member Author

You're welcome. :)

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?

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:

(defn require-me [&rest body]
  `(do (require mymodule) ~@body))

which you'd use like:

(defmacro foo [n]
  (require-me `(mymodule.repexpr ~n (raw-input "Gimme some input: "))))

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.

@tuturto
Copy link
Contributor

tuturto commented Nov 3, 2016

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.

@tuturto tuturto merged commit 14fddbe into hylang:master Nov 3, 2016
@Kodiologist Kodiologist deleted the require-extensions branch November 3, 2016 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants