October 18, 2019
On Friday, 18 October 2019 at 15:12:45 UTC, Meta wrote:
> 
>
> Not necessarily. I haven't tried this myself, but I think you could get pretty far with UDAs and some template magic. What I'm thinking:
>
> [snip]

I don't understand this line:
@given!(implements!(R, ForwardRange),
        implements!(inheritImpl!(ForwardRange, MapResult, R), ForwardRange))

I guess I still don't understand why you prefer the implements with interfaces rather than the concepts library's versions of isInputRange/isForwardRange/etc. What does this version add to the static if version (assuming phobos's isInputRange, etc., were replaced with the concepts version?
October 18, 2019
On Friday, 18 October 2019 at 15:56:24 UTC, jmh530 wrote:
> On Friday, 18 October 2019 at 15:12:45 UTC, Meta wrote:
>> 
>>
>> Not necessarily. I haven't tried this myself, but I think you could get pretty far with UDAs and some template magic. What I'm thinking:
>>
>> [snip]
>
> I don't understand this line:
> @given!(implements!(R, ForwardRange),
>         implements!(inheritImpl!(ForwardRange, MapResult, R), ForwardRange))

I wrote that in response to Rikki's assertion:
"This unfortunately still ties the implementation itself to the interface. Which goes against DbI."

Just some spitballing on how DbI could harmoniously co-exist with Atila's concepts library.


> I guess I still don't understand why you prefer the implements with interfaces rather than the concepts library's versions of isInputRange/isForwardRange/etc. What does this version add to the static if version (assuming phobos's isInputRange, etc., were replaced with the concepts version?

I don't necessarily, but it's good to have options. I think it would be very nice if the concepts library were as easy as `import std.range.interfaces; @implements(InputRange, MyCoolRangeType)`, because then the friction for using it is reduced down to almost 0. I also just like the cleanliness of separating the interface from the implementation, which isInputRange also allows, but not in as clean a manner.
October 18, 2019
On Friday, 18 October 2019 at 16:14:26 UTC, Meta wrote:
> [snip]
>
> I wrote that in response to Rikki's assertion:
> "This unfortunately still ties the implementation itself to the interface. Which goes against DbI."
>
> Just some spitballing on how DbI could harmoniously co-exist with Atila's concepts library.

I'm never quite sure whether I grok DbI or not...but all those static if's in phobos's MapResult are introspection to make sure that the range type has the appropriate functions and include the relevant text in MapResult. So it seems to me that isInputRange in phobos is DbI-compatible, particularly when re-reading what Rikki wrote. He seems to be arguing that what you are doing is not DbI because you are using interfaces instead. I think he would be making that complaint about any use of interfaces. However, the concepts library has more than just implements. Its alternative isInputRange could replace phobos's isInputRange or alternately be used used with the models template as needed. In my limited DbI understanding, that would be closer to DbI than what you are trying to do.
October 18, 2019
On Friday, 18 October 2019 at 17:09:55 UTC, jmh530 wrote:
> On Friday, 18 October 2019 at 16:14:26 UTC, Meta wrote:
>> [snip]
>>
>> I wrote that in response to Rikki's assertion:
>> "This unfortunately still ties the implementation itself to the interface. Which goes against DbI."
>>
>> Just some spitballing on how DbI could harmoniously co-exist with Atila's concepts library.
>
> I'm never quite sure whether I grok DbI or not...but all those static if's in phobos's MapResult are introspection to make sure that the range type has the appropriate functions and include the relevant text in MapResult.

Exactly, making it very hard to use @implements from Atila's library, because MapResult may or may not implement the necessary primitives to be, say, a forward range, depending on whether the source range implements them. Yes, of course you can just use `isInputRange` instead, but we should aim to provide options, and I prefer the cleanliness of the former.

> So it seems to me that isInputRange in phobos is DbI-compatible, particularly when re-reading what Rikki wrote. He seems to be arguing that what you are doing is not DbI because you are using interfaces instead.

I may be misunderstanding him, but I believe his main problem with using interfaces is that the struct conforms to the interface, but does not inherit its the behaviour (e.g., MapResult implements .save only if the underlying range does, and forwards to its implementation, which is not possible without extra boilerplate, were we to just allow structs to implement interfaces as a solution).

Also, interfaces can't have non-immutable fields... and there's _not_ really any way around that, but at least in the concept of Phobos ranges, the API consists only of functions, not fields.

My point was that we can do DbI using structs implementing interfaces, and I provided some strawman implementation to show how it might be done.

> However, the concepts library has more than just implements. Its alternative isInputRange could replace phobos's isInputRange or alternately be used used with the models template as needed.

Sure, I'm not disputing that.

> In my limited DbI understanding, that would be closer to DbI than what you are trying to do.

I disagree, my whole point being that DbI is still possible while also allowing structs to implement interfaces (which is basically what @implements is), and maybe preferable in some cases.
October 19, 2019
On 19/10/2019 7:35 AM, Meta wrote:
> On Friday, 18 October 2019 at 17:09:55 UTC, jmh530 wrote:
>> On Friday, 18 October 2019 at 16:14:26 UTC, Meta wrote:
>>> [snip]
>>>
>>> I wrote that in response to Rikki's assertion:
>>> "This unfortunately still ties the implementation itself to the interface. Which goes against DbI."
>>>
>>> Just some spitballing on how DbI could harmoniously co-exist with Atila's concepts library.
>>
>> I'm never quite sure whether I grok DbI or not...but all those static if's in phobos's MapResult are introspection to make sure that the range type has the appropriate functions and include the relevant text in MapResult.
> 
> Exactly, making it very hard to use @implements from Atila's library, because MapResult may or may not implement the necessary primitives to be, say, a forward range, depending on whether the source range implements them. Yes, of course you can just use `isInputRange` instead, but we should aim to provide options, and I prefer the cleanliness of the former.
> 
>> So it seems to me that isInputRange in phobos is DbI-compatible, particularly when re-reading what Rikki wrote. He seems to be arguing that what you are doing is not DbI because you are using interfaces instead.
> 
> I may be misunderstanding him, but I believe his main problem with using interfaces is that the struct conforms to the interface, but does not inherit its the behaviour (e.g., MapResult implements .save only if the underlying range does, and forwards to its implementation, which is not possible without extra boilerplate, were we to just allow structs to implement interfaces as a solution).

So my problem is with the struct itself knowing about the "interface" (whatever definition you use doesn't matter).

Currently we are good with isInputRange. Because the implementation of an input range does not need to know about that "function" in any form including the InputRange class interface.

DbI works at the usage site, not at the declaration site of whatever is getting passed in. Its about limiting and adapting the user code to fit the types being passed in.

So it doesn't matter if you inherit a class interface from a struct, or use a UDA, you are telling the type about the interface.

I.e.

auto map(Args)(Args arg) {
	static struct Map {
		@property {
			Something front() @nogc { ... }
			bool empty() @nogc { ... }
		}

		void popFront() @nogc { ... }
	}

	return Map(arg);
}

void myFunc(IR)(IR input) { ... }



What we want to do, is restrict the template parameter (IR) to an input range. But the input range interface that is being used (e.g. a variant like @nogc) may not have been known to the map function (doesn't matter why) and there is no reason for it to have known about it at all.

There may also be other restricting factors like the ElementType. Now for very simple cases this can be done as part of a template parameter, and more complex ones can go into template constraints like we do now (since its a more advanced use case).

Thing is, if all we want to do is to check if its an input range, we shouldn't need to use template constraints. They are a very advanced language feature involving CTFE and language based traits.

So if we were to look into rewriting Phobos, shouldn't we make something that is common and fairly advanced, into something that is simple to understand? Because that is what I'm arguing for.

Also:

auto:InputRange map(Args)(Args arg) { .. }

A function that returns a value whose type conforms to the InputRange specification. This would significantly improve the documentation (as it cannot be done right now and people have tried to find a solution).
October 19, 2019
On Saturday, 19 October 2019 at 02:57:33 UTC, rikki cattermole wrote:
> auto:InputRange map(Args)(Args arg) { .. }
>
> A function that returns a value whose type conforms to the InputRange specification. This would significantly improve the documentation (as it cannot be done right now and people have tried to find a solution).

auto map(Args...)(Args arg)
    out (result; isInputRange!(typeof(result)))
do {
    ...
}
October 20, 2019
On 20/10/2019 2:45 AM, Paul Backus wrote:
> On Saturday, 19 October 2019 at 02:57:33 UTC, rikki cattermole wrote:
>> auto:InputRange map(Args)(Args arg) { .. }
>>
>> A function that returns a value whose type conforms to the InputRange specification. This would significantly improve the documentation (as it cannot be done right now and people have tried to find a solution).
> 
> auto map(Args...)(Args arg)
>      out (result; isInputRange!(typeof(result)))
> do {
>      ...
> }

I am unsure how to respond to this.

Yes it works right now.

But that amount of syntax and the semantics that go along with it, does not inspire "ease of use" or "easy to understand" in me.
October 20, 2019
On Saturday, 19 October 2019 at 02:57:33 UTC, rikki cattermole wrote:
> So my problem is with the struct itself knowing about the "interface" (whatever definition you use doesn't matter).
>
> Currently we are good with isInputRange. Because the implementation of an input range does not need to know about that "function" in any form including the InputRange class interface.
>
> DbI works at the usage site, not at the declaration site of whatever is getting passed in. Its about limiting and adapting the user code to fit the types being passed in.
>
> So it doesn't matter if you inherit a class interface from a struct, or use a UDA, you are telling the type about the interface.
>
> I.e.
>
> auto map(Args)(Args arg) {
> 	static struct Map {
> 		@property {
> 			Something front() @nogc { ... }
> 			bool empty() @nogc { ... }
> 		}
>
> 		void popFront() @nogc { ... }
> 	}
>
> 	return Map(arg);
> }
>
> void myFunc(IR)(IR input) { ... }
>
>
>
> What we want to do, is restrict the template parameter (IR) to an input range. But the input range interface that is being used (e.g. a variant like @nogc) may not have been known to the map function (doesn't matter why) and there is no reason for it to have known about it at all.

I assume you're describing ML's signatures, which I'm not familiar with, but I see your problem with structs implementing interfaces. However, I don't see how `implements` has this same problem. It's all ad hoc; the "implementing" struct never has to know that the InputRange interface exists at all.

> There may also be other restricting factors like the ElementType. Now for very simple cases this can be done as part of a template parameter, and more complex ones can go into template constraints like we do now (since its a more advanced use case).
>
> Thing is, if all we want to do is to check if its an input range, we shouldn't need to use template constraints. They are a very advanced language feature involving CTFE and language based traits.
>
> So if we were to look into rewriting Phobos, shouldn't we make something that is common and fairly advanced, into something that is simple to understand? Because that is what I'm arguing for.

I agree, but the only comparable language that tried to solve this problem is C++, and they ended up with a real monster of a feature. Rust has `impl <trait>`, but it has the same problem as interfaces in that it's not ad hoc.

> Also:
>
> auto:InputRange map(Args)(Args arg) { .. }
>
> A function that returns a value whose type conforms to the InputRange specification. This would significantly improve the documentation (as it cannot be done right now and people have tried to find a solution).

Yes, I agree that's a major weakness both for template constraints and Atila's concepts library.
October 20, 2019
On 20/10/2019 2:32 PM, Meta wrote:
> 
>> There may also be other restricting factors like the ElementType. Now for very simple cases this can be done as part of a template parameter, and more complex ones can go into template constraints like we do now (since its a more advanced use case).
>>
>> Thing is, if all we want to do is to check if its an input range, we shouldn't need to use template constraints. They are a very advanced language feature involving CTFE and language based traits.
>>
>> So if we were to look into rewriting Phobos, shouldn't we make something that is common and fairly advanced, into something that is simple to understand? Because that is what I'm arguing for.
> 
> I agree, but the only comparable language that tried to solve this problem is C++, and they ended up with a real monster of a feature. Rust has `impl <trait>`, but it has the same problem as interfaces in that it's not ad hoc.

Swift and Rust can be lumped together (trait + protocols) which I believe are loosely based upon ML's signatures. Except that you need to have a separate implementation type to join the interface to the original type.

In D we don't need to have the secondary implementation to join the two together because of our meta-programming capabilities along with CTFE.

Here is the best resource for it we have managed to come up with on Discord: https://www.cs.cmu.edu/~rwh/introsml/modules/sigstruct.htm

While we can take inspiration from other languages I do think that we are mostly on our own with this. Our idioms and usage of them are different enough that we can't just copy other programming languages designs for language features for what we are already doing poorly in library.
October 20, 2019
On Sunday, 20 October 2019 at 00:46:54 UTC, rikki cattermole wrote:
> On 20/10/2019 2:45 AM, Paul Backus wrote:
>> 
>> auto map(Args...)(Args arg)
>>      out (result; isInputRange!(typeof(result)))
>> do {
>>      ...
>> }
>
> I am unsure how to respond to this.
>
> Yes it works right now.
>
> But that amount of syntax and the semantics that go along with it, does not inspire "ease of use" or "easy to understand" in me.

I agree that it's a bit ugly. The main advantage is that it's part of the function's signature, so it's guaranteed to show up in the documentation.