You, Jeff and Stefan seem to be concerned with different kinds of "ambiguity." Suppose I import `foo(T1, T2)` from module `A` and `foo(T2, T1)` from module `B`. I take you to claim that if I call `foo(x, y)` then, as long as there is no ambiguity which method is appropriate, the compiler should just choose which of `A.foo()` and `B.foo()` has the proper signature. I take you to be concerned with potential ambiguity about which method should apply to a given argument.
On the other hand, I take Jeff and Stefan to be concerned with the ambiguity of to which object the *name* '`foo()`' refers (though they should chime in if this claim or any of the following are wayward). Suppose we're the compiler and we come across a use of the name '`foo()`' while running through the code. Our first instinct when we see a name like '`foo()`' is to look up the object to which the name refers. But what object *does* this name refer to? You (Scott) seem to want the compiler to be able to say to itself before looking up the referent, "If the argument types in this use of '`foo()`' match the signature of `A.foo()`, then this instance of '`foo()`' refers to `A.foo()`. If the argument types match the signature of '`B.foo()`', then this instance of '`foo()`' refers to '`B.foo()`'." But then '`foo()`' isn't really a name, for the referent of a name cannot be a disjunction of objects. Indeed, the notion of a "disjunctive name" is an oxymoron. If you use my name, but your reference could be to either me or some other object depending on context, then you haven't really used *my* *name*, which refers to me regardless of context. I suspect that many problems await if you try to adopt this "disjunctive reference" scheme. In particular, you'd need to develop a general way for the compiler to recognize not only that your use of '`foo()`' isn't intended as a name but rather as a disjunctive reference but also that every other name-like object is *actually* a name. I strongly suspect that there is no general way to do this. After all, what sorts of contexts could possibly determine with sufficient generality whether or not a given name-like object is actually a name or instead a disjunctive reference. The most obvious approach seems to be to let the compiler try to determine the referent of '`foo()`' and, if there is no referent, then to see whether or not there exist imported functions with the same name. If such imported functions exist, then perhaps the compiler decides that '`foo()`' is a disjunctive reference and tries to find an unambiguously matching method. But do we really want the compiler to assume, just because you used a "name" that has no referent but matches the name of one or more imported functions, that you really intend to use that "name" as a disjunctive reference? This is not even to mention the performance costs associated with trying to look up a referent-less "name," deciding that the "name" is actually intended as a disjunctive reference, and then trying to find a matching method and determine a "true" reference. Now, if you're not going to try to implement '`foo()`' as a disjunctive reference, then it must be a name. But to which object does the name refer? The obvious choice would be some "merged" function that aggregates all the unambiguously distinct methods of `A.foo()` and `B.foo()`. But where does this object live? Do we make a *new* object, i.e. a function also named `foo()` whose methods are copies of all the unambiguously distinct methods of `A.foo()` and `B.foo()` and which belongs to the scope in which `A.foo()` and `B.foo()` were imported? But then if one imports `A.foo()` and `B.foo()` in a global scope one thereby creates a global object as the referent of '`foo()`'. I suppose this isn't so bad for functions, but it seems perverse that just importing a name from a module should create global object. Okay, so maybe we shouldn't create a new object. The alternative is to have to have our compiler decide upon import to let '`foo()`' refer once and for all either to `A.foo()` or `B.foo()`, and whichever one it does refer to will "absorb" all unambiguously distinct methods from the other. The first problem here is that it is entirely arbitrary which of the two functions should be the "true" referent, and which should hand over its methods. That this decision would be entirely arbitrary suggests (to me, at least) that it is not the best one to make. And regardless of which function is chosen to be the "true" referent, we still end up with the similarly perverse byproduct that just importing a name from module `A` results in a change to an object from module `B`. This seems to defeat the purpose of a module. Again, this is just what I understand Stefan and Jeff to be arguing. I hope that either/both will point out any misinterpretations or errors on my part. On Sunday, April 26, 2015 at 6:24:15 AM UTC-4, Scott Jones wrote: > > Yes, precisely... and I *do* want Julia to protect the user *in that case*. > If a module has functions that are potentially ambiguous, then 1) if the > module writer intends to extend something, they should do it *explicitly*, > exactly as now, and 2) Julia *should* warn when you have "using" package, > not just at run-time, IMO. > I have *only* been talking about the case where you have functions that > the compiler can tell in advance, just by looking locally at your module, > by a very simple rule, that they cannot be ambiguous. > > Scott > > On Sunday, April 26, 2015 at 5:46:15 AM UTC-4, [email protected] wrote: >> >> >> >> On Sunday, April 26, 2015 at 7:23:02 PM UTC+10, Scott Jones wrote: >>> >>> Ah, but that is NOT the situation I've been talking about... If the >>> writer of a module wants to have a function that takes ::Any, and is not >>> using any other types that are local to that package, then, from the rules >>> I'd like to see, they *would* have to explicitly import from Base (or >>> whichever module they intended to extend). >>> >> >> Neither module is extending anything, they are separate. Its the user >> that wants to use them both in the same program, and its the user that >> Julia is protecting. >> >> Cheers >> Lex >> >> >> >>> >>> Scott >>> >>> On Sunday, April 26, 2015 at 12:25:28 AM UTC-4, [email protected] wrote: >>>> >>>> The situation I was describing is that there is: >>>> >>>> module A >>>> type Foo end >>>> f(a::Any) ... >>>> f(a::Foo) ... >>>> >>>> which expects f(a) to dispatch to its ::Any version for all calls where >>>> a is not a Foo, and there is: >>>> >>>> module B >>>> type Bar end >>>> f(a::Bar) ... >>>> >>>> so a user program (assuming the f() functions combined): >>>> >>>> using A >>>> using B >>>> >>>> b = Bar() >>>> f(b) >>>> >>>> now module A is written expecting this to dispatch to A.f(::Any) and >>>> module B is written expecting this to dispatch to B.f(::Bar) so there is >>>> an >>>> ambiguity which only the user can resolve, nothing tells the compiler >>>> which >>>> the user meant. >>>> >>>> Cheers >>>> Lex >>>> >>>> >>>> On Sunday, April 26, 2015 at 12:00:50 PM UTC+10, Michael Francis wrote: >>>>> >>>>> I don't think Any in the same position is a conflict. This would be >>>>> more of an issue if Julia did not support strong typing, but it does and >>>>> is >>>>> a requirement of dynamic dispatch. Consider >>>>> >>>>> function foo( x::Any ) >>>>> >>>>> Will never be chosen over >>>>> >>>>> Type Foo end >>>>> >>>>> function foo( x::Foo ) >>>>> >>>>> As such I don't get the argument that if I define functions against >>>>> types I define they cause conflicts. >>>>> >>>>> Being in the position of having implemented a good number of modules >>>>> and being bitten in this way both by my own dev and by changes to other >>>>> modules I'm very concerned with the direction being taken. >>>>> >>>>> I do think formalization of interfaces to modules ( and behaviors) >>>>> would go a long way but expecting a diverse group of people to coordinate >>>>> is not going to happen without strong and enforced constructs. >>>>> >>>>> As an example I have implemented a document store database interface, >>>>> this is well represented by an associative collection. It also has a few >>>>> specific methods which would apply to many databases. It would be nice to >>>>> be able to share the definition of these common interfaces. I don't >>>>> advocate adding these to base so how should it be done? >>>>> >>>>>
