Skip to content

Instantly share code, notes, and snippets.

@rwdaigle
Last active February 10, 2016 22:18
Show Gist options
  • Save rwdaigle/d11bf4ff5466324b0951 to your computer and use it in GitHub Desktop.
Save rwdaigle/d11bf4ff5466324b0951 to your computer and use it in GitHub Desktop.
Why are there so many Elixir directives? Capture of discussion on #elixir-lang w/ Jose Valim and Chris McCord.
*Note: this conversation was santized only in that unrelated messages were removed from the stream*
[10:19] == rwdaigle [47b7ec4a@gateway/web/freenode/ip.71.183.236.74] has joined #elixir-lang
[10:22] <rwdaigle> chrismccord: we chatted a bit on twitter about this the other day, so I wrote up my thoughts on there being too many Elixir directives - this is an unpublished draft FYI: https://medium.com/@rwdaigle/89190b05560
[10:26] <chrismccord> rwdaigle : "I contend, though there may be implementation reasons for this many directives, their multiplicity is detrimental to the new developer experience."
[10:26] <chrismccord> rwdaigle : this probably sums up the situation well
[10:26] <chrismccord> rwdaigle : 1) they are necessary, and must be understood
[10:26] <chrismccord> rwdaigle 2) they can initially be confusing to newcomers
[10:27] <chrismccord> that said, I don't believe we can collapse them
[10:27] <badger> Was thinking that. There's always a little confusion over the "use" keyword though.
[10:27] <chrismccord> rwdaigle : " And “alias” and “import” are so similar in their purpose that it seems petty to have them be seperate directives."
[10:28] <chrismccord> rwdaigle : alias is very different from import
[10:28] <chrismccord> import is pulling functions into your context from another module
[10:28] <chrismccord> rwdaigle : alias literally only "aliasing" a module name to something else
[10:28] <chrismccord> it does not change your local context
[10:29] <Nicd-> what is the use for require?
[10:29] <gazler> Nicd-: Making sure a module is available at compile time.
[10:29] <Nicd-> if I import or alias something (or call in some other way), doesn't it crash anyway if the module is not available?
[10:30] <gazler> Usually for macros.
[10:30] <gazler> s/Usually//
[10:32] <gazler> Nicd-: You can't use a macro without requiring the module first.
[10:32] <chrismccord> rwdaigle : wrt to having to require before use, check this https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/kernel.ex#L3637
[10:33] <chrismccord> rwdaigle : all `use Foo` is really is just `require Foo; Foo.__using__([])`
[10:33] <rwdaigle> chrismccord: I struggle with the pragmatic difference between alias and import - why would I alias something when I'm not intending to reference it in the local context?
[10:33] <chrismccord> rwdaigle : import imports function into your local context
[10:33] <gazler> alias Foo.Bar allows you to write `Bar.baz`
[10:34] <gazler> import Foo.Bar` allows you to write`baz()` instead of `Bar.baz()`
[10:34] <rwdaigle> chrismccord: and why do I import functions into my local context - to invoke them without having to use the full module namespace, right?
[10:34] <chrismccord> rwdaigle : I may want to write `User.name(user)` intstead of `MyApp.Admin.User.name(user)`
[10:35] <chrismccord> rwdaigle : but imagine if imported MyApp.Admin.User
[10:35] <chrismccord> `name`
[10:35] <chrismccord> could clash with local functions, and it could clash with other imports (if we don't use alias)
[10:36] <chrismccord> rwdaigle : yes. It's case by case
[10:36] <kronicdeth> I personally prefer `alias` over `import` because I can just visually see where a function is defined.  import can import everything and then you don't really  know which module a function is defined in
[10:36] <gazler> I rarely use import without the `only` option.
[10:36] <chrismccord> rwdaigle : also importing everything makes it less clear where the functions are coming from
[10:37] <rwdaigle> chrismccord: I guess I contend the reason a developer would alias or import is the same - a more convenient way to reference a function. Whether the functions are imported locally or just have an aliased name are mostly immaterial (to the purpose - you obviously need to be aware of them for conflict issues)
[10:38] <gazler> rwdaigle: You alias modules, you import functions.
[10:39] <rwdaigle> chrismccord: I think I'm talking around you, but it feels more like a design issue than a documentation/understanding one... Maybe I need to just get deeper into it
[10:39] <chrismccord> rwdaigle : I think once you get deeper you'll come around. I agree it can be confusing
[10:39] <rwdaigle> gazler: I understand the functional differences, but I don't think they're relevant to the developer (or they are relevant, but shouldn't be two separate directives)
[10:40] <chrismccord> rwdaigle but I disagree it is a design issue. Keep in mind we have had extensive conversrtaions around this during the development of Elixir. You can serach the mailing list archives for context that might help
[10:41] <chrismccord> rwdaigle : also, I think you'll find alias and import are distinct things and you'll use both depending on the scenario. Most Elixir libs I see use them differently
[10:41] <chrismccord> rwdaigle : since Elixir has no implicit namespaces, alias comes in to shorter our module references
[10:41] <rwdaigle> chrismccord: I looked at the mailing list for some background but couldn't find any - probably bad search term
[10:43] <elbow-jason> rwdaigle: for instance if I have a length/1 functin in my module Deeply.Nested.ModuleThat.ShouldNotBe.So.Nested and I import that module, I have no way of calling length/1 as a naked function without causing ambiguity and specifying Kernel.length/1 or specifying my modules length/1. In that case, alias is extremely helpful.
[10:44] <elbow-jason> and in fact the naked length/1 will not compile iirc
[10:44] <elbow-jason> the compiler forces disambiguation.
[10:47] <rwdaigle> elbow-jason: right, but you don't need two separate directives to make that distinction: https://gist.github.com/rwdaigle/edeca6b8fbb2d34cec75
[10:49] <elbow-jason> rwdaigle: point taken
[10:50] <rwdaigle> chrismccord: also, when don't I want something I alias or import to be compiled and available (the stated purpose of "require")
[10:50] <josevalim> rwdaigle:  there is one very important difference between alias and import
[10:50] <josevalim> alias does not require the module to exist
[10:51] <josevalim> so if Foo depend on Bar and vice-versa, they can alias each other
[10:51] <josevalim> they can't, however, import each other
[10:52] <josevalim> that's why I said alias/use they are kind of non-negotiable
[10:52] <josevalim> import and require could be fused though as long as we have a decent api
[10:52] <chrismccord> josevalim : how would I `import Logger` without changing my context?
[10:52] <josevalim> chrismccord: that's the api bit
[10:53] <josevalim> today you can do
[10:53] <josevalim> import Logger, only: []
[10:53] <josevalim> but that's obviously verbose as a replacement for reuqire
[10:53] <josevalim> at some point we had
[10:53] <josevalim> import :all, Logger
[10:53] <josevalim> import :functions, Logger
[10:53] <josevalim> we could add import :none, Logger
[10:53] <josevalim> but that is confusing as well
[10:54] <chrismccord> `import Logger, but_really_i_only_want_to_call_its_macros_so_dont_import_anything: true`
[10:54] <chrismccord> josevalim : `import` is just too confusing imo to be used in a way that not actually imports functions
[10:55] <josevalim> chrismccord: we need to drop our bias though, otherwise we don't change anything :)
[10:55] <josevalim> in my languages import is about making the thing available in the namespace
[10:55] <josevalim> (which is our require)
[10:55] <chrismccord> josevalim : agree, java et all
[10:55] <chrismccord> but I think that ship maybe has sailed by now
[10:56] <josevalim> here is the thing though. we can either have different commands, or we can have complex commands
[10:56] <chrismccord> import has always been abou timporting functions into your context. Changing it to accommodate require woudl change its semantics
[10:56] <josevalim> chrismccord: but that's exactly what it is being proposed :p
[10:57] <josevalim> to see if we change slightly change its semantics to accomodate more things
[10:57] <josevalim> it is worth noticing import today already requires
[10:57] <chrismccord> right, as does use
[10:57] <josevalim> and of all of those
[10:57] <josevalim> require is by far the one used the least
[10:59] <josevalim> here is python
[10:59] <josevalim> import foo (equivalent to our require)
[10:59] <josevalim> from foo import * (equivalent to our import)
[11:00] <chrismccord> the fact that our `import Foo` imports everything is the sticky point
[11:00] <josevalim> the big question is if this is any better or any worse than having both import / require
[11:01] <chrismccord> if we had `import Foo` which only required`, and `import Foo, :all`, then it would be an easier choice
[11:01] <josevalim> chrismccord: right. it would have to be a very long term migration
\[11:02] <josevalim> chrismccord: first support import Foo, :all. make import Foo invalid later on, then remove require
[11:02] <chrismccord> josevalim : and break 90% of libs when eventually making invalid
[11:03] <josevalim> right there would be warnings and what not
[11:03] <josevalim> we are talking years here
[11:04] <chrismccord> yeah, but I'm not convinced the churn is worth it
[11:10] <chrismccord> rwdaigle https://groups.google.com/forum/#!msg/elixir-lang-core/EsiO881g2MA/k966k4BY2cAJ
[11:13] <chrismccord> josevalim : the only thing I could think that doesn't break apis is `import Logger, only: :require` but uuugggh
[11:13] <josevalim> chrismccord: or import :module, Logger
[11:13] <josevalim> or something of sorts
[11:13] <josevalim> but then it will lead to people doing import Logger
[11:14] <josevalim> when they just want to call Logger.debug
[11:15] <chrismccord> faced with breaking code later on with more complex commands, or keeping separate, by simple commands, maintaining the status quo gets my vote
[11:15] <chrismccord> josevalim : rwdaigle It's tricky though because I was also confuse by the directives as a newcomer
[11:16] <chrismccord> but once you understand the design decisions, and tradeoffs, I think it's a happy result
[11:18] <rwdaigle> chrismccord: josevalim thank you for this discussion! I can't say I still don't wish the number of directives were reduced, but I at least understand some of the nuance between them all more. I'll probably take another pass through my post with more detail so it's preserved for others.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment