January 12, 2021
On Tuesday, 12 January 2021 at 16:57:43 UTC, Q. Schroll wrote:
> Yes. But I'm not saying: Do exactly as Java does. I'm saying: Java has this very interesting concept that D might want to learn, too.

Right. D has to chose something that blends well with what is already there. :-)

But I actually think that the semantics for this are there, but the syntax is lacking, but maybe I misunderstand what you want.

> For example, if you call T.method(), but no constraint says that T.method() exists, the compiler rejects the code. On the other hand, in C++ or D, if you call T.method() in a template and only test types that happen to have method(), everything is good between friends (Andrei's quote).

But I don't quite see how this is different from C++ concepts, which basically is better syntax for testing traits of types provided through parameters/methods.


> I'm not arguing you *always* want static checks, obviously DbI hardly works that way. All I'm saying is, the big thing generics would bring to D is **statically ensuring** that the requirements put on type parameters suffice for the implementation to be valid.

Yes, I certainly agree that this is desirable. I just don't understand what semantics are missing. I think D has this? Just not the syntax?

January 12, 2021
On Tuesday, 12 January 2021 at 17:12:11 UTC, Ola Fosheim Grøstad wrote:
> On Tuesday, 12 January 2021 at 16:57:43 UTC, Q. Schroll wrote:
>> For example, if you call T.method(), but no constraint says that T.method() exists, the compiler rejects the code. On the other hand, in C++ or D, if you call T.method() in a template and only test types that happen to have method(), everything is good between friends (Andrei's quote).
>
> But I don't quite see how this is different from C++ concepts, which basically is better syntax for testing traits of types provided through parameters/methods.

C++ concepts, as far as I'm aware, don't cause templates to be checked for conformance prior to instantiation--which is what's being asked for here. For that, you'd need something more like Rust's traits.
January 12, 2021
On Tuesday, 12 January 2021 at 17:16:19 UTC, Paul Backus wrote:
> C++ concepts, as far as I'm aware, don't cause templates to be checked for conformance prior to instantiation--which is what's being asked for here. For that, you'd need something more like Rust's traits.

Not sure what you mean? C++ use SFINAE. If it fails it will look elsewhere.


January 12, 2021
On Tuesday, 12 January 2021 at 17:17:40 UTC, Ola Fosheim Grøstad wrote:
> On Tuesday, 12 January 2021 at 17:16:19 UTC, Paul Backus wrote:
>> C++ concepts, as far as I'm aware, don't cause templates to be checked for conformance prior to instantiation--which is what's being asked for here. For that, you'd need something more like Rust's traits.
>
> Not sure what you mean? C++ use SFINAE. If it fails it will look elsewhere.

As far as I'm aware, C++ does not prevent you from writing code like this:

template<typename T>
concept MyConcept = requires (T t) { T.foo(); };

template<MyConcept T>
void fun(T t) { t.bar(); }

In Java or Rust, the above would be a compile-time error.
January 12, 2021
On Tuesday, 12 January 2021 at 17:32:17 UTC, Paul Backus wrote:
> template<MyConcept T>
> void fun(T t) { t.bar(); }
>
> In Java or Rust, the above would be a compile-time error.

Ok, I get what you say. Concepts is more for library implementors than applications, so it is assumed the library implementor knows what he is doing, and the focus is on putting restrictions on library usage.


But you can do that in C++ to some extent. You can remove members from a declaration, even though they are present in the definition. Thanks to header files and reinterpret casting. There is a lot of stuff you can do in C++ (but not really recommended).


January 12, 2021
On Tuesday, 12 January 2021 at 17:43:39 UTC, Ola Fosheim Grøstad wrote:
> But you can do that in C++ to some extent. You can remove members from a declaration, even though they are present in the definition. Thanks to header files and reinterpret casting. There is a lot of stuff you can do in C++ (but not really recommended).

You can do it in D too, if you use an interface or a type-erasure library like Atila's tardy. But it requires additional runtime overhead compared to the loosely-checked template version. In Rust, on the other hand, you get strict type checking of generic code with zero overhead at runtime.
January 12, 2021
On Tuesday, 12 January 2021 at 17:32:17 UTC, Paul Backus wrote:
> As far as I'm aware, C++ does not prevent you from writing code like this:
>
> template<typename T>
> concept MyConcept = requires (T t) { T.foo(); };
>
> template<MyConcept T>
> void fun(T t) { t.bar(); }
>
> In Java or Rust, the above would be a compile-time error.

Btw, when I really want stuff like this in C++ I create a class with a reference to the original class. So then the interface is a wrapper-class and you gain access to it through an overloaded function with a "agreed upon name".

E.g.

someobject.protected_access.doit()

where "protected_access" is an overloaded function that returns a struct with a reference to someobject and the doit() interface.





January 12, 2021
On Tuesday, 12 January 2021 at 17:53:26 UTC, Ola Fosheim Grøstad wrote:
>
> someobject.protected_access.doit()

In C++ it would obviously be:

protected_access(someobject).doit()

:)


January 12, 2021
On Tuesday, 12 January 2021 at 17:52:16 UTC, Paul Backus wrote:
> You can do it in D too, if you use an interface or a type-erasure library like Atila's tardy. But it requires additional runtime overhead compared to the loosely-checked template version. In Rust, on the other hand, you get strict type checking of generic code with zero overhead at runtime.

I'll have a closer look at Tardy. I previously misunderstood it to be a different language and not a DUB package. Thank you.
January 12, 2021
So, this is one, perhaps clumsy way of doing it:


import std.stdio;

struct StackConcept(T,E) {
    alias self_type = T;
    alias element_type = E;
	T* self;
    this(T* obj){ self = obj; }
    pragma(inline,true) void push(E x){ self.push(x); }
    pragma(inline,true) E pop(){ return self.pop(); }
}

template is_StackConcept(T) {
    enum bool is_StackConcept = is(StackConcept!(T.self_type,T.element_type)==T);
}

struct intstack {
    int[] arr = [];
    void push(int x){ arr.length++; arr[$-1] = x; }
    int pop(){ auto tmp=arr[$-1]; arr.length--; return tmp; }

    auto as_StackConcept(){
    	return StackConcept!(intstack,int)(&this);
	}
}

auto pop_second(T)(T* obj){
    auto stack = obj.as_StackConcept();
    static assert (is_StackConcept!(typeof(stack)));
	auto tmp = stack.pop();
    auto tmp2 = stack.pop();
    stack.push(tmp);
    return tmp2;
}

void main()
{
    intstack stack;
    stack.push(1);
    stack.push(2);
    stack.push(3);
    pop_second(&stack);
    writeln(stack.pop());
    writeln(stack.pop());
}