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