Nice summary, it shows that, in the case where the module developer knows about an existing module and intends to extend its functions, Julia "just works".
But it misses the actual problem case, where two modules are developed in isolation and each exports an original sin (sorry couldn't resist). In this case the user of both modules has to distinguish which sin they are committing. Although in some situations it might be possible for Julia to determine that there is no overlap between the methods simply, it is my understanding that in general it could be an expensive whole program computation. So at the moment Julia just objects to overlapping names where they are both original sin. Cheers Lex On Wednesday, April 29, 2015 at 1:50:54 PM UTC+10, MA Laforge wrote: > > I can see that this issue is convoluted. There appears to be competing > requirements, and getting things to start humming is non trivial. > > Instead of dealing with "what if-s"... I want to start with more concrete > "what does"... > > *Transgressions.sin* > First, I don't fully understand Jeff's talk about "Transgressions.sin". I > disagree that "you can't get both behaviors" with map(sin, [1.0, "sloth", > 2pi, "gluttony"]). > > I tried the following code in Julia, and everything works fine: > module Transgressions > Base.sin(x::String) = "Sin in progress: $x" > end > > using Transgressions #Doesn't really do anything in this example... > map(sin, [1.0, "sloth", 2pi, "gluttony"]) > > This tells me that when one uses map on an Array{Any}, Julia dynamically > checks the object type, and applies multi-dispatch to execute the expected > code. > > I admit that one could argue this is not how "object oriented design" > usually deals with this... but that's duck typing for you! > > Ok... so what is the *real* problem (as I see it)? Well, the problem is > that Julia essentially decides that Base "owns" sin... simply because it > was defined first. > > The workaround here was to "extend" Base.sin from module > "Transgressions". This works reasonably well when one *knows* that Base > defines the sin "family of methods"... but not very good when one wants to > appropriate a new verb (enable, trigger, paint, draw, ...). > > Why should any one module "own" such a verb (family of methods)? This > makes little sense to me. > > *As for Michael Turok's idea of the "SuperSecretBase"* > As some people have pointed out, SuperSecretBase is a relatively elegant > way to define a common interface for multiple implementations (Ex: > A/BConnectionManager). However, this is not really appropriate in the case > when modules want to use the same verb for two completely different domains > (ex: draw(x::Canvas, ...) vs draw(x::SixShooter, ...)). > > And, as others have also pointed out: the SuperSecretBase solution is not > even that great for modules that *do* want to implement a common > interface. If company A needs to convince standards committee X to settle > on an interface of accepted verbs... that will surely impede on product > deployment. And even then... Why should standards committee X "own" that > verb in the first place??? Why not standards committee Y? > > *Regarding the comment about not using "using"* > Well, that just seems silly to me... by not using "using"... you > completely under-utilize the multi-dispatch engine & its ability to author > crisp, succinct code. > > ==>And I would like to point out: The reason that multi-dispatch works so > well at the moment is because (almost) everyting in Julia is "owned" by > Base... so there are no problems extending methods >>>>In base Julia<<<< > > *Some improvements on Transgressions.sin* > FYI: I don't really like my previous example of Transgressions.sin. The > reason: The implementation does not make sufficient use of what I would > call "hard types" (user-defined types). Instead, it uses "soft types" > (int/char/float/string). > > Hard types are very explicit, and they take advantage of multiple > dispatch. On the other hand, a method that takes *only* soft types is more > likely to collide with others & fail to be resolved by multiple dispatch. > > I feel the following example is a *much* better implementation to resolve > the sin paradox: > module Religion > #Name "Transgressions" has a high-likelyhood of name collisions - > don't "export": > type Transgressions; name::String; end > > #Personally, I find this "Transgressions" example shows that base > should *not* "own" sin. > #Multi-dispatch *should* be able to deal with resolving ambiguities... > #In any case, this is my workaround for the moment: > Base.sin(x::Transgressions) = "Sin in progress: $x" > > #Let's hope no other module wants to "own" method "absolve"... > absolve(x::Transgressions) = "Sin absolved: $x" > > export absolve #Logically should have sin here too... but does not > work with Julia model. > end > > using Religion > Xgress = Religion.Transgressions #Shorthand... "export"-ing > Transgressions susceptible to collisions. > > map(sin, [1.0, Xgress("sloth"), 2pi, Xgress("gluttony")]) > > Initially, creating a type "Transgressions" seems to be overdoing things a > bit. However, I have not noticed a performance hit. I also find it has > very little impact on readability. In fact I find it *helps* with > readability in most cases. > > Best of all: Despite requiring a little more infrastructure in the module > definition itself, there is negligible overhead in the code that *uses* > module Religion. >