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`.
>