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 -- It is widely believed that reinventing the wheel is a waste of time; but I disagree: without wheel reinventers, we would be still be stuck with wooden horse-cart wheels.