My mistake for continuing to use the term "ambiguous".  Julia eats 
ambiguity for breakfast, choosing the most specific method from those 
defined *for the same function*.  

The problem is not ambiguity, it is that merging unrelated functions 
automatically can silently change the behaviour of user code, because the 
merge has changed the definition of "more specific".  When you extend a 
function with a new method that is what you intend to do, fine, but when 
modules have unrelated functions you may not intend to do that by just 
"using" them both.  

That the methods in your module only use types defined in the module does 
not prevent another module from using a more general type in the same 
parameter position ("Any", or a parent of your type), so it is necessary to 
check all methods of all functions of the same name in all modules in scope 
against each other, before deciding that the merge cannot change which 
method is dispatched for that type.  To emphasise, the problem is not your 
module, its "using" your module changing other unrelated user code silently.


On Saturday, May 16, 2015 at 12:30:59 PM UTC+10, Scott Jones wrote:
>
>
>
> On Friday, May 15, 2015 at 9:36:01 PM UTC-4, ele...@gmail.com wrote:
>>
>>
>>
>> On Saturday, May 16, 2015 at 6:58:18 AM UTC+10, Scott Jones wrote:
>>>
>>> About functions that actually take the same types (which means they are 
>>> operating on types they they didn't define themselves), I think the new 
>>> approach in 0.4 (give a warning and don't merge in the "new" meaning) is 
>>> correct.
>>> I would not like to see that change to have the ambiguity only resolved 
>>> / warned about at run-time.
>>>
>>> I've said all along, I'm only talking about cases where there is no 
>>> ambiguity...
>>> Package A defines TypeA, and all of it's exported functions always have 
>>> TypeA in their method signature (even if it's only `::Type{TypeA})`.  Say 
>>> it has a "connect(db::TypeA, ipaddr, port)" method...
>>> Package B defines TypeB, (same as above),  Why should it not be able to 
>>> also define a "connect(db::TypeB,datasetname:UTF8String)"?
>>>
>>
>> Hi Scott,
>>
>> I'm not sure you meant it, but the way you wrote that makes it sound like 
>> a simple fairly local test:
>>
>> if module defines *concrete* type and all methods have a parameter of 
>> that type: merge ok
>>
>> But as I understand it, the test would need to be more complex, for 
>> function 'f' something like:
>>
>> for each function named f in the current scope:
>>     for each method of the function:
>>         for each parameter position in the method:
>>
>
> Not so, you stop at the first type that is defined in the same module.
>

Sure, the loops will shortcut when any "can't merge" situation is 
encountered.  Can also shortcut if the number of parameters doesn't match, 
but varargs upsets that.
 
  

>            if parameter is varargs: can't merge # not sure how else to 
>> handle varargs, can fill infinite parameter positions
>>            get the type of the parameter
>>
>
> If the varargs is limited to a type, or union of types, you simply check 
> that (for unions, *all* the types in the union have to be defined in the 
> same module for it to count)
>

Not sure I understand you here. Varargs methods are about the *number* of 
parameters you can pass to a call 
http://docs.julialang.org/en/release-0.3/manual/functions/#varargs-functions.
 

>  
>
>>            if the type depends on a generic parameter: can't merge # 
>> since the types the generic parameter takes depends on the uses, and we 
>> don't know that until we know if it merges or not
>>
>
> Give an example please of exactly what you mean here... (remember, I'm 
> still a Julia newbie...)
>

Sorry, in some other languages "generic" means a parametric method, and I 
sometimes revert to that usage, oh that computer science would agree on its 
terminology :).

I mean if the method is a parametric method eg f{T}(a::T) the type of 'a' 
depends on the parameter T.  IIUC the method f{T} gets re-compiled for all 
types that it gets as parameters, where there is no existing method, so, 
without knowing all *uses* of f(), Julia can't know which methods exist to 
merge.
 

>  
>
>>            if the type of the parameter is a subtype of a type already 
>> used in this parameter position in a different function: can't merge # 
>> "ambiguous"
>>
>
> Again, not sure what the problem is... that sort of ambiguity check you'd 
> have anyway, right?
>

See above, yes, the problem is not ambiguity as such, rather that in this 
situation the resolution of the ambiguity would change if the functions 
merged, ie the behaviour of code can change. You might want that when you 
imported both modules, but you might not.
 

>  
>
>>            add the type to the list for this parameter position
>> whew, ok to merge
>>
>> Since Julia is a dynamic JITed language not an AOT language I think this 
>> can be run at any time the JIT compiles some new code.  That could do 
>> terrible things to performance.
>>
>
> I really don't think so...
> I also think that when declaring/defining a method in a module, you should 
> be able to specify the following:
> 1) If it is meant to extend a function outside the module (and possibly 
> specify exactly which function, i.e.something like module.name)
>

IIUC thats exactly what you do, module.name.

One aggravating circumstance here is that Julia actually doesn't define 
"functions" separately from "methods", the first method creates a new 
function if one of that name is not in scope.  Specifying that this method 
is module.name is simply specifying an explicit function in another module, 
and if it exists this method is now an extension of it.

 

> 2) If it is meant to be possibly the root of a new concept that might be 
> extended by other packages, and the programmer guarantees that it is meant 
> to be unambiguous (the compiler would check to
>     see if it used at least one local type, but it would only need to do 
> that in this case)
>

As noted above, I believe thats not a sufficient test.
 

> 3) If it is meant to be only callable via module.name (or 
> module.module.name [etc]), outside the module, i.e. it is not allowed to 
> make it extend something at the higher level.
>

"Only callable by qualified name" depends on the situation in the user 
code, which modules they "using" into a specific scope.  When a  module is 
being compiled this is not known, so it can't affect anything in the module.
 

> 4) If it can be brought into a higher level, but is not guaranteed to be 
> unambiguous.. this would get the same checking that all methods get now, 
> and would not be merged into a higher level if the name conflicted
>     (that is the same behavior in v0.4)
>

By "can be brought into a higher level" do you mean it is exported?  If so 
yes, IIUC 0.4 rejects name conflicts in "using" staements.  It could be 
made to allow situations where there can be no change of behaviour in 
merging the methods into one function, but as I have been arguing thats not 
a simple test.  
 

> 5) If it is only meant to be callable *within* the module...
>

> Julia really lacks the capability (AFAIK) to isolate code (and types / 
> enums / etc) in a module, and that causes a lot of headaches.
>
>
Agree, you can't extend a function from another module in a way thats only 
visible in this module.  Thats why extension needs to be explicit "I do 
mean it, and I understand it has global consequences".  But independent 
objects that are not exported should already be isolated properly I believe.
 

>  

Reply via email to