Tom, this is a very legitimate concern. A simple solution is to have a
coding standard not to use `using` when writing packages. Google has
created coding standards for both C++ and Python, which are now widely used
beyond the company.

Automatic function merging goes in the opposite direction: with this
feature it becomes impossible to even say which package a function comes
from – it's not even a meaningful question anymore. That is the point of
Jeff's Base.sin versus Transgression.sin example – map(sin, [1.0, "greed",
2pi, "sloth"]). There is no answer to the question of which of the function
Base.sin and Transgreassion.sin the `sin` function refers to – it can only
refer to some new `sin` that exists only in the current module and calls
either Base.sin or Transgressions.sin depending on the runtime values of
its arguments. Perhaps this can be made clearer with an even nastier
example, assuming hypothetical code with function merging:

module Foo
    export f
    immutable F end
    f(::F) = "this is Foo"
end

module Bar
    export f
    immutable B end
    f(::B) = "this is Bar"
end

julia> using Foo, Bar

julia> f(rand(Bool) ? Foo.F() : Bar.B()) # which `f` is this?


Which `f` is intended to be called here? It cannot be statically determined
– it's not well-defined since it depends on the value of rand(Bool). Some
dynamic languages are Ok with this kind of thing, but in Julia, the
*meaning* of code should be decidable statically even if some of the
behavior may be dynamic. Compare with this slightly different version of
the above code (works on 0.4-dev):

module Sup
    export f
    f(::Void) = nothing # declare generic function without a la #8283
<https://github.com/JuliaLang/julia/issues/8283>
end

module Foo
    export f
    import Sup: f
    immutable F end
    f(::F) = "this is Foo"
end

module Bar
    export f
    import Sup: f
    immutable B end
    f(::B) = "this is Bar"
end

julia> using Foo, Bar

julia> f(rand(Bool) ? Foo.F() : Bar.B())


Why is this ok, while the previous code was problematic? Here you can say
which `f` is called: Foo and Bar share `f` so the answer is well-defined –
`f` is always `Sup.f`.

On Wed, Apr 29, 2015 at 3:36 PM, Tom Breloff <[email protected]> wrote:

> Stefan:  I agree that typing Distributions.Normal(0,1) in the REPL when
> you're just doing data exploration is really frustrating.  However, when
> you're building a package or a larger system, it can be really important to
> either explicitly qualify your types/functions or to explicitly "import
> Distributions: Normal".  Frequently I'll be looking at some code within a
> large package with a call to "Normal", but I don't know what function will
> be called here.  If I do some type of explicit importing, then I can do a
> simple find command on my directory to figure out what package it may have
> been defined in.  If I did a "using", then I have a lot more searching to
> figure out the correct function.  Not to mention the silent overwriting of
> functions by "using" different modules can cause unexpected problems.  For
> that reason, I try to keep "using" to an absolute minimum in most of my
> code.
>
> On Wednesday, April 29, 2015 at 2:49:34 PM UTC-4, Stefan Karpinski wrote:
>>
>> This scheme seems overly focused on object-oriented programming styles at
>> the cost of making other programming styles much more inconvenient. In an
>> o-o language that might be fine, but non-o-o styles are quite common in
>> Julia. The `connect` example keeps coming up because it is one of those
>> cases where o-o works well. That is not the norm in numerical package,
>> however. This proposal would, for example, make using Distributions
>> <https://github.com/JuliaStats/Distributions.jl> a nightmare – you'd
>> have to explicitly import almost everything that it exports
>> <https://github.com/JuliaStats/Distributions.jl/blob/dacb401cfcfc7463f69af0009497960038b25dd5/src/Distributions.jl#L18-L238>
>>  to
>> use. That includes type constructors for distributions etc., since those
>> are themselves (basically) generic functions and their arguments are just
>> built-ins. Instead of doing this:
>>
>> using Distributions
>> X = Normal(0.0, 1.0)
>> p = pdf(X, 0.1)
>>
>>
>> you'd have to do this:
>>
>> using Distributions
>> X = Distributions.Normal(0.0, 1.0)
>> p = Distributions.pdf(X, 0.1)
>>
>>
>> Or you'd have to explicitly import every Distributions type and generic
>> stats function that you want to use. Instead of being able to write `using
>> Distributions` and suddenly having all of the stats stuff you might want
>> available easily, you'd have to keep qualifying everything or explicitly
>> importing it. Both suck for interactive usage – and frankly even for
>> non-interactive usage, qualifying or explicitly importing nearly every name
>> you use is a massive and unnecessary hassle.
>>
>> On Wed, Apr 29, 2015 at 2:06 PM, Michael Francis <[email protected]>
>> wrote:
>>
>>> I would expect the user to explicitly import those method, I did not
>>> preclude their existence. And it would be quite reasonable to support the
>>> existing import all syntax hence
>>>
>>> using MyModule
>>> ^ imports only those functions which explicitly reference user types
>>> defined in the module
>>> importall MyModule.Extensions
>>> ^imports the additional functionality on base types
>>>
>>> if I subsequently import another function which conflicts then we throw
>>> an error. This would mean that the vast majority of non conflicting
>>> functions can be trivially exported and used without a namespace qualifier
>>> and extensions to base types would also work, but with the name collision
>>> check in place.
>>>
>>> I don't believe this violates the expression problem ?
>>>
>>>
>>> On Wednesday, April 29, 2015 at 1:55:14 PM UTC-4, Stefan Karpinski wrote:
>>>>
>>>>
>>>> I made the point at the outset that it isn't hard (or expensive) if the 
>>>> *exported
>>>>> *functions from a module *must reference types defined in that module*.
>>>>> Hence the suggestion that module developers should only be able to export
>>>>> functions which reference owned/hard/contained/user types.
>>>>>
>>>>
>>>> Unless I'm misunderstanding, this is a very limiting restriction. It
>>>> would mean, for example, that you can't define and export a generic
>>>> square(::Number) function. That's a silly example, but it's completely
>>>> standard for packages to export new functions that operate on pre-existing
>>>> types that don't dispatch on any type that "belongs" to the exporting
>>>> module.
>>>>
>>>> Another way of looking at this is that such a restriction would prevent
>>>> solving half of the expression problem
>>>> <http://en.wikipedia.org/wiki/Expression_problem>. In object-oriented
>>>> languages, extending existing operations to new types is easily done via
>>>> subtyping, but adding new operations to existing types is awkward or
>>>> impossible. In functional languages, adding new operations to existing
>>>> types is easy, but extending existing operations to new types is awkward or
>>>> impossible. Multiple dispatch lets you do both easily and intuitively – so
>>>> much so that people can easily forget why the expression problem was a
>>>> problem in the first place. Preventing the export of new functions
>>>> operating only on existing types would hobble the language, making it no
>>>> more expressive than traditional object-oriented languages.
>>>>
>>>
>>

Reply via email to