www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - ++Phobos

reply berni44 <dlang d-ecke.de> writes:
In the feedback thread H. S. Teoh is talking about that we need a 
new/better version of Phobos. I remember having read the same in 
a comment on some PR recently. How would this better version look 
like? Is there allready any picture of what we do want? One says, 
if you don't know the goal, all paths are the wrong ones...

A few things that I remember from various sources:

* Some improvement in the realm of ranges.
* Replacement of some old modules. (std.xml, std.outbuffer, 
std.socket, what else?)
* Adding some missing modules (std.eventloop, std.database, what 
else?)
* Unifying some modules (everything covering selfinspection, like 
std.traits and so on, whar else?)

Anything more?
Oct 16
next sibling parent Les De Ridder <les lesderid.net> writes:
On Wednesday, 16 October 2019 at 17:20:28 UTC, berni44 wrote:
 In the feedback thread H. S. Teoh is talking about that we need 
 a new/better version of Phobos. I remember having read the same 
 in a comment on some PR recently. How would this better version 
 look like? Is there allready any picture of what we do want? 
 One says, if you don't know the goal, all paths are the wrong 
 ones...

 A few things that I remember from various sources:

 * Some improvement in the realm of ranges.
 * Replacement of some old modules. (std.xml, std.outbuffer, 
 std.socket, what else?)
 * Adding some missing modules (std.eventloop, std.database, 
 what else?)
 * Unifying some modules (everything covering selfinspection, 
 like std.traits and so on, whar else?)

 Anything more?
Getting rid of autodecoding is another big one.
Oct 16
prev sibling next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 17/10/2019 6:20 AM, berni44 wrote:
 In the feedback thread H. S. Teoh is talking about that we need a 
 new/better version of Phobos. I remember having read the same in a 
 comment on some PR recently. How would this better version look like? Is 
 there allready any picture of what we do want? One says, if you don't 
 know the goal, all paths are the wrong ones...
 
 A few things that I remember from various sources:
 
 * Some improvement in the realm of ranges.
 * Replacement of some old modules. (std.xml, std.outbuffer, std.socket, 
 what else?)
 * Adding some missing modules (std.eventloop, std.database, what else?)
 * Unifying some modules (everything covering selfinspection, like 
 std.traits and so on, whar else?)
 
 Anything more?
I expect to start work on std.eventloop as part of the (informal) graphics workgroup soon-ish (although it may not end up as part of Phobos). However my main concern with developing a new version of Phobos is isInputRange and friends will still exist. Unless the language has a better way of describing it e.g. signatures we may as well call it an incremental improvement not a new version.
Oct 16
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Oct 17, 2019 at 10:59:11AM +1300, rikki cattermole via Digitalmars-d
wrote:
[...]
 However my main concern with developing a new version of Phobos is
 isInputRange and friends will still exist. Unless the language has a
 better way of describing it e.g. signatures we may as well call it an
 incremental improvement not a new version.
What's wrong with isInputRange? T -- "Hi." "'Lo."
Oct 16
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 17/10/2019 11:16 AM, H. S. Teoh wrote:
 On Thu, Oct 17, 2019 at 10:59:11AM +1300, rikki cattermole via Digitalmars-d
wrote:
 [...]
 However my main concern with developing a new version of Phobos is
 isInputRange and friends will still exist. Unless the language has a
 better way of describing it e.g. signatures we may as well call it an
 incremental improvement not a new version.
What's wrong with isInputRange? T
So these sorts of "functions" are aggregates of behavior and properties that require reading the source code to know fully what it entails. Yes, aggregates of behavior can be complex, and it does make sense to use "functions" to do it. But the key difference here is if you can describe what most of the behaviors and properties are to the compiler it can produce better error messages but also allow code to be more descriptive. I.e. /// Docs go here signature InputRange( named ElementType) { property { /// Docs go here ElementType front(); /// Docs go here bool empty(); } /// Docs go here void popFront(); } void func(T:InputRange)(T myInputRange) {} The above uses DIP1020 just to make this conversation a little easier to understand (ElementType will be inferred automatically from the implementation). And because we have described what an input range is in terms of an interface, we can use it as a value typed vtable that yes can work in -betterC. Something like this should enable us to have better documentation using much simpler looking features without hiding away details in the source code.
Oct 16
next sibling parent reply Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
On Wednesday, 16 October 2019 at 22:34:00 UTC, rikki cattermole 
wrote:
 /// Docs go here
 signature InputRange( named ElementType) {
 	 property {
 		/// Docs go here
 		ElementType front();

 		/// Docs go here
 		bool empty();
 	}

 	/// Docs go here
 	void popFront();
 }

 void func(T:InputRange)(T myInputRange) {}
The experimental.typecons has an incomplete modification to wrap. It allows wrapping a structure into an interface if it conforms. If the edge cases were worked out then similar logic would be applicable to the function signature. ...) if(implements!(InputRange, R)) What would be nice from the language is the ability to print a message if no template matching occurs. Remember if it does not match it is just pulled from overload resolution.
Oct 16
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 17/10/2019 1:36 PM, Jesse Phillips wrote:
 
 On Wednesday, 16 October 2019 at 22:34:00 UTC, rikki cattermole wrote:
 /// Docs go here
 signature InputRange( named ElementType) {
      property {
         /// Docs go here
         ElementType front();

         /// Docs go here
         bool empty();
     }

     /// Docs go here
     void popFront();
 }

 void func(T:InputRange)(T myInputRange) {}
The experimental.typecons has an incomplete modification to wrap. It allows wrapping a structure into an interface if it conforms. If the edge cases were worked out then similar logic would be applicable to the function signature. ...) if(implements!(InputRange, R)) What would be nice from the language is the ability to print a message if no template matching occurs. Remember if it does not match it is just pulled from overload resolution.
That works for matching an implementation to an interface. It does miss out on better error messages and any ability to let the interface override or extend the implementation (e.g. extraction of ElementType from front or using the declared type in body of the implementation). I.e. signature InputRange( named ElementType = ElementTypeCompat!(typeof(this)) { ... } Not to mention it forces what amounts to idiomatic D into a second class position in many use cases. Not preferable IMO.
Oct 16
parent reply Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
On Thursday, 17 October 2019 at 00:46:12 UTC, rikki cattermole 
wrote:
 I.e.

 signature InputRange( named ElementType = 
 ElementTypeCompat!(typeof(this)) { ... }

 Not to mention it forces what amounts to idiomatic D into a 
 second class position in many use cases. Not preferable IMO.
auto foo(R, E = ElementType!R)() if(implements!(InputRange!E, R)) That seems pretty idiomatic D. Now as you said, it doesn't improve the error message. But what if the language changed so it could?
Oct 16
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 17/10/2019 4:32 PM, Jesse Phillips wrote:
 On Thursday, 17 October 2019 at 00:46:12 UTC, rikki cattermole wrote:
 I.e.

 signature InputRange( named ElementType = 
 ElementTypeCompat!(typeof(this)) { ... }

 Not to mention it forces what amounts to idiomatic D into a second 
 class position in many use cases. Not preferable IMO.
auto foo(R, E = ElementType!R)() if(implements!(InputRange!E, R)) That seems pretty idiomatic D.
And that's the problem, for what is very common, it is absolutely horrible code.
 Now as you said, it doesn't improve the error message. But what if the 
 language changed so it could?
That would require flow analysis and it won't give good names to different aspects. We have done research into template constraints and to improve the error messages there. But aggregate checks like isInputRange and with that implements, the compiler can't understand it even with flow analysis sadly. Right now there is no way to communicate to the compiler the human aspects of a concept. Sure we can do the technical, but that only gets us so far (which we have been doing). This is what I am arguing for, a way to communicate to the compiler the human aspects of a concept. The behaviors and properties it must exhibit to match our mental model, not the compilers model.
Oct 16
parent FeepingCreature <feepingcreature gmail.com> writes:
On Thursday, 17 October 2019 at 03:46:23 UTC, rikki cattermole 
wrote:
 On 17/10/2019 4:32 PM, Jesse Phillips wrote:
 Now as you said, it doesn't improve the error message. But 
 what if the language changed so it could?
That would require flow analysis and it won't give good names to different aspects. We have done research into template constraints and to improve the error messages there. But aggregate checks like isInputRange and with that implements, the compiler can't understand it even with flow analysis sadly.
I still think the only change that is needed is to make the operators return an error value instead of a boolean on failure. The error value then just needs to propagate properly through && and ||, and you should get an unambiguous error. You could even add further information with a wrapper template, as long as the error object is inspectable and rewritable. I don't think the right approach is to have the compiler automagically produce good error messages, but rather give the template developers tools to help the compiler inform the user what actually went wrong.
Oct 17
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On Wednesday, 16 October 2019 at 22:34:00 UTC, rikki cattermole 
wrote:

 So these sorts of "functions" are aggregates of behavior and 
 properties that require reading the source code to know fully 
 what it entails.

 Yes, aggregates of behavior can be complex, and it does make 
 sense to use "functions" to do it. But the key difference here 
 is if you can describe what most of the behaviors and 
 properties are to the compiler it can produce better error 
 messages but also allow code to be more descriptive. I.e.

 /// Docs go here
 signature InputRange( named ElementType) {
 	 property {
 		/// Docs go here
 		ElementType front();

 		/// Docs go here
 		bool empty();
 	}

 	/// Docs go here
 	void popFront();
 }

 void func(T:InputRange)(T myInputRange) {}

 The above uses DIP1020 just to make this conversation a little 
 easier to understand (ElementType will be inferred 
 automatically from the implementation).

 And because we have described what an input range is in terms 
 of an interface, we can use it as a value typed vtable that yes 
 can work in -betterC.

 Something like this should enable us to have better 
 documentation using much simpler looking features without 
 hiding away details in the source code.
Can't we just allow structs to implement interfaces and use template specialization? Something like this: interface InputRange(ElementType) { ElementType front(); bool empty(); void popFront(); } struct Range(E) : InputRange!E { E front() { return E.init; } bool empty() { return true; } void popFront() { } } void foo(T : InputRange!int)(T range) {} void bar() { InputRange!int a = Range!int.init; // will not compile } -- /Jacob Carlborg
Oct 17
parent rikki cattermole <rikki cattermole.co.nz> writes:
On 18/10/2019 12:48 AM, Jacob Carlborg wrote:
 Can't we just allow structs to implement interfaces and use template 
 specialization? Something like this:
 
 interface InputRange(ElementType)
 {
      ElementType front();
      bool empty();
      void popFront();
 }
 
 struct Range(E) : InputRange!E
 {
      E front() { return E.init; }
      bool empty() { return true; }
      void popFront() { }
 }
 
 void foo(T : InputRange!int)(T range) {}
 
 void bar()
 {
      InputRange!int a = Range!int.init; // will not compile
 }
 
 -- 
 /Jacob Carlborg
That is a solution yes. The reason I do not believe it is one of the better ones is because the struct has to inherit from the interface (and fields are not supported). For reference Basile came up with this very solution over a year ago. The way we use input ranges today and with that DbI in general, is that the interface inherits from the implementation as part of the declaration of its usage. This is a very different approach to how we think of classes and much more inline with signatures from ML[0]. [0] https://www.cs.cmu.edu/~rwh/introsml/modules/sigstruct.htm
Oct 17
prev sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 16 October 2019 at 22:34:00 UTC, rikki cattermole 
wrote:
 On 17/10/2019 11:16 AM, H. S. Teoh wrote:
 [...]
So these sorts of "functions" are aggregates of behavior and properties that require reading the source code to know fully what it entails. [...]
https://github.com/atilaneves/concepts/blob/master/source/concepts/implements.d
Oct 17
parent reply Meta <jared771 gmail.com> writes:
On Thursday, 17 October 2019 at 16:21:18 UTC, Atila Neves wrote:
 On Wednesday, 16 October 2019 at 22:34:00 UTC, rikki cattermole 
 wrote:
 On 17/10/2019 11:16 AM, H. S. Teoh wrote:
 [...]
So these sorts of "functions" are aggregates of behavior and properties that require reading the source code to know fully what it entails. [...]
https://github.com/atilaneves/concepts/blob/master/source/concepts/implements.d
Correct me if I'm wrong, but would using this with std.range be as simple as: import std.range; implements!(MyCoolRange, InputRange) struct MyCoolRange(T) { .... } long sum(R)(R range) if (implements!(R, InputRange)) { .... }
Oct 17
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 17 October 2019 at 19:37:38 UTC, Meta wrote:
 [snip]

 Correct me if I'm wrong, but would using this with std.range be 
 as simple as:

 import std.range;

  implements!(MyCoolRange, InputRange)
 struct MyCoolRange(T)
 {
     ....
 }

 long sum(R)(R range)
 if (implements!(R, InputRange))
 {
     ....
 }
implements is for an interface. You would actually use models (with the isInputRange from the concepts library, not phobos), so change implements!(MyCoolRange, InputRange) to models!(MyCoolRange, isInputRange) and you don't need models for below, just change if (implements!(R, InputRange)) to if (isInputRange!R) but make sure you are using the one from the concepts library.
Oct 17
parent reply Meta <jared771 gmail.com> writes:
On Thursday, 17 October 2019 at 20:50:54 UTC, jmh530 wrote:
 On Thursday, 17 October 2019 at 19:37:38 UTC, Meta wrote:
 [snip]

 Correct me if I'm wrong, but would using this with std.range 
 be as simple as:

 import std.range;

  implements!(MyCoolRange, InputRange)
 struct MyCoolRange(T)
 {
     ....
 }

 long sum(R)(R range)
 if (implements!(R, InputRange))
 {
     ....
 }
implements is for an interface. You would actually use models (with the isInputRange from the concepts library, not phobos), so change implements!(MyCoolRange, InputRange) to models!(MyCoolRange, isInputRange) and you don't need models for below, just change if (implements!(R, InputRange)) to if (isInputRange!R) but make sure you are using the one from the concepts library.
I'm referring to the InputRange et al. interfaces from std.range.interfaces: https://dlang.org/phobos/std_range_interfaces.html
Oct 17
parent jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 17 October 2019 at 21:24:15 UTC, Meta wrote:
 [snip]

 I'm referring to the InputRange et al. interfaces from 
 std.range.interfaces:
 https://dlang.org/phobos/std_range_interfaces.html
Oh sorry. I completely forgot that was in there.
Oct 17
prev sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 18/10/2019 8:37 AM, Meta wrote:
 On Thursday, 17 October 2019 at 16:21:18 UTC, Atila Neves wrote:
 On Wednesday, 16 October 2019 at 22:34:00 UTC, rikki cattermole wrote:
 On 17/10/2019 11:16 AM, H. S. Teoh wrote:
 [...]
So these sorts of "functions" are aggregates of behavior and properties that require reading the source code to know fully what it entails. [...]
https://github.com/atilaneves/concepts/blob/master/source/c ncepts/implements.d
Correct me if I'm wrong, but would using this with std.range be as simple as: import std.range; implements!(MyCoolRange, InputRange) struct MyCoolRange(T) {     .... } long sum(R)(R range) if (implements!(R, InputRange)) {     .... }
This unfortunately still ties the implementation itself to the interface. Which goes against DbI.
Oct 17
parent reply Meta <jared771 gmail.com> writes:
On Friday, 18 October 2019 at 00:19:08 UTC, rikki cattermole 
wrote:
 Correct me if I'm wrong, but would using this with std.range 
 be as simple as:
 
 import std.range;
 
  implements!(MyCoolRange, InputRange)
 struct MyCoolRange(T)
 {
      ....
 }
 
 long sum(R)(R range)
 if (implements!(R, InputRange))
 {
      ....
 }
This unfortunately still ties the implementation itself to the interface. Which goes against DbI.
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: //Unfortunately this is necessary so that R is in scope for the UDAs. //Maybe it can be avoided with std.traits.TemplateArgsOf / TemplateOf? template MapResult(R) { //MapResult is always at least an input range implements!(MapResult, InputRange) //If the wrapped type is a forward range, MapResult //is also a forward range and will forward to its implementation given!(implements!(R, ForwardRange), implements!(inheritImpl!(ForwardRange, MapResult, R), ForwardRange)) //etc. struct MapResult { R store; mixin forwardTo!(ForwardRange, store); //Input range primitives defined down here } }
Oct 18
parent reply jmh530 <john.michael.hall gmail.com> writes:
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?
Oct 18
parent reply Meta <jared771 gmail.com> writes:
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.
Oct 18
parent reply jmh530 <john.michael.hall gmail.com> writes:
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.
Oct 18
parent reply Meta <jared771 gmail.com> writes:
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.
Oct 18
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
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).
Oct 18
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
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 { ... }
Oct 19
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
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.
Oct 19
parent Paul Backus <snarwin gmail.com> writes:
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.
Oct 19
prev sibling parent reply Meta <jared771 gmail.com> writes:
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.
Oct 19
parent rikki cattermole <rikki cattermole.co.nz> writes:
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.
Oct 19
prev sibling next sibling parent Rel <relmail rambler.ru> writes:
On Wednesday, 16 October 2019 at 17:20:28 UTC, berni44 wrote:
 In the feedback thread H. S. Teoh is talking about that we need 
 a new/better version of Phobos. I remember having read the same 
 in a comment on some PR recently. How would this better version 
 look like? Is there allready any picture of what we do want? 
 One says, if you don't know the goal, all paths are the wrong 
 ones...

 A few things that I remember from various sources:

 * Some improvement in the realm of ranges.
 * Replacement of some old modules. (std.xml, std.outbuffer, 
 std.socket, what else?)
 * Adding some missing modules (std.eventloop, std.database, 
 what else?)
 * Unifying some modules (everything covering selfinspection, 
 like std.traits and so on, whar else?)

 Anything more?
I'd like Phobos to be something like Go's stdlib in a sence that a lot of the stuff needed for day to day programming would be builtin. Crypto stuff, compression stuff, https stuff and etc.
Oct 17
prev sibling parent reply berni44 <dlang d-ecke.de> writes:
On Wednesday, 16 October 2019 at 17:20:28 UTC, berni44 wrote:
 How would this better version look like?
I hoped to be able to summarize all the things, that you wrote, but effectivly there is only little, that came up. Thanks anyway to all of you who answered. From my point of few there are two quite different things to do: a) Adding, removing and combining modules. That's something, that should be doable, partly by using deprecation, partly even without. This could be called "incremental improvement". b) Correcting design mistakes made in the past. This sounds much more difficult, especially when it affects stuff, that is used quite often, like strings and ranges. And there is the fear, that this might bring down D alltogether. This would probably need a "new version".
Oct 20
parent rikki cattermole <rikki cattermole.co.nz> writes:
On 21/10/2019 2:37 AM, berni44 wrote:
 b) Correcting design mistakes made in the past. This sounds much more 
 difficult, especially when it affects stuff, that is used quite often, 
 like strings and ranges. And there is the fear, that this might bring 
 down D alltogether. This would probably need a "new version".
I wouldn't worry about it, we can do it incrementally and limit breakage. And we can always split out any module that is being removed we have done it before i.e. std.streams
Oct 20