-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Changes focus in require explanation #6041
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
Conversation
@@ -507,13 +507,12 @@ defmodule Kernel.SpecialForms do | |||
defmacro alias(module, opts), do: error!([module, opts]) | |||
|
|||
@doc """ | |||
Requires a given module to be compiled and loaded. | |||
Tells Elixir you want to use the macros defined in a 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.
Maybe: "Requires a module in order to use its 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.
I like @josevalim's suggestion.
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 we put on the shoes of an Elixir programmer, what does "Requires" mean?
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 structure I have in mind is:
- Opt-in to use macros
- Expand with example etc.
- (Maybe) BTW, Elixir technically needs to compile and load the module if not available (just so you know it in case you are suprised seen the code being run).
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 structure looks good to me, but I would mention "requires" just because the term we refer to this process with is "requiring", so mentioning it is not a bad thing IMO.
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.
Hmmm.
I think the docs can use "requires" elsewhere, meaning "what require
does". But using that verb in the description of require
itself seems circular to me. I don't know what is "requiring" while defining "requiring", you see what I mean.
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.
I see what you mean, but the short line of documentation at the top is meant to just remind you what a function does or give you a slight idea so that you can go to the full docs if you need to use it. We do the same for alias
and import
btw, where we mention aliasing and importing respectively.
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.
Agree that "telling Elixir" is maybe too informal.
The programmer should know that while public functions are callable, public macros are not unless you opt-in.
require
could be renamed to allow_me_to_call_public_macros_from
.
Let's see if we can come with a better wording!
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.
@whatyouhide didn't see that last remark before writing my last comment. Good then, I'll revise the patch in a minute.
I agree with the new direction. Maybe we should just discuss what should be the best phrasing. Let's wait for more feedback besides mine before doing further changes. :) |
Let me add a different angle to the argument. If opting-in was not necessary, the current explanation would be good. But since opting-in is necessary, from the point of view of the programmer that is all is needed to say. And Elixir knows that in addition to mark the module as required, it has to ensure it is loaded in the VM to make the macro available (in whatever phase). But that is internal. It could be said, maybe, that Elixir is going to compile and load the required module as a side-effect, in case that matters to the user, but that would be an additional remark, rather than the defining functionality. |
Btw, I agree with the premise that knowing the module is loaded or not is irrelevant. :) |
Notice that usually modules should not be required before usage, | ||
the only exception is if you want to use the macros from a module. | ||
In such cases, you need to explicitly require them. | ||
Exported module functions are globally available, but in order to use |
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.
"Exported module functions" reads somehow a bit clunky to me personally. What if we go with
All the functions that a module exports are available everywhere, but in order to use [same as before]
Thoughts?
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.
Good!
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.
And we don't talk about exports from the Elixir perspective. I guess we can just say "Modules are globally 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.
Mh but it's nice to say that "functions in modules are globally available" IMO. We could say "Public functions in modules are globally available" since we do talk about public/private.
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.
Ahhh, wait. Too much Erlang these days :), in Elixir that would be "public" I guess :).
@@ -507,13 +507,12 @@ defmodule Kernel.SpecialForms do | |||
defmacro alias(module, opts), do: error!([module, opts]) | |||
|
|||
@doc """ | |||
Requires a given module to be compiled and loaded. | |||
Tells Elixir you want to use the macros defined in a 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.
I like @josevalim's suggestion.
All feedback addressed ❤️ . |
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.
Looks beautiful, your PRs are always a pleasure to review and merge 💟
❤️ 💚 💙 💛 💜 |
Great addition @fxn |
I think from a programmer's perspective, the fact that in order to expand macros the compiler needs to ensure the module is compiled and loaded is not relevant.
The compiler needs the module loaded in memory to have their functions available (a macro is a function prefixed with "MACRO-"), because it may need to call the function early in the expand phase. Note that the module may be loaded in memory when expansion happens:
by the time
is_odd
has to be expanded,Elixir.Integer
is already loaded in the VM, and thus the macro available for dispatch. But Elixir errs, because the code does not opt-in. Since Elixir knows the macro exists, it gives you a better error message ("you need to require in order to use a macro", or "there is a macro with the same name"), than the one it can give if the module is not even loaded ("unknown function").From my perspective, that is not really the concern of the programmer. The programmer does not care about what the compiler needs to do. For example, the parallel compiler may need to compile a module if needed as well, and the programmer does not need to say so.
What the programmer cares in my view is that they have to opt-in. This is the spot in the source code where opting-in is stored. And that is a design decision that I suppose has as a goal to prevent macros from leaking everywhere. You need to consciously opt-in.
In order to make the macro available in the scope, Elixir may need to compile and load and therefore
require
also ensures the module is loaded and compiled, but that is implementation (again, as I see it).