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

module and import aliasing #1255

Closed
kmsquire opened this issue Sep 5, 2012 · 96 comments · Fixed by #37396
Closed

module and import aliasing #1255

kmsquire opened this issue Sep 5, 2012 · 96 comments · Fixed by #37396
Assignees
Labels
design Design of APIs or of the language itself modules speculative Whether the change will be implemented is speculative
Milestone

Comments

@kmsquire
Copy link
Member

kmsquire commented Sep 5, 2012

For the module system, we can import and use a module with dot notation:

import ArgParse
... 
    ArgParse.add_argument(p, "--opt1") 
...

This can be useful to prevent namespace pollution. Because of the verbosity of module names, however, it would be nice to have module aliases:

import ArgParse as ap 
... 
    ap.add_argument(p, "--opt1") 
...
@carlobaldassi
Copy link
Member

I realized minutes ago that you can do the following:

import ArgParse
ap = ArgParse

ap.add_argument(...)

I still think it would be nice to have the "import as" notation as syntactic sugar though.

@ghost ghost assigned JeffBezanson Sep 5, 2012
@kmsquire
Copy link
Member Author

kmsquire commented Sep 8, 2012

While I'm here and thinking about it, it would also be nice to have a way of importing multiple specific functions in the same import line, as mentioned in @JeffreySarnoff 's forum post. A search through the issues didn't reveal such a request yet.

Something like

# from the forum post
import Module.(export1,export2,export3)
# or
import Module.[export1,export2,export3]
# or maybe
import Module.{export1,export2,export3}

It's a different issue than this one, but related. Should I

  1. update this issue, or
  2. create a new issue

(I'm assuming the usefulness of such a feature is not controversial...)

Edit: This works now with import Module: export1, export2, export3

@toivoh
Copy link
Contributor

toivoh commented Feb 12, 2013

I think that it should also support e.g.

import ArgParse.add_argument as addarg

to be able to rename module members upon import.

@aviks
Copy link
Member

aviks commented Feb 13, 2013

Wondering how renamed function names would participate in dispatch?

@JeffBezanson
Copy link
Sponsor Member

It would have to work basically like an alias, equivalent to const foo = Base.bar.

@kmsquire
Copy link
Member Author

It would have to work basically like an alias, equivalent to const foo =
Base.bar.

When we import a function, we either plan to use it or override it. Given
the above, with

import Base.bar as foo

we can definitely use foo as Base.bar, but would defining foo also override
Base.bar? Should it?

@lindahua
Copy link
Contributor

This is an old issue, but I think it is time to revisit this as we are approaching 0.2

In writing codes, I found I always wanted to write import NumericExtensions as ne and ended up reminding my self this hasn't been supported.

I guess this is not too difficult to implement, and in my opinion, this is much nicer than writing

import NumericExtensions
const ne = NumericExtensions

@jiahao
Copy link
Member

jiahao commented Oct 17, 2013

+1

1 similar comment
@diegozea
Copy link
Contributor

+1

@porterjamesj porterjamesj mentioned this issue Nov 20, 2013
21 tasks
@quinnj
Copy link
Member

quinnj commented Jun 20, 2014

Is this still relevant? I personally dont' mind just doing the extra foo = Foo for module aliasing, but it sounds like there was some consensus to support aliasing sugar. Someone who feels strongly enough may have to do a PR themselves.
Note however that x as y was one of the strongest contenders for convert(y,x) syntax mentioned in #1470. Seems like it wouldn't be too difficult to disambiguate the two, but just noting.

@JeffBezanson
Copy link
Sponsor Member

The value of this feature is that you could avoid importing the original name.

@bokertov
Copy link

+1
Would be great to have this feature.

@mindbound
Copy link

+1

@StefanKarpinski
Copy link
Sponsor Member

Rather than introducing a new keyword as, how about using the => operator, as in

import Tlahuixcalpantecuhtli => Venus: xxxxxxxxx => x9, yyyyyyyy => y8, zzz

This would orthogonally allow aliasing of the module and any subset of variables in the same syntax.

@jakebolewski
Copy link
Member

I don't know, that seems really ugly to me.

@StefanKarpinski
Copy link
Sponsor Member

import Tlahuixcalpantecuhtli as Venus: xxxxxxxxx as x9, yyyyyyyy as y8, zzz

Is that any better? I find it far less readable.

@ssfrr
Copy link
Contributor

ssfrr commented Jul 22, 2014

I personally find as to be more self-explanatory than =>, which seems like it could mean any number of things.

full disclosure: python bias.

@BobPortmann
Copy link
Contributor

I like the => syntax better than as.

@jiahao
Copy link
Member

jiahao commented Jul 22, 2014

I like bikesheds that get stuck in Nash equilibria.

@jakebolewski
Copy link
Member

Why would you rebind the inner symbols of a module? Its ugly because you really should not be doing that.

I like Haskell's import syntax. I feel Python's namespaces are more granular than Julia's modules. Julia's module's feel more in spirit to haskell's modules, so maybe we should look there for inspiration.

@prcastro
Copy link
Contributor

I agree with @ssfrr , the as keyword is much more self-explatory, and is alo familiar to Python/Haskell users. To solve @StefanKarpinski problem with readability, python-like approach would also help:

from Tlahuixcalpantecuhtli as Venus import xxxxxxxxx as x9, yyyyyyyy as y8, zzz

I feel like I'm talking to the REPL. But I know that this kind of suggestion is not feasible.

@StefanKarpinski
Copy link
Sponsor Member

from Tlahuixcalpantecuhtli as Venus import xxxxxxxxx as x9, yyyyyyyy as y8, zzz

This is one of my least favorite syntactic choices in Python. To change the meaning slightly you have to rearrange the entire line radically, changing the initial keyword and the ordering of everything. It looks completely different from other imports, obscuring that fact they that do almost the same thing. This is a terrible syntactic design, imo, giving far too much weight to being superficially English-like.

@dcjones
Copy link
Contributor

dcjones commented Jul 22, 2014

👍 for as, 👎 for from

import a => b: c => d, e => f feels a little too perl-esque to me.

@BobPortmann
Copy link
Contributor

I never heard of Nash equilibria and after reading about it I don't think this constitutes one. But the funny thing is, while this bikeshed has 10 responses in one hour, a proposal on #6984 which strikes me as very important (but more difficult) has gone 4 hours with no comment!

As for as vs. =>: either will be a nice improvement over the present situation.

@quinnj
Copy link
Member

quinnj commented Jul 22, 2014

Good point @BobPortmann on #6984.

I think => is great for this, FWIW.

@dcjones
Copy link
Contributor

dcjones commented Jul 22, 2014

But the funny thing is, while this bikeshed has 10 responses in one hour, a proposal on #6984 which strikes me as very important (but more difficult) has gone 4 hours with no comment!

In Parkinson's original analogy, this issue is the bike shed and #6984 is the nuclear reactor.

@kmsquire
Copy link
Member Author

kmsquire commented Jul 3, 2020

I didn’t intend for my message to imply that this isn’t a big deal.

I would still prefer a syntax for this.

@DominiqueMakowski
Copy link

DominiqueMakowski commented Jul 4, 2020

ImportMacros is sufficient. I just agree with comments that it doesn’t look that clean.

I agree. Also, for:

using ImportMacros
@using OtherPackage as ...

It makes it a bit annoying when presenting the language to newcomers or programming beginners to explain why a given script starts with this inconsistency, as it complicates the flow and shifts the focus of the teaching.

neither seems to be used very frequently in practical code to warrant special syntax

The reason why people don't use ImportMacros a lot in actual code a lot could be due to a tradeoff. For instance, I'd be using import ... as ... almost every time, but likely not at the cost of having to import first another package to do it. More than an additional dependency, it also has an impact in terms of "feel" (and some people like their code to look sleek (especially in python or Julia) 🤷‍♂️ , and this using/@using gives a rather "hacky" vibe to the introduction of a script).

So yeah, "ImportMacros is sufficient". For people that really want to use it. But that's the thing, it's not a vital issue, and people can work without it. Nonetheless, I strongly believe it is typically these little things that make for a language's syntax to stick, hence this monster-thread to add its support to base.

I think that import ... as ... is syntactic sugar that would be definitely worth it, it's really easy to understand and to explain, and especially useful in languages such as Julia in which package tend to have rather long names (obviously, this matters only under the assumption that people do not want to do using ...).

And the fact that Python had it implemented first shouldn't really be relevant; Julia should strive at having the best syntax before trying to distance itself from other languages and/or emphasize its own identity.

@goretkin
Copy link
Contributor

goretkin commented Jul 4, 2020

Having syntax for this and conventions around popular modules (I'm thinking of how it's always import numpy as np and import matplotlib.pyplot as plt) would also give a concise alternative to doing using SuchAndSuch and relying on exported names (which is something I generally avoided in "library" code).

@tpapp
Copy link
Contributor

tpapp commented Jul 5, 2020

@DominiqueMakowski:

presenting the language to newcomers or programming beginners to explain why a given script starts with this inconsistency

I don't understand why you think it is an inconsistency, it is simply namespace management, which is a normal part of programming in the large.

Also, renaming modules may not be something that newcomers to Julia would encounter first.

I am not saying that there isn't a use case, but it may be to rare to justify its own keyword. At the moment, searching for using ImportMacros gives <10 unique results on Github.

I'd be using import ... as ... almost every time, but likely not at the cost of having to import first another package to do it

This cuts both ways: if the (very minor) cost of using a ligthweight package like ImportMacros or an extra symbol (import LongFoo; const Foo = LongFoo) is not worth the benefit, then the benefit may not be that large.

Julia should strive at having the best syntax before trying to distance itself from other languages and/or emphasize its own identity.

I am not sure why you think this is a motivation in this discussion.

@StefanKarpinski
Copy link
Sponsor Member

There are some cases where you might want to use as where the const assignment doesn't quite work:

julia> using Distributed

julia> import Future; const future = Future
Future

julia> Future === Distributed.Future # oops, this is what we wanted
false

julia> Future === future # not what we wanted `Future` to mean
true

Yes, you can work around this, but it's awkward. Given that it's often handy, sometimes needed and that import FooBar as FB is the generally agreed upon "least surprising" syntax, that seems like what we should go with. All this needs is for someone to make a PR implementing it.

@elalaouifaris
Copy link
Contributor

New Julia user here.
I come from a mostly R background, using Python as well. I have bad experiences with the package common name space that loading mechanisms like using leads to. The risk of collision is one, but just from a readability perspective, their is a mental burden of constantly thinking about each method's module.

I don't know how complex adding this syntax is? Is it doable for a newbie?

@StefanKarpinski
Copy link
Sponsor Member

Probably not very easy. It will involve hacking the parser code which is written in Scheme and wiring the change through the AST, potentially touching some C code as well.

@StefanKarpinski StefanKarpinski modified the milestones: 1.x, 1.6 Aug 2, 2020
@hotung1027
Copy link

It would be a nice feature for avoid function name conflict in multiple modules.

@baggepinnen
Copy link
Contributor

From a discussion on slack, I add some thoughts

I wonder if this will make people less careful with choosing function names and properly adding methods to the correct functions. The fact that, e.g.,

numpy.sin
sympy.sin

are different functions is a surefire way of having to implement two version of your function if you want it to work for both numerical and symbolic inputs. If such namespacing becomes widespread in julia as well due to the convenience, we might miss out on a lot of code reuse? I.e., I'm afraid this might give us less of "This encourages people to work together" as it was put by Lyndon in https://www.oxinabox.net/2020/02/09/whycompositionaljulia.html

JeffBezanson added a commit that referenced this issue Sep 8, 2020
@ninjaaron
Copy link
Contributor

ninjaaron commented Sep 10, 2020

@baggepinnen I don't think this changes anything in that regard.

In Julia methods implemented in different modules are not merged by importing both methods into the same namespace.

julia> module Foo
           export sin
           sin(s::String) = uppercase(s)
       end
Main.Foo

julia> sin(1)
0.8414709848078965

julia> using .Foo
WARNING: using Foo.sin in module Main conflicts with an existing identifier.

julia> sin("wat")
ERROR: MethodError: no method matching sin(::String)
Closest candidates are:
  sin(::BigFloat) at mpfr.jl:727
  sin(::Missing) at math.jl:1197
  sin(::Complex{Float16}) at math.jl:1145
  ...
Stacktrace:
 [1] top-level scope at REPL[4]:1
 [2] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.1/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288

julia> Foo.sin("wat")
"WAT"

One has to explicitly add a dispatch to the original function for this to work:

julia> Base.sin(s::String) = Foo.sin(s)

julia> sin("wat")
"WAT"

As far as I can tell, there is no possible way for module aliasing to change how functions are dispatched.

And honestly, some functions with the same name should live in separate namespaces. A find_delta function in a numeric library is going to do something unrelated to to find_delta in a file diff library. Once your code is so polymorphic that it will find a way to execute on broken input, you're getting into JavaScript territory, and nobody wants that.

@baggepinnen
Copy link
Contributor

@ninjaaron I might have failed to convey some nuance in my concern above. I am not concerned with this change changing anything other than the strategy people choose to take when they implement libraries.
The person implementing Foo encountering a warning like

julia> using .Foo
WARNING: using Foo.sin in module Main conflicts with an existing identifier.

can choose to either simply import .Foo.sin as sin2, or think about whether or not he really wanted to provide a new dispatch for Base.sin. My point was that if it becomes very easy to simply consider them different functions, maybe this will become too widespread, even in situations when they really should be different methods of the same function. The current situation, where it's slightly more awkward to deal with different functions with the same name has the nice side effect that people talk to each other and do their best to figure out if it really is the same function or not. I'm not arguing against the possibility to have different functions with the same name, which of course can be very useful. I'm not even sure whether my concern is important or not, but figured it was worth it to lift it to ensure it has been considered.

@ninjaaron
Copy link
Contributor

@baggepinnen That makes sense, and there's no harm in bringing it up. I don't think this will make a huge difference for people creating libraries, since module aliasing will only affect library users. I guess it is possible that having easier module aliasing will result in fewer users complaining about naming conflicts, but I would be surprised if that has a tremendous impact on APIs.

When I write a library, I'm usually pretty conscious of whether I want to add a dispatch to a function in Base or I want to keep it separate. I would assume most other library authors are as well.

@hotung1027
Copy link

@ninjaaron I think if the current convention is using specific implementation for dispatch like

numpy.sin
sympy.sin

using import numpy.sin as np_sin as an extra option for user/developer should not affect the current code base.
The only concern will only be affecting the expose function/interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design Design of APIs or of the language itself modules speculative Whether the change will be implemented is speculative
Projects
None yet
Development

Successfully merging a pull request may close this issue.