Re: implementing such a merge function. My first instinct would be to create a list of methods from each function, find the intersection, then return a function with methods determined by the methods from each input function, with methods in the intersection going to the value of "conflicts_favor". My question is, would it be okay to create a function f within the scope of merge, add methods to f by iterating through a list, and then return f? In particular, if one assigns the value of a global constant 'connect' to the returned function of merge, is a copy of the returned function created and then bound to 'connect', or would 'connect' be bound to something else that would cause trouble down the line?
Thank you! D On Sunday, April 26, 2015 at 1:41:24 PM UTC-4, Jeff Bezanson wrote: > > 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. > > Scott, I think you're overstating the damage done by a name collision > error. You can't expect to change package versions underneath your > code and have everything keep working. A clear, fairly early error is > one of the *better* outcomes in that case. > > In your design, there are *also* situations where an update to a > package causes an error or warning in client code. I'll grant you that > those situations might be rarer, but they're also subtler. The user > might see > > Warning: modules A and B conflict over method foo( #= some huge signature > =# ) > > What are you supposed to do about that? > > It's worth pointing out that merging functions is currently very > possible; we just don't do it automatically. You can do it manually: > > using GlobalDB > using SQLDB > > connect(c::GlobalDBMgr, args...) = GlobalDB.connect(c, args...) > connect(c::SQLDBMgr, args...) = SQLDB.connect(c, args...) > > This will perform well since such small definitions will usually be > inlined. > > If people want to experiment, I'd encourage somebody to implement a > function merger using reflection. You could write > > const connect = merge(GlobalDB.connect, SQLDB.connect, > conflicts_favor=SQLDB.connect) > > > On Sun, Apr 26, 2015 at 12:10 PM, David Gold <[email protected] > <javascript:>> 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. > > > > 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? > >>>>>> > > >
