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.