March 12, 2013
On 3/11/2013 8:43 PM, deadalnix wrote:
> Yes, that is the idea. It seems really cool.

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.
March 12, 2013
On Tuesday, 12 March 2013 at 04:34:05 UTC, Walter Bright wrote:
> On 3/11/2013 8:43 PM, deadalnix wrote:
>> Yes, that is the idea. It seems really cool.
>
> 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.

Yes, but that allow static checking. The argument is the same as for dynamic vs static typing. In PHP for instance, you can do thing like :

if(is_string($foo)) {
    // handle the case for string
} else {
    // Do something else
}

And this is very similar to our template constraint system and static if, except we are meta here (ie, meta dynamic typing vs meta static typing).
March 12, 2013
On 3/11/2013 10:49 PM, deadalnix wrote:
> And this is very similar to our template constraint system and static if, except
> we are meta here (ie, meta dynamic typing vs meta static typing).

The issue I have with it is it is only able to express concepts as types. Values are important, too, as they are also parameters to templates.
March 12, 2013
On Tuesday, 12 March 2013 at 06:05:54 UTC, Walter Bright wrote:
> On 3/11/2013 10:49 PM, deadalnix wrote:
>> And this is very similar to our template constraint system and static if, except
>> we are meta here (ie, meta dynamic typing vs meta static typing).
>
> The issue I have with it is it is only able to express concepts as types. Values are important, too, as they are also parameters to templates.

Both definitively have advantages and drawbacks. Does the C++ team have any plan on making concept work with values ?

Concepts are for instance THE perfect match for all kind of range we have.
March 12, 2013
On 3/11/2013 11:17 PM, deadalnix wrote:
> Both definitively have advantages and drawbacks. Does the C++ team have any plan
> on making concept work with values ?

I don't know.

> Concepts are for instance THE perfect match for all kind of range we have.

Not for infinite ranges, which depend on empty returning a compile time value.

March 12, 2013
On 3/12/13, deadalnix <deadalnix@gmail.com> wrote:
> Concepts are for instance THE perfect match for all kind of range we have.

I don't know about that. Consider that with template parameters and static if you can pick and choose which range type you will implement:

struct Range(Store)
{
    static if (hasLength!Store)
    {
        // implement length, make the range random-access
    }
    else
    {
        // make the range an input range
    }
}

How would you implement this with concepts if you have to put the list of implements at the struct header?
March 12, 2013
On Tuesday, 12 March 2013 at 06:34:50 UTC, Walter Bright wrote:
> Not for infinite ranges, which depend on empty returning a compile time value.

It was already discussed int he past that this was a bad idea. I think that is another symptom of that poor design choice than an actual issue with concepts.
March 12, 2013
On Tuesday, 12 March 2013 at 07:10:36 UTC, Andrej Mitrovic wrote:
> On 3/12/13, deadalnix <deadalnix@gmail.com> wrote:
>> Concepts are for instance THE perfect match for all kind of range
>> we have.
>
> I don't know about that. Consider that with template parameters and
> static if you can pick and choose which range type you will implement:
>
> struct Range(Store)
> {
>     static if (hasLength!Store)
>     {
>         // implement length, make the range random-access
>     }
>     else
>     {
>         // make the range an input range
>     }
> }
>
> How would you implement this with concepts if you have to put the list
> of implements at the struct header?

I was thinking about constraint. I don't think static if should go away in any way.
March 12, 2013
By now, I'm suspecting that I'm missing the point entirely, but here it is.

The idea is this:
Given
    interface A {...}
    interface B : A {...}
    struct S {supposed to implement B}
use alias this to enable implicit conversions:
    S to Concept!(B, S)
    Concept!(B, S) to Concept!(A, S)
The number of alias this hops then decides which overload is taken.

No rights reserved.


import std.traits: BaseTypeTuple;

mixin template implements(Iface) {
    @property inout(Concept!(Iface, typeof(this))) _concept()() inout {
        return typeof(return)(this);
    }
    alias _concept this;

    // ensure that the interface is implemented
    alias typeof(this) Self;
    private class _Tester : Iface {
        Self s;
        mixin ForwardInterface!(s, Iface);
    }
}

struct Concept(Iface, Impl) {
    Impl impl;
    mixin ForwardInterface!(impl, Iface);

    alias BaseTypeTuple!Iface B;
    static if(B.length == 1) {
        @property inout(Concept!(B, typeof(impl))) _concept()() inout {
            return typeof(return)(impl);
        }
        alias _concept this;
    } else {
        // because multiple alias this aren't here yet
        static assert(B.length == 0);
    }
}

import std.traits: ParameterTypeTuple, ReturnType;
import std.string: format;

mixin template ForwardInterface(alias target, Iface) {
    mixin ForwardMembers!(target, Iface, __traits(allMembers, Iface));
}
mixin template ForwardMembers(alias target, Iface, memberNames ...) {
    static if(memberNames.length > 0) {
        mixin ForwardOverloads!(target, memberNames[0],
            __traits(getOverloads, Iface, memberNames[0]));
        mixin ForwardMembers!(target, Iface, memberNames[1 .. $]);
    }
}
mixin template ForwardOverloads(
    alias target,
    string memberName,
    overloads ...
) {
    static if(overloads.length > 0) {
        alias overloads[0] o;
        mixin(format(
            q{
                ReturnType!o %1$s(ParameterTypeTuple!o args) %2$-(%s %) {
                    target.%1$s(args);
                }
            },
            memberName,
            [
                (is(typeof(o) == immutable) ? "immutable" : ""),
                (is(typeof(o) == const) ? "const" : ""),
                (is(typeof(o) == shared) ? "shared" : "")
            ]
        ));
        mixin ForwardOverloads!(target, memberName, overloads[1 .. $]);
    }
}

// example from http://forum.dlang.org/post/qqnwpprluchyaphvammf@forum.dlang.org

interface A1 {void foo();}
interface A2 : A1 {void bar();}
interface B1 {void fun();}
interface B2 : B1 {void gun();}

struct S1 {
    mixin implements!A2;
    void foo() {}
    void bar() {}
}

struct S2 {
    mixin implements!B2;
    void fun() {}
    void gun() {}
}

int dostuff(X, Y)(Concept!(A1, X) a, Concept!(B1, Y) b) {return 1;}
int dostuff(X, Y)(Concept!(A2, X) a, Concept!(B1, Y) b) {return 2;}

import std.stdio;
void main() {
    S1 s1;
    S2 s2;
    writeln(dostuff(s1, s2)); // calls the more specialized Overload-2
}

// But, if we add the following overload, then the above
// function call dostuff(s1, s2) doesn't know whether to
// call Overload-2 or, the equally specialized, Overload-3:

int dostuff(X, Y)(Concept!(A1, X) a, Concept!(B2, Y) b) {return 3;}

// And, if we add yet another, more specialized, overload,
// then the previous ambiguity goes away:

int dostuff(X, Y)(Concept!(A2, X) a, Concept!(B2, Y) b) {return 4;}
March 12, 2013
On Tuesday, 12 March 2013 at 07:13:11 UTC, deadalnix wrote:
> On Tuesday, 12 March 2013 at 06:34:50 UTC, Walter Bright wrote:
>> Not for infinite ranges, which depend on empty returning a compile time value.
>
> It was already discussed int he past that this was a bad idea. I think that is another symptom of that poor design choice than an actual issue with concepts.

When was it discussed that this was a bad idea?

I'm not saying it isn't (I think it isn't), but I don't remember any such discussion. I'd love to read it.