> > 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. >
@Scott: Ah, I see. I apologize for this oversight. I can understand how working under this assumption (that the arguments have types specific to modules) would simplify the search for the relevant method. But I don't see how it comes to bear on the problem of 'foo()''s referent. I keep getting accused of insisting that every name have only one > meaning. Not at all. When you extend a function there are no > restrictions. The `connect` methods for GlobalDB and SQLDB could > absolutely belong to the same generic function. From there, it's > *nice* if they implement compatible interfaces, but nobody will force > them to. > @Jeff: I didn't mean to impute to you that position, though I apologize if my wording was unclear. On Monday, April 27, 2015 at 8:05:55 AM UTC-4, Scott Jones wrote: > > > > 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? >>>>>>> >>>>>>>
