Stefan,

I am sorry, but my experience leads me to disagree with your statement that 
Julia is unable to dispatch a function dynamically (@ runtime).  Quote 
included:
"

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):
"

Indeed, running this code, I get a misleading error message.  Whenever the 
code tries to run f(Foo.F()), I get the following message:
"
ERROR: `f` has no method matching f(::F)
"

*Here is the problem:*
The error actually happened when Bar tried to declare f() - which was 
already exported by Foo, then "using"-d by the module user.

So, in order to play nice, Bar would have to extend Foo.f()... even though 
(in an ideal world) Bar should never need to know that Foo "owned" f().

To make matters worse (on v.0.3.6), Bar then takes control of f() - and 
"steals" it from Foo... as a warning - not an error.

To make my case, I submit a workaround for this example:
#Sorry: My version of Julia does not have rand(Bool)...
Base.rand(::Type{Bool}) = randbool()

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

module Bar
    #export f #Nope... cannot do this... Foo defined first: it "owns" f
    immutable B end

    #Sad but true: Foo "owns" f... so we must adhere to this reality:
    import Foo
    Foo.f(::B) = "this is Bar"
end

using Foo, Bar

#No problem... Julia has an algorithm to dispatch functions
#even if the compiler cannot resolved the call statically.
#Of course, a statically resolved dispatch would be faster than a dynamic 
one...
for i in 1:10
println(f(rand(Bool) ? Foo.F() : Bar.B()))
end


...So your statement confuses me a little...



On Wednesday, April 29, 2015 at 4:41:38 PM UTC-4, Stefan Karpinski wrote:
>
> 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`.
>

Reply via email to