import, require, and use

738 views
Skip to first unread message

Anthony Grimes

unread,
Aug 12, 2013, 4:39:36 PM8/12/13
to elixir-l...@googlegroups.com
I was thinking about the import, require, and use earlier and wanted to open up a bit of discussion about them.

So, if I understand things correctly, they do the following:

  • `require` is "I'd like to be able to use the macros in this module, please."
  • `use` is "Please execute __USING__ in this module, please."
  • `import` is "I'd like to use these functions (maybe all of them) without qualifying them."
I think that having all three of these is confusing. In particular, 'require'. 'require' does not make a lot of sense to me. How does the word 'require' translate to 'gimme the macros'? Why do we need something separate for it? Furthermore, the word 'use' and the word 'require' share some common ground with each other. I get confused about what each of these things do if I haven't used them in the past 4 hours.

Is there a reason all of these things have to be separate? I could see a scenario where 'require' and 'import' could be the same thing. Something like

import Foo, macros: true

The problem with this is that you end up importing Foo *and* its macros, when perhaps all you wanted to do was get the macros. Another option might be to make it so importing something doesn't automatically import everything in the module. For example, like require in Clojure,

import Foo, refer: :all

'refer' would replace 'only' and would be able to take this new :all keyword to mean "import everything from this module". If you didn't pass it and only passed `macros: true`, it'd be just like require is today.

Given this, one could potentially extend it to add `import Foo, use: true` or something. A bigger problem is that 'use' does not tell me what it does at all. It seems more like an 'execute hook' kind of thing.

At this point, it'd be very much like Clojure's 'require':

(ns foo
  (:require [foo.bar :refer [1 2 3]]
               [baz.quux :refer :all]))

In any case, that's my nuclear proposal that requires lots of huge breaking changes and people are inevitably not going to agree with and hate, so my second proposal is just that we put more consideration behind our naming of these things:

  • How does 'require' communicate that we're asking to use macros?
  • How does 'use' say we're going to execute __USING__ and such?
If there are already clear definitions behind these things that I'm missing then I will certainly accept it, but it's still worth looking at given that I've been using Elixir for a while now and these names still make no sense to me. In the case of require, if it had simply been called something like 'require_macros', I don't think I'd have been confused. I'm not sure what I'd rather 'use' be called. I certainly don't like 'use' because it doesn't tell me at all what is going on, but I can't think of a better name that would at the moment.

Anyways, I figured this worthy of a bit of discussion!

Anthony Grimes

unread,
Aug 12, 2013, 4:45:49 PM8/12/13
to elixir-l...@googlegroups.com
ericmj on IRC pointed out that `import Foo` requires macros as well. At this point, it seemed like `import Foo, only: :macros` was another option. But then he pointed out that with 'require' alone one must qualify macros, whereas with 'import Foo' and its implicit require one does not have to qualify them, so that proposal wouldn't quite work. This really seems way too confusing.

Christopher Allen

unread,
Aug 12, 2013, 4:54:43 PM8/12/13
to elixir-l...@googlegroups.com
Words like require/use/import/load are largely interchangeable. In the absence of needing to distinguish between Erlang vs. Elixir libraries, is there a need for more than one verb here?

require lib

lib/verb

require lib :refer [verb]

verb

Why are macros treated differently? They're just functions where I'm from.


On Monday, August 12, 2013 1:39:36 PM UTC-7, Anthony Grimes wrote:

Alexei Sholik

unread,
Aug 12, 2013, 6:43:23 PM8/12/13
to elixir-lang-core
The abundance of verbs with slight differences is unfortunate here. However, I don't see a clear way to fix the situation. I like Anthony's proposal to unify "require" and "import", but I'm not sure if it is a good idea.

Let me first clarify why `require` is necessary. Function calls in Erlang are dynamic -- when you compile module A that calls a function from module B, B might not even exist at that time. A will still compile fine. Elixir inherits this behavior.

Now consider what happens if B defines a macro "mymac", and A calls it like this: "B.mymac". Elixir has no idea whether it's a macro or a function. Without "require B", it assumes a function and compiles A successfully. But when A is executed and it tries to call B.mymac, it fails because there is no such function.

Using "require" is necessary to tell the compiler "this call here is actually a macro call, so please load this module for me to be able to use macros from it during compilation".

---

I have taken Anthony's idea a bit further to see what we could end up with: http://pastebin.com/48LZWmPi. This would allow us to get rid of "require" at the expense of overloading "import". Like I said, I don't see it as a clear benefit because referring names in lexical scope has little to do with the necessity to require macros as explained above.

---

The intent of "use" is different still and I don't see it how it could be merged with import. Use is really just a built-in convention. We could get rid of it and replace each "use" call with

    require Module
    Module.initialize()

The benefit of "use" is that it provides a single way to perform the above. If it was absent, people could name their initialization functions different names, require different arguments and so on.

I'm not sure where your confusion about "use" stems from. If you use it, you have read about it in the Getting Started guide or in the docs. If the confusion is because of "use" being used in other languages for different purposes -- that's tougher to address.

---

To conclude, all three have different purpose and semantics:

* `require M` is only needed to let the compiler know about macros defined in module M. There is no way for Elixir to automatically require a module without additional overhead for each call at compile time or changing semantics of function calls;

* `import M` is a mechanism to modify lexical scope with definitions from M;

* `use M` is a built-in convention for modules that require some amount of setup before use. Usually inside "__using__" macro you will use a combination of `require` and `import` calls, not necessarily to the same module. Thus, unifying it with import will limit the flexibility it currently has.



--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Best regards
Alexei Sholik

Alexei Sholik

unread,
Aug 12, 2013, 6:48:39 PM8/12/13
to elixir-lang-core
Words like require/use/import/load are largely interchangeable.

Well, these are all different words if you look up the definitions. The fact that different languages may use one word for different purposes does not imply the words themselves are interchangeable.

I agree that "use" on its own does not give any information about what it does. The other two are more or less descriptive:

* "require M" signals the compiler that M is _required_ to be loaded before compiling the module with the call to "require".

* "import M" should be pretty straightforward as it's used for similar purposes in other langs: import names from M into the current scope.


On Mon, Aug 12, 2013 at 11:54 PM, Christopher Allen <c...@bitemyapp.com> wrote:

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Alexei Sholik

unread,
Aug 12, 2013, 6:53:11 PM8/12/13
to elixir-lang-core
In the link to pastebin, please disregard the second line:

       import M, as: B                           # same as the current behavior

I actually think this option should be removed. It doesn't make sense to me to import everything from M and make an alias for it.

Christopher Allen

unread,
Aug 12, 2013, 6:55:42 PM8/12/13
to elixir-l...@googlegroups.com
The point is the verbs and their definitions are arbitrary without any apparent design or thought behind them, with a serious lack of orthogonality to boot.

Alexei Sholik

unread,
Aug 12, 2013, 6:55:47 PM8/12/13
to elixir-lang-core
Lastly, there is still some appeal about unifying "require" and "import" that I can point out: both preload the module.

In the case of require, the module is preloaded to make its macros available.

In the case of "import", the module is preloaded because it is "required" and to check if it has the specified functions (ones listed with "only").
Message has been deleted

José Valim

unread,
Aug 13, 2013, 4:00:43 AM8/13/13
to elixir-l...@googlegroups.com
The point is the verbs and their definitions are arbitrary without any apparent design or thought behind them

They are not arbitrary. Alexei just explained what each of them do, with a lot of detail. Assuming that two special forms (require and import) in the language were simply slapped together with no thought process is misguided at best.

Alexei already went into details about what they do and why they work like that. The confusion here seems to be require, which requires a module to be available. Of course, at the moment you import a function or a macro from another module, the imported module needs to be available, so we know what it is importing, so it has to do a require behind the scenes.

On the other hand, use is meant to be a common extension point that invokes a macro in a module. But, to invoke the macro, we need to require it first.

So import and use are orthogonal, one imports and purely lexical while the other actually executes user code. I think putting them together would be a disaster, as we are mixing lexical behaviour with code that could literally do anything, including writing to your filesystem.

require and import could be put together, as they are both lexical, work on modules and an import does a require. We remove one word from our vocabulary by putting them together. Basically, the functionality we have today is:

* Ensure Foo is available (today's require Foo)
* import everything in Foo, except __?__ functions (today's import Foo)
* import macros from Foo (today's import :macros, Foo)
* import functions from Foo (today's import :functions, Foo)
* import Foo with only or except (today's import Foo, only: [] | except: [])

Here is my proposal: https://www.refheap.com/17558

Advantages:

* Importing everything (which is often a bad practice) is now explicit as "import Foo, :all";
* require is gone;

There are two issues with it though:

* Today, "import Foo" means import all of Foo except functions/macros starting with "__", as they are often callbacks. In the example above, we are doing "import Foo, :all". If we say :all should include everything, skipping underscored functions can be contradictory. However, I cannot think that anyone, ever, would like to include the callback ones (and importing them actually leads to local conflicts)

Raynes pointed out that, in the proposed syntax, you only need to import modules (as in import Foo) that contain macros. This is no different than with require, but does using import instead of require make it more confusing, less confusing, or equally confusing?

Feedback is welcome,

Anthony Grimes

unread,
Aug 13, 2013, 4:08:17 AM8/13/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
For clarification, José and I came to these conclusions and this proposal after a very long discussion on IRC, and I entirely support this proposal and have the same issues with it that he does, particularly the bit about importing only things that you need macros from. In this case, the separation of these two things was actually kind of useful.

import Foo.Bar

I feel like this is actually going to end up being just as confusing as having separate require, alias, and imports was. You don't need to do this if you just want to access functions like Foo.Bar.baz/1, you only need to do it with macros. This is already going to be something that no user is going to expect, so I'm currently debating whether or not even bothering to combine these things is worth it when there is still going to be confusion and explanation overhead.

Nonetheless, it's open for discussion!

Saša Jurić

unread,
Aug 13, 2013, 5:04:12 AM8/13/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
I like the import options from the proposal. It seems more intention revealing than the present state. I especially like how alias is taken out of the picture.
However, I find the parameterless import, which is supposed to substitute require, a bit misguiding. When I write import Foo, Foo is not imported, but just required, right?

Alexei Sholik

unread,
Aug 13, 2013, 5:24:43 AM8/13/13
to elixir-lang-core
Same as Saša.

One note: "alias" does not do anything to ensure the module is available, right? Then it will be a slight change in semantics if we go with "import Foo, as: Bar"


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

José Valim

unread,
Aug 13, 2013, 5:25:44 AM8/13/13
to elixir-l...@googlegroups.com
One note: "alias" does not do anything to ensure the module is available, right? Then it will be a slight change in semantics if we go with "import Foo, as: Bar"

Correct. For this reason, it is likely alias will remain. import Foo, as: Bar will just be a convenience.

Sasa Juric

unread,
Aug 13, 2013, 5:33:56 AM8/13/13
to elixir-l...@googlegroups.com
If import Foo, as: Bar is conceptually treated as "import lexically Foo in the current module, referencing it as Bar", I would argue it has the same semantics as the remaining import clauses. The fact that nothing ensures the module availability is an implementation detail.

A semi related question: will / should this work
  import Foo, :macros, as: Bar
?

On Aug 13, 2013, at 11:25 AM, José Valim wrote:


One note: "alias" does not do anything to ensure the module is available, right? Then it will be a slight change in semantics if we go with "import Foo, as: Bar"

Correct. For this reason, it is likely alias will remain. import Foo, as: Bar will just be a convenience.

--
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/EsiO881g2MA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.

Alexei Sholik

unread,
Aug 13, 2013, 9:10:55 AM8/13/13
to elixir-lang-core
> If import Foo, as: Bar is conceptually treated as "import lexically Foo in the current module, referencing it as Bar", I would argue it has the same semantics as the remaining import clauses. The fact that nothing ensures the module availability is an implementation detail.

But the question here is "does it ensure module availability or not?". Currently, "alias" doesn't, "import" does:

    defmodule M do
      def donut do
        import Donut, as: Alias
      end
    end

    $ elixirc imp.ex
    == Compilation error on file imp.ex ==
    ** (CompileError) /Users/alco/Documents/git/elixir/imp.ex:3: module Donut is not loaded and could not be found

I think the current "alias" is useful on its own, when you don't want to actually import or require anything and just want to shorten a module name. And if we make "import as:" behave as the current alias, it might as well be extracted into another keyword because it will have different behavior from all other "import" forms.


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.

José Valim

unread,
Aug 13, 2013, 9:15:17 AM8/13/13
to elixir-l...@googlegroups.com
But the question here is "does it ensure module availability or not?". Currently, "alias" doesn't, "import" does:

We will keep exactly those semantics. alias for anything (no checks) and import with :as will check it exists and do an alias.

Sasa Juric

unread,
Aug 13, 2013, 9:23:57 AM8/13/13
to elixir-l...@googlegroups.com
Argh, I didn't realize import A, as: B already exists. Sorry for the noise.

On Aug 13, 2013, at 3:15 PM, José Valim wrote:


But the question here is "does it ensure module availability or not?". Currently, "alias" doesn't, "import" does:

We will keep exactly those semantics. alias for anything (no checks) and import with :as will check it exists and do an alias.

Yurii Rashkovskii

unread,
Aug 13, 2013, 11:24:36 AM8/13/13
to elixir-l...@googlegroups.com
José,

Thanks for circulating a proposal. Some good thought process was put into it.

However, I believe that this proposal should not be rushed. 

Firstly, it creates a backward-incompatible change. 

Secondly (and most importantly) I don't see how overloading "import" is any less confusing. Effectively, you're offering two meanings of "import" — one that does not import functions and macros and one that does. With this exchange, I wouldn't be surprised if there will be a clone of this thread some time in the future, but about "these different imports confuse me". Require and import are different in their meaning. Require is not a variation of import.

In my opinion, having Alexei's concise explanations in the documentation (or a slightly extended version of those) in one place would very likely be a much better solution. Whenever somebody will be confused, they should be pointed to that piece of documentation.


--

José Valim

unread,
Aug 13, 2013, 12:40:02 PM8/13/13
to elixir-l...@googlegroups.com
Secondly (and most importantly) I don't see how overloading "import" is any less confusing. Effectively, you're offering two meanings of "import" — one that does not import functions and macros and one that does.

Of course, the proposal only makes sense if we can overload the meaning of import and get something out of it. The proposal is an experiment to check if we could change import to mean "importing a module" and, after importing a module, we can optionally import functions and macros from this module:

    import Foo.Bar # imports just the module
    import Foo.Bar, :all # imports the module, along side its functions and macros

I think this *could* be a valid interpretation. As I said, we overload at the cost of removing a word from our vocabulary. Now, if it will actually solve any confusion, it is something much harder to grasp.

Anthony Grimes

unread,
Aug 13, 2013, 2:07:06 PM8/13/13
to elixir-l...@googlegroups.com
Entirely in agreement. Nobody wants this to be rushed for sure. I merely felt it was necessary to come up with an alternative (if only to shoot it down) so that we have a point of reference in the future if this comes up again.

I agree that better documentation will help. The Getting Started guide skims over 'require' too much at the moment, so seeing that reworded would be helpful.

Overall, I like the proposal all except for the fact that we'd be replacing one question with another one. The question right now is "Why do we have require and import and all this noise?", we'd be making it "Why do I only have to import some modules but not others?". Like you said, I'm not sure if it helps confusion enough to warrant such a big change.

José Valim

unread,
Aug 14, 2013, 2:39:44 AM8/14/13
to elixir-l...@googlegroups.com
Overall, I like the proposal all except for the fact that we'd be replacing one question with another one. The question right now is "Why do we have require and import and all this noise?", we'd be making it "Why do I only have to import some modules but not others?".

We need to answer the second question anyway when talking about requires. Even if we are thinking we are replacing one question by the other, the second one is much easier to answer:

"You can import any module and importing it will guarantee it is available at compilation time. Since, in order to use macros, the macros need to be available at compilation time, you always need to import a module before using its macros."

This is exactly the same explanation we have for require today.

José Valim
Skype: jv.ptec
Founder and Lead Developer

Anthony Grimes

unread,
Aug 14, 2013, 2:14:01 PM8/14/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
That's a good way of looking at it. This and the long discussion we had about it all on IRC last night puts me right on the same page as you. I'm still for this change, leaving use out of the picture because it does something completely unrelated (even if it does have a bit of an unfortunate name). Cutting down from 4 things all using names that can be interpreted as the same thing to two things is *way* better.

Cheers.
Reply all
Reply to author
Forward
0 new messages