On Sunday, April 26, 2015 at 12:10:41 PM UTC-4, David Gold wrote: > > 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. >
No, that is not at all what I am saying. I am saying that if I have foo(A.type, T1, T2), in module A, and foo(B.type, T2, T1), I should be able to call foo(myA, myT1, myT2), and foo(myB, myT2, myT1) without any problems. Everybody here seems to think I am talking about a quite different case that what I really am... which is the limited case where all of the functions that I export from my module have at least one parameter that is a type local to the module, so that the compiler can know right away that it is unambiguous... (since the rule is that you dispatch to the most specific type). One should be able to write modules in isolation, and as long as one conforms to that simple rule of always having a local type used in all exported functions, that module should be able to be used with `using` anywhere, without warnings, without worrying that Base or one of the packages that the module uses have later on added a conflicting name... Scott > 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? >>>>>> >>>>>>
