On Saturday, May 16, 2015 at 11:30:13 PM UTC+10, Scott Jones wrote:
>
>
>
> On Saturday, May 16, 2015 at 8:50:12 AM UTC-4, ele...@gmail.com wrote:
>>
>>
>>> If Module A has connect(value::Any), and I add a module B with 
>>> connect(value::TypeB), I don't see a problem with calls to connect with 
>>> something of TypeB "changing" the behavior
>>> of Module A by not letting it grab *all* calls to connect().
>>> Maybe it's still too early in the morning, but I still don't see the 
>>> problem here... you go to the most specific type, not the the generic one...
>>>
>>
>> Example, assume there exists an entity relationship graph module that has 
>> a "connect" function that connects a relationship between two named nodes 
>> and attaches a piece of unknown data:
>>
>> module relationship
>>     connect(data::Any, from::UTF8string, to::UTF8string) = ...
>> end
>>
>> And a database module where you connect to the named database with a 
>> username, note this obeys the rule "must use type defined in same module":
>>
>> module scotts_database_module
>>     type scotts_database end
>>     connect(db::scotts_database, name::UTF8string, user::UTF8string) = ...
>> end
>>
>> In my user code *assuming functions merged* :
>>
>> using relationships, scotts_database_module # function connect now has 
>> two methods
>>
>> #I make lots of relationship connections like
>> connect(1, "nodea", "nodeb") # connection with an integer data
>> connect("some text", "nodeb", "nodec") # connection with a string data
>> # now I want to connect two nodes with one of your excellent databases as 
>> the data 
>> sd = scotts_database()
>> connect(sd, "nodea", "nodeb") # connection with a database data
>>
>> huh? why is my best customer getting a *runtime* error "no database named 
>> 'nodea'"?
>>
>
> OK, I'd say that is something the user will need to solve, and the 
> documentation for the relationship "connect", because it is making a 
> totally generic function, using types not defined by it,
> is the one that should "lose" in this situation.
> The user didn't *have* to use "using" for the relationships module... and 
> people who write modules like relationships, since they are making a very 
> generic function, need to be careful about the
> names they chose, or not export those names.
> connect() is an obvious very bad choice...
>

The reality is that in natural language verbs are overloaded between 
domains, and even in the same domain (eg CS has database "connect", network 
"connect", graph "connect" etc).  Programmers are going to use the verbs 
that are natural to their domain in their software, that is natural and 
good since it aids understanding in that domain, but there are going to be 
clashes when domains mix.

Since there *will* be multiple use of verbs, I don't believe that a 
language can, in good conscience, deliberately lay traps for its users like 
that I illustrated above.

Julia 0.4 quite correctly prevents this problem from happening by refusing 
to import names that clash, and only allowing explicit manual extension of 
verbs, which as Toivo points out is a key strength of Julia.

I agree that it would be good to minimise the number of times qualified 
names are needed in user code, and of course qualification does not work at 
all for operators, but naive automatic merging of functions that have the 
same name is not the way to do it.

Automerging functions which *really* cannot cause problems, seems to me, to 
require complex testing. But if "somebody" made a PR where it could be 
trialled, maybe the impact would be found to be acceptable, or at least a 
good trade off for the number of situations it improves.  But it doesn't 
solve all cases, and I don't really have a feel for how much it would solve 
and if it would be worth it.

Otherwise manually instructing function merges when they make sense may 
also assist, this is already being investigated.

Or an aliasing "using" so the name can be changed on import:

  using scotts_database_module.connect as sdconnect

or some such syntax would avoid qualification.

I don't have any other magic solutions to decreasing qualification 
verbosity I'm afraid, suggestions are welcome.
 

> [...] 
>
>>
>> [...]
>>
>>> Maybe this is just in 0.4, but you can specify the type of a Varargs 
>>> parameter, i.e.  x::TypeB...
>>>
>>
>> Yes, but there can still be any number of parameters of that type in a 
>> call.   f(x::TypeB...) has to be checked against f(a::TypeA, b::TypeB, 
>> c::TypeC) because the relationship between TypeA and TypeB and between 
>> TypeC and TypeB could be such that again merging these functions will 
>> change the dispatch of f(x::TypeB...).  Its not impossible to do it, but it 
>> significantly complicates the checking algorithm.
>>
>
> Are you saying that this is an issue if TypeA <: TypeB, or TypeC <: TypeB?
>

Yes
 
[...]

>
> In order to T to act to make the method unambigous, T must be of a type 
> defined in that module.   So, if you had something like f(T)(a::T), wihtout 
> restricting T as above with the Union, it could not be declared to be 
> unambigous,
> and the compiler could easily detect that and give an error.
>

Yes, thats why I said parametric functions (which I originally incorrectly 
called generic) should block merges.
 
[...]

> In the scotts_database example above, how could you, as the writer of that 
>> module make such a guarantee, you have no way of knowing I would use your 
>> module with the relationship module, in fact you didn't even know it 
>> existed.  Or "connect" could even just be a function in the users own code, 
>> not even in a module.
>>
>
> I can make that guarantee, because my functions that I (using new syntax) 
> make that guarantee for, all use a type defined in my module.
> Since that will always be the most specific type, that is what always will 
> be dispatched to.
>

How would that prevent the problem in the scotts_database example?

[...]

> Don't export it?
>>
>
> I did some test cases, and even not exported, things leak out...  maybe 
> it's a bug...
>

Possibly, grab your examples and make an issue, at the very least it might 
expose holes in the documentation, or our understanding.
 
[...]

>  
>
>> Unfortunately, they leak into the global method table :-(
>>>
>>
>> Which "they" do you mean?
>>
>>
> methods that extend functions, even if they don't export... seems pretty 
> bad to me...
>

Yes, I think it would need each scope to have a method table for all the 
functions extended in it, so the extension remains local, unless the name 
is exported, then it is not needed.  I guess local extensions have not been 
seen as significant enough so far for anyone to do it.  Or nobody has 
pointed out the "issue" (hint hint).  

And as an alternative, extension of external functions could be disallowed 
if the name is not exported, at least then nobody can expect it to remain 
local.

Cheers
Lex
 

>  
>
>>
>>> Scott 
>>>
>>

Reply via email to