C++ guys hate static_if?

Nick Sabalausky SeeWebsiteToContactMe at semitwist.com
Tue Mar 12 13:14:15 PDT 2013


On Tue, 12 Mar 2013 09:57:44 -0700
"H. S. Teoh" <hsteoh at quickfur.ath.cx> wrote:

> On Tue, Mar 12, 2013 at 12:28:07PM -0400, Nick Sabalausky wrote:
> > On Tue, 12 Mar 2013 16:02:24 +0100
> > "TommiT" <tommitissari at 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" <tommitissari at 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.
> 

Duck-typing solves that problem at the cost of potentially introducing
silent bugs. But who says it can only be solved that way? The
problem is trivially solvable *without* the downsides of duck-typing.
Just make the ducks explicit instead of implicit. All you need is
something analogous to "cast": For example, an "(a|A)ssumeImplements"
wrapper. Or maybe even just make it an extra feature of "cast":

    import std.typecons : assumeImplements, AssumeImplements;

    concept IBob { Some syntax to require "void bob()" }
    struct BobType : IBob { void bob() {...} }
    struct AliceType { void bob() {...} }

    // *Explicit* duck-typing
    alias AssumeImplements!(IBob, AliceType) AliceTypeIBob;

    void useBob(IBob ib) {...} // Or whatever syntax

    void main() {
        BobType b;
        useBob(b); // Ok
    
        AliceTypeIBob ab;
        useBob(ab); // Ok

        AliceType a;
        useBob(a); // Fails
    
        // Ok, *explicit* duck-typing
        useBob(assumeImplements!IBob(a));

        // Maybe ok, semi-explicit duck-typing
        useBob(cast(IBob)a);
    }


> 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];
> 		}
> 	}
> 

Interesting.



More information about the Digitalmars-d mailing list