On Tuesday, 12 March 2013 at 16:59:41 UTC, H. S. Teoh wrote:
On Tue, Mar 12, 2013 at 12:28:07PM -0400, Nick Sabalausky wrote:
On Tue, 12 Mar 2013 16:02:24 +0100
"TommiT" <tommitiss...@hotmail.com> wrote:
> On Tuesday, 12 March 2013 at 13:44:35 UTC, Nick Sabalausky
> wrote:
> > On Tue, 12 Mar 2013 12:51:03 +0100
> > "TommiT" <tommitiss...@hotmail.com> wrote:
> >
> >> On Tuesday, 12 March 2013 at 02:39:06 UTC, TommiT wrote:
> >> > struct S1 implements A2 {
> >> > void foo() { }
> >> > void bar() { }
> >> > }
> >>
> >> That's not good. Types shouldn't have to explicitly say
> >> that they implement a concept.
> >
> > I *strongly* disagree. A much as I love ranges (for
> > example),
> > their duckiness is the one thing I consider to be a huge
> > mistake.
>
> The problem with having to explicitl specify that a type
> implements a certain concept, is the resulting strong
> coupling between the concept definition and the type. This
> prevents "happy accidents" like the following from happening:
>
> Alice and Bob write libraries without knowing anything about
> each other or each other's code. Alice implements the
> following in her library:
>
> concept IntegerLike {
> ...
> }
>
> void foo(IntegerLike N)(N n) { }
>
> Bob implements the following in his library:
>
> struct SafeInt {
> ...
> }
>
> Later Bob realizes that Alice has written this cool function
> foo which accepts his type SafeInt as an argument because
> SafeInt just so happens to fulfill the requirements of the
> IntegerLike concept defined in Alice's library.
>
> Although, the majority of concepts should come from the
> standard library.
"Happy accidents" is nothing more than another way of saying
"Shit
fucked up, but by pure dumb luck there was no damage". It's an
absolutely *terrible* thing to encourage and design for. You
may as
well just go dynamic all the way, a la ActionScript 2 or
Python - it's
all the same "let random things happen by accident and blindly
hope it
just happens to turn out correct" philosophy.
Design-by-accident is an anti-pattern.
To me more specific, the problem with duck typing is that it
falsely
assumes that name+signature uniquely defines semantics (which
is
clearly not a valid assumption). Avoiding accidental screwups
under
duck typing *is* feasible if there's only a few well-known
duck types
that are ever in play (ex: our entire list of available duck
types is
a handful of phobos-defined ranges and basically nothing
else). But it
does not scale: The likelihood of accidental fuckups is
multiplied
with each additional duck type in existence, with non-stdlib
duck
types carrying a greater "accidental fuck up" weight.
I see it from another perspective: I've had to deal with
proprietary
libraries that defined types that couldn't be used with a
particular
container type, just because the container type expects the
item type to
implement a particular interface, but it doesn't. But actually,
it
*does* have the requisite members to implement that interface;
it just
didn't *say* it implemented that interface. So there's the need
for Yet
Another Useless Java-style Wrapper Class just to work around
this
nonsense. Duck-typing solves this problem.
Of course, full-out ducktyping has its own problems, like you
said; but
there needs to be a way of rewriting APIs such that you could
say "type
T doesn't implement interface I right now, but actually, if you
rewrite
T.A to T.X and T.B to T.Y, then T implements interface I just
fine".
Though with D, I suspect this may actually be possible via
alias this:
struct RewireInterface(T, Interface, Tuple!(string,string)...
rewrites)
{
T t;
alias t this;
foreach (rewrite; rewrites) {
alias rewrite[0] = rewrite[1];
}
}
OK, maybe not. But the foreach could be replace with a suitable
recursive template so that the generated aliases are at the
top-level in
RewireInterface. Probably some other hack is needed to work
around the
need for Tuple in the compile-time parameters, which I'm pretty
sure DMD
rejects right now. But assuming all this can be worked around,
you could
do something like this:
struct StraitJacketedProprietaryItem {
int propX() { ... }
int propY() { ... }
}
concept MyInterface {
int myX();
int myY();
}
alias NonStraitJacketedItem = RewireInterface!(
StraitJacketedProprietaryItem, MyInterface,
"myX", "propX",
"myY", "propY"
);
assert(NonStraitJacketedItem implements MyInterface);
OK, lots of pseudo-code going on here, but you get the idea.
T
D current compile time capabilities already allow this kind of
stuff. I don't see concept as a good idea to introduce into D
right now, but definitively something to look at for the future.