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]> 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?
>>>>>>
>

Reply via email to