For anyone who isn't following changes to Julia master closely, Jeff closed #4345 <https://github.com/JuliaLang/julia/issues/4345> yesterday, which addresses one major concern of "programming in the large".
I think the other concern about preventing people from intentionally or accidentally monkey-patching is very legitimate as well, but it's way less clear what to do about it. I've contemplated the idea of not allowing a module to add methods to a generic function unless it "owns" the function or one of the argument types, but that feels like such a fussy rule, I don't think it's the right solution. But I haven't come up with anything better either. On Wed, Apr 22, 2015 at 6:19 PM, Jeff Bezanson <[email protected]> wrote: > I think it's reasonable to adopt a convention in some code of not using > `using`. > > Another way to look at this is that a library author could affect name > visibility in somebody else's code by adjusting the signature of a > method. That doesn't seem like a desirable interaction to me. Often > somebody might initially define foo(::Image), and then later realize > it's actually applicable to any array, and change it to > foo(::AbstractArray). Doing that shouldn't cause any major fuss. > > On Wed, Apr 22, 2015 at 5:58 PM, Michael Francis <[email protected]> > wrote: > > You are correct it is restrictive, though I will take some convincing > that > > this is a bad thing, as systems get larger in Julia it is going to be > > increasingly important to manage code reuse and prevent accidental > masking > > of types. Multiple dispatch is a wonderful tool for supporting these > goals. > > Unfortunately allowing people the ability to export get(<string>) et al > to > > the users scope seems like a bad idea. This is already happening from > > modules today. Perhaps the middle ground is to force an explicit import, > so > > using only imports functions which have types defined in the module. The > > person defining the module exports all the functions they want but only > > those that are 'safe' e.g. follow my original rule are implicitly > imported. > > Hence you would have something like the following code. This is not far > > different from the importall today, except that the exports are > > automatically restricted. > > > > using MyMath # Imports only those functions which include types > > defined in MyMath > > import MyMath.* # Imports all other functions defined in MyMath > > import MyMath.afunc # Imports one function > > import MyOther.afunc # Fails collides with MyMath.afunc > > > > > > > > > > > > > > On Wednesday, April 22, 2015 at 4:40:03 PM UTC-4, Jeff Bezanson wrote: > >> > >> That rule seems extremely restrictive to me. It would be very common, > >> for example, to create a library of functions that operate on standard > >> data types like numbers and arrays. I don't see that we can exclude > >> that kind of use. > >> > >> Also, printing a warning is not the key part of #4345. The important > >> part is that you'd have to qualify names in that case, which is the > >> same thing that would happen if `export`ing the names were disallowed. > >> > >> > >> On Wed, Apr 22, 2015 at 8:47 AM, Michael Francis <[email protected]> > >> wrote: > >> > I read through the issues / threads ( and some others ) > >> > > >> > https://github.com/JuliaLang/julia/issues/2327 > >> > https://github.com/JuliaLang/julia/issues/4345 > >> > > >> > I'm not sure that the either the SuperSecretBase or the warning are > the > >> > correct approach. I'd like to propose a counter which is a very simple > >> > rule. > >> > > >> > "You can only export functions from a module where they reference at > >> > least > >> > one type defined in the module." > >> > > >> > There may have to be a slight tweak for Base, though it is not hard to > >> > argue > >> > that the primitive types are defined in Base. > >> > > >> > so > >> > > >> > module Module1 > >> > type Bar end > >> > my( b::Bar ) = 1 > >> > > >> > export my # fine exports to the global space > >> > end > >> > > >> > module Module2 > >> > type Foo end > >> > my() = 1 > >> > > >> > export my # ERROR exporting function which does not reference > >> > local > >> > type > >> > end > >> > > >> > module Module3 > >> > type Wow end > >> > my( w::Wow ) = 1 > >> > my() = 1 > >> > end > >> > export my # Is an ERROR I can not export a function which does > not > >> > reference a local type > >> > end > >> > > >> > So in the example provided my Mike above, multiple dispatch would do > the > >> > right thing. If I also want to define a function for value in my > module > >> > it > >> > would work consistently against the types I define. We don't have to > >> > perform > >> > recursive exports and import usage should be reduced. > >> > > >> > If you want to define an empty function you can do so with a default > arg > >> > module Module4 > >> > type Zee end > >> > my( ::Type{Zee} = Zee ) = 1 > >> > export my # Works, but I can select against it using multiple > >> > dispatch > >> > by providing the last arg > >> > end > >> > > >> > > >> > I can't convince myself that exporting Types in general (nor macros) > is > >> > a > >> > good idea. > >> > > >> > A tweak may be to add C# like module alias syntax, which is just > >> > syntactic > >> > sugar over what we have ( except that we would likely want the > >> > definition of > >> > MY to be const in the scope. > >> > > >> > MY = using Foo.Bar.ReallyLongModuleName > >> > t = MY.Type() > >> > my( t ) > >> > > >> > > >> > Thoughts ? > >> > > >> > > >> > I'm sure there is something I have missed, but this simple rule would > >> > seem > >> > to encourage multiple dispatch and support the development of modules. > >> > > >> > On Tuesday, April 21, 2015 at 1:07:40 PM UTC-4, Jeff Bezanson wrote: > >> >> > >> >> We're planning to do something about this: #4345. When `using` two > >> >> modules with conflicting names, we should do something other than > pick > >> >> one depending on order. Most likely we will print a warning, and > >> >> require uses of the name to be qualified. > >> >> > >> >> If the two modules really do want to define different methods for the > >> >> same function, then either one has to import the other, or you have > to > >> >> use your SuperSecretBase approach. > >> >> > >> >> On Tue, Apr 21, 2015 at 9:37 AM, Michael Turok <[email protected]> > >> >> wrote: > >> >> > Note that this can be made to work by tearing a page from Base: we > >> >> > can > >> >> > a > >> >> > module (SuperSecretBase), that defines a stub value() function. We > >> >> > then > >> >> > use > >> >> > importall SuperSecretBase in each of Foo and Bar. But this means > >> >> > that > >> >> > any > >> >> > module we create would need to declare its functions into > >> >> > SuperSecretBase. > >> >> > > >> >> > julia> workspace() ; include("mike.jl") > >> >> > > >> >> > julia> using Foo > >> >> > > >> >> > julia> using Bar > >> >> > > >> >> > julia> value(Bar.BarType()) > >> >> > "Bar::value" > >> >> > > >> >> > julia> value(Foo.FooType()) > >> >> > "Foo::value" > >> >> > > >> >> > julia> > >> >> > > >> >> > Modified code follows: > >> >> > > >> >> > module SuperSecretBase > >> >> > value() = nothing > >> >> > export value > >> >> > end > >> >> > > >> >> > # ------------------------------ > >> >> > > >> >> > module Foo > >> >> > > >> >> > importall SuperSecretBase > >> >> > > >> >> > importall Base > >> >> > type FooType end > >> >> > > >> >> > value(x::FooType) = "Foo::value" > >> >> > get(x::FooType) = "Foo::get" > >> >> > > >> >> > export value > >> >> > > >> >> > end > >> >> > > >> >> > # ------------------------------ > >> >> > > >> >> > module Bar > >> >> > > >> >> > importall SuperSecretBase > >> >> > > >> >> > importall Base > >> >> > > >> >> > type BarType end > >> >> > > >> >> > value(x::BarType) = "Bar::value" > >> >> > get(x::BarType) = "Bar::get" > >> >> > > >> >> > export value > >> >> > > >> >> > end > >> >> > > >> >> > > >> >> > > >> >> > > >> >> > On Tuesday, April 21, 2015 at 9:26:01 AM UTC-4, Michael Turok > wrote: > >> >> >> > >> >> >> Hi, > >> >> >> > >> >> >> What is the idiomatic way to create a function value() in > different > >> >> >> modules, dispatched on different arguments, without getting the > >> >> >> warning/error about conflicting with an existing identifier? > >> >> >> > >> >> >> It seems like there is an order dependency with the example below. > >> >> >> Seems > >> >> >> like the 2nd module defines value(), unless you had already used > >> >> >> value() > >> >> >> prior to importing the 2nd module. > >> >> >> > >> >> >> Note that if I do the same with get() a function defined in Base, > I > >> >> >> don't > >> >> >> get an error. > >> >> >> > >> >> >> Code and output from julia REPL below. > >> >> >> > >> >> >> Any help appreciated, > >> >> >> Michael > >> >> >> > >> >> >> # this is mike.jl > >> >> >> > >> >> >> # ------------------------------ > >> >> >> module Foo > >> >> >> # ------------------------------ > >> >> >> importall Base > >> >> >> type FooType end > >> >> >> > >> >> >> value(x::FooType) = "Foo::value" > >> >> >> get(x::FooType) = "Foo::get" > >> >> >> > >> >> >> export value > >> >> >> > >> >> >> end > >> >> >> > >> >> >> # ------------------------------ > >> >> >> module Bar > >> >> >> # ------------------------------ > >> >> >> importall Base > >> >> >> > >> >> >> type BarType end > >> >> >> > >> >> >> value(x::BarType) = "Bar::value" > >> >> >> get(x::BarType) = "Bar::get" > >> >> >> > >> >> >> export value > >> >> >> > >> >> >> end > >> >> >> > >> >> >> Using this in the REPL: > >> >> >> julia> workspace() ; include("mike.jl") > >> >> >> > >> >> >> julia> using Foo > >> >> >> > >> >> >> julia> value(Foo.FooType()) > >> >> >> "Foo::value" > >> >> >> > >> >> >> julia> using Bar > >> >> >> Warning: using Bar.value in module Main conflicts with an existing > >> >> >> identifier. > >> >> >> > >> >> >> julia> value(Bar.BarType()) > >> >> >> ERROR: `value` has no method matching value(::BarType) > >> >> >> > >> >> >> # ----------------------------------------------------- > >> >> >> > >> >> >> julia> workspace() ; include("mike.jl") > >> >> >> > >> >> >> julia> using Foo > >> >> >> > >> >> >> julia> using Bar > >> >> >> > >> >> >> julia> value(Foo.FooType()) > >> >> >> ERROR: `value` has no method matching value(::FooType) > >> >> >> > >> >> >> julia> value(Bar.BarType()) > >> >> >> "Bar::value" > >> >> >> > >> >> >> # ----------------------------------------------------- > >> >> >> > >> >> >> julia> workspace() ; include("mike.jl") > >> >> >> > >> >> >> julia> using Bar > >> >> >> > >> >> >> julia> using Foo > >> >> >> > >> >> >> julia> value(Foo.FooType()) > >> >> >> "Foo::value" > >> >> >> > >> >> >> julia> value(Bar.BarType()) > >> >> >> ERROR: `value` has no method matching value(::BarType) > >> >> >> > >> >> >> julia> >
