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.

Reply via email to