March 12, 2013
On Tuesday, 12 March 2013 at 14:24:11 UTC, TommiT wrote:
> The difference is in function overload resolution. Polymorphic concept based template would know about the hierarchical nature of the concepts, say ForwardRange is a sub-concept of InputRange, and thus the function overload resolution would be able to choose the template which has the most derived/specialized concept parameter that still matches with the given template argument.

Example:

void foo(R)(R) if (isInputRange!R) { }
void foo(R)(R) if (isForwardRange!R) { }

int[] arr;
foo(arr); // ambiguity error

// Polymorphic concept to the rescue:

concept InputRange {
    // definition of the concept
    ...
}

// ForwardRange is an extension of InputRange
concept ForwardRange : InputRange {
    ...
}

void bar(InputRange R)(R) { }
void bar(ForwardRange R)(R) { }

int[] arr;
bar(arr); // calls the second one
March 12, 2013
On Tuesday, 12 March 2013 at 13:44:35 UTC, Nick Sabalausky wrote:
> On Tue, 12 Mar 2013 12:51:03 +0100
> "TommiT" <tommitissari@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.
March 12, 2013
On Tuesday, 12 March 2013 at 14:16:15 UTC, H. S. Teoh wrote:
> On Tue, Mar 12, 2013 at 12:39:48PM +0100, TommiT wrote:
>> On Tuesday, 12 March 2013 at 04:34:05 UTC, Walter Bright wrote:
>> >It's interfaces without the vtable[].
>> >
>> >It's still solely based on type signatures. D constraints make
>> >pretty much anything that can be computed at compile time a
>> >testable gate.
>> 
>> Yeah, you're right. That kind of interface syntax doesn't really
>> lend itself to specifying concepts. So, here's another attempt at
>> a concept syntax (and functionality):
>> 
>> concept AscendingInfiniteInputRange {
>>      // 'this' is an instance of a type which implements the
>>      // AscendingInfiniteInputRange concept given the
>>      // if-condition below is true:
>>      if( is(typeof(this.empty) : bool)
>>      &&  is(typeof(this.front))
>>      && !is(typeof(this.front) == void)
>>      &&  is(typeof(this.popFront() == void)
>>      // testing a compile time evaluable value:
>>      &&  this.empty == false
>>      // static members can also be tested:
>>      &&  typeof(this).infinite == true
>>      &&  typeof(this).sortedAscending == true )
>> }
>
> How is this any different from the current isInputRange!R,
> isForwardRange!R, etc.?
>
>
> T

With the thing defined that way not that much. But consider :

concept InputRange(T) {
    bool empty;
    T front;
    void popFront();
}

Then you can :
 - Validate range in a static manner.
 - Validate template before they are instantiated.
 - Express intent.
 - Make overload rules easier to understand and more predictable when used.

Definitively the idea it nice. I don't see it a replacement of static if as it doesn't handle values.
March 12, 2013
On Tuesday, 12 March 2013 at 15:10:25 UTC, deadalnix wrote:
> On Tuesday, 12 March 2013 at 14:16:15 UTC, H. S. Teoh wrote:
>> On Tue, Mar 12, 2013 at 12:39:48PM +0100, TommiT wrote:
>>> On Tuesday, 12 March 2013 at 04:34:05 UTC, Walter Bright wrote:
>>> >It's interfaces without the vtable[].
>>> >
>>> >It's still solely based on type signatures. D constraints make
>>> >pretty much anything that can be computed at compile time a
>>> >testable gate.
>>> 
>>> Yeah, you're right. That kind of interface syntax doesn't really
>>> lend itself to specifying concepts. So, here's another attempt at
>>> a concept syntax (and functionality):
>>> 
>>> concept AscendingInfiniteInputRange {
>>>     // 'this' is an instance of a type which implements the
>>>     // AscendingInfiniteInputRange concept given the
>>>     // if-condition below is true:
>>>     if( is(typeof(this.empty) : bool)
>>>     &&  is(typeof(this.front))
>>>     && !is(typeof(this.front) == void)
>>>     &&  is(typeof(this.popFront() == void)
>>>     // testing a compile time evaluable value:
>>>     &&  this.empty == false
>>>     // static members can also be tested:
>>>     &&  typeof(this).infinite == true
>>>     &&  typeof(this).sortedAscending == true )
>>> }
>>
>> How is this any different from the current isInputRange!R,
>> isForwardRange!R, etc.?
>>
>>
>> T
>
> With the thing defined that way not that much. But consider :
>
> concept InputRange(T) {
>     bool empty;
>     T front;
>     void popFront();
> }

What if I write a type like the following:

struct MyType {
    int _value;

    @property bool empty() const { return true; }
    @property ref const(int) front() const { return _value; }
    void popFront() const { }
}

Does MyType fulfill the requirements of your InputRange(T) concept? I don't think it does since its front returns by ref const(int) and InputRange(T)'s front returns by value.
March 12, 2013
On Tuesday, 12 March 2013 at 15:26:19 UTC, TommiT wrote:
>> With the thing defined that way not that much. But consider :
>>
>> concept InputRange(T) {
>>    bool empty;
>>    T front;
>>    void popFront();
>> }
>
> What if I write a type like the following:
>
> struct MyType {
>     int _value;
>
>     @property bool empty() const { return true; }
>     @property ref const(int) front() const { return _value; }
>     void popFront() const { }
> }
>
> Does MyType fulfill the requirements of your InputRange(T) concept? I don't think it does since its front returns by ref const(int) and InputRange(T)'s front returns by value.

It doesn't because popFront is const and that don't make any sense. But more generally, I wrote that stuff quickly to demonstrate what it could look like and not to provide an accurate definition of InputRange.
March 12, 2013
On Tuesday, 12 March 2013 at 15:37:01 UTC, deadalnix wrote:
> On Tuesday, 12 March 2013 at 15:26:19 UTC, TommiT wrote:
>>> With the thing defined that way not that much. But consider :
>>>
>>> concept InputRange(T) {
>>>   bool empty;
>>>   T front;
>>>   void popFront();
>>> }
>>
>> What if I write a type like the following:
>>
>> struct MyType {
>>    int _value;
>>
>>    @property bool empty() const { return true; }
>>    @property ref const(int) front() const { return _value; }
>>    void popFront() const { }
>> }
>>
>> Does MyType fulfill the requirements of your InputRange(T) concept? I don't think it does since its front returns by ref const(int) and InputRange(T)'s front returns by value.
>
> It doesn't because popFront is const and that don't make any sense.

I didn't mean to make popFront const. My point was that InputRange concept requires that the front property returns a non-void type but it doesn't care whether or not it is returned by ref, const ref, or by value. I don't see how you could easily convey that kind of requirement in an interface-like syntax.
March 12, 2013
On Tue, 12 Mar 2013 16:02:24 +0100
"TommiT" <tommitissari@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@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.

March 12, 2013
On Tue, Mar 12, 2013 at 12:28:07PM -0400, Nick Sabalausky wrote:
> On Tue, 12 Mar 2013 16:02:24 +0100
> "TommiT" <tommitissari@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@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.
March 12, 2013
On Tue, 12 Mar 2013 09:57:44 -0700
"H. S. Teoh" <hsteoh@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@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@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.

March 12, 2013
On 3/9/2013 10:10 AM, deadalnix wrote:
> On Saturday, 9 March 2013 at 17:51:31 UTC, Ali Çehreli wrote:
>> On 03/09/2013 03:15 AM, Artur Skawina wrote:
>>
>> >   - static-if not creating scopes /is/ confusing, but what
>> would be a better
>> >     alternative?
>>
>> I am surprised that << and >> are never mentioned:
>>
>>     static_if >>
>>         void foo();
>>     <<
>>
>> Problem solved. ;)
>>
>> Ali
>
> static if(condition) «
>      void foo();
> »
>
> Let's do it with style ;)

So now I am going to need a unicode keyboard or a massive list of unicode code points.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18