www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - syntax sugar: std.path::buildPath instead of from!"std.path".buildPath

reply Timothee Cour via Digitalmars-d <digitalmars-d puremagic.com> writes:
What about allowing syntax sugar as an alternative to relying on the new
`from/Module` inline import idiom:

```
void fun(T)(std.stdio::File input, T value) if (std.traits::isIntegral!T)
{...}
```

instead of:

```
void fun(T)(Module!"std.stdio".File input, T value) if
(Module!"std.traits".isIntegral!T) {...}
```

Rationale:

* this reads much better (less noise); same as `=>` syntax for lambdas

* this is expected to be a very common pattern, so might as well make it as
simple as possible

* not particular on which symbol is used, could be something else, so long
it doesn't involve writing a string such as from!"std.traits". But :: will
be familiar to those coming from C++/rust etc.

* from!"" is too loose and can be abused arbitrarily:

```
// this compiles
void fun(){
  from!"std.stdio; pragma(msg,`abuse...`); import std.stdio".File a;
}
```

Furthermore this is useful in other scenarios, namely when an import is
used only once in a context:
```
auto fun(){  return std.file::getcwd; }
```
is more DRY; instead of:
```
auto fun(){ static import std.file;  return std.file.getcwd; }
auto fun(){ return Module!"std.file".getcwd; }
```

NOTE: if :: is not feasible for whatever reason, let's consider other
symbols without prejudice to this proposal.
Feb 13 2017
next sibling parent reply Jack Stouffer <jack jackstouffer.com> writes:
On Tuesday, 14 February 2017 at 03:49:28 UTC, Timothee Cour wrote:
 What about allowing syntax sugar as an alternative to relying 
 on the new `from/Module` inline import idiom:

 ```
 void fun(T)(std.stdio::File input, T value) if 
 (std.traits::isIntegral!T)
 {...}
 ```

 instead of:

 ```
 void fun(T)(Module!"std.stdio".File input, T value) if
 (Module!"std.traits".isIntegral!T) {...}
 ```

 Rationale:

 * this reads much better (less noise); same as `=>` syntax for 
 lambdas

 * this is expected to be a very common pattern, so might as 
 well make it as simple as possible

 * not particular on which symbol is used, could be something 
 else, so long it doesn't involve writing a string such as 
 from!"std.traits". But :: will be familiar to those coming from 
 C++/rust etc.

 * from!"" is too loose and can be abused arbitrarily:

 ```
 // this compiles
 void fun(){
   from!"std.stdio; pragma(msg,`abuse...`); import 
 std.stdio".File a;
 }
 ```

 Furthermore this is useful in other scenarios, namely when an 
 import is
 used only once in a context:
 ```
 auto fun(){  return std.file::getcwd; }
 ```
 is more DRY; instead of:
 ```
 auto fun(){ static import std.file;  return std.file.getcwd; }
 auto fun(){ return Module!"std.file".getcwd; }
 ```

 NOTE: if :: is not feasible for whatever reason, let's consider 
 other symbols without prejudice to this proposal.
The only thing from has going for it is that it doesn't require a language change. Yours does, so it should be compared to the proposals in DIP1005 and not just from. TBQH all the the proposed syntaxes for the DIP1005 problem kind of suck, because at the end of the day there's no good way to solve the problem. Every proposed solution either 1. Looks terrible (and some people highly underestimate the cost of a language looking bad), or 2. Complicate our already complex function definitions even further. How is a beginner not going to look at this and feel like barfing with (import std.datetime, std.range.primitives) void fun(R)(scope R r, const ref Systime value) if (isInputRange!R && !isFinite!R) { ... } But, I'm still of the opinion that the `with` syntax is currently the best proposal. Others have already shown the weaknesses of the `from!` approach.
Feb 13 2017
next sibling parent Timothee Cour via Digitalmars-d <digitalmars-d puremagic.com> writes:
 The only thing from has going for it is that it doesn't require a
language change. Yours does, so it should be compared to the proposals in DIP1005 and not just from. indeed. But just because something can be done in library code doesn't mean it should. Same exact rationale for: `lazy`, `=>`, scope(exit), etc. All of these coul've been done in pure library code, but the syntax sugar makes them particularly easy to use. Syntax sugar should be used sparingly, but it makes sense if * it simplifies syntax compared to best possible library code * it's for a common use case On Mon, Feb 13, 2017 at 9:03 PM, Jack Stouffer via Digitalmars-d < digitalmars-d puremagic.com> wrote:
 On Tuesday, 14 February 2017 at 03:49:28 UTC, Timothee Cour wrote:

 What about allowing syntax sugar as an alternative to relying on the new
 `from/Module` inline import idiom:

 ```
 void fun(T)(std.stdio::File input, T value) if (std.traits::isIntegral!T)
 {...}
 ```

 instead of:

 ```
 void fun(T)(Module!"std.stdio".File input, T value) if
 (Module!"std.traits".isIntegral!T) {...}
 ```

 Rationale:

 * this reads much better (less noise); same as `=>` syntax for lambdas

 * this is expected to be a very common pattern, so might as well make it
 as simple as possible

 * not particular on which symbol is used, could be something else, so
 long it doesn't involve writing a string such as from!"std.traits". But ::
 will be familiar to those coming from C++/rust etc.

 * from!"" is too loose and can be abused arbitrarily:

 ```
 // this compiles
 void fun(){
   from!"std.stdio; pragma(msg,`abuse...`); import std.stdio".File a;
 }
 ```

 Furthermore this is useful in other scenarios, namely when an import is
 used only once in a context:
 ```
 auto fun(){  return std.file::getcwd; }
 ```
 is more DRY; instead of:
 ```
 auto fun(){ static import std.file;  return std.file.getcwd; }
 auto fun(){ return Module!"std.file".getcwd; }
 ```

 NOTE: if :: is not feasible for whatever reason, let's consider other
 symbols without prejudice to this proposal.
The only thing from has going for it is that it doesn't require a language change. Yours does, so it should be compared to the proposals in DIP1005 and not just from. TBQH all the the proposed syntaxes for the DIP1005 problem kind of suck, because at the end of the day there's no good way to solve the problem. Every proposed solution either 1. Looks terrible (and some people highly underestimate the cost of a language looking bad), or 2. Complicate our already complex function definitions even further. How is a beginner not going to look at this and feel like barfing with (import std.datetime, std.range.primitives) void fun(R)(scope R r, const ref Systime value) if (isInputRange!R && !isFinite!R) { ... } But, I'm still of the opinion that the `with` syntax is currently the best proposal. Others have already shown the weaknesses of the `from!` approach.
Feb 13 2017
prev sibling parent reply Timothee Cour via Digitalmars-d <digitalmars-d puremagic.com> writes:
as for language change, I believe the proposed syntax (`::`) can be reduced
to a minimum if we we make `fullyqualifiedName` `::` a reduction to the
from/Module inline import idiom. No complex corner case or new bug
introduced, it could be done as a pure AST transformation


On Mon, Feb 13, 2017 at 9:32 PM, Timothee Cour <thelastmammoth gmail.com>
wrote:

 The only thing from has going for it is that it doesn't require a
language change. Yours does, so it should be compared to the proposals in DIP1005 and not just from. indeed. But just because something can be done in library code doesn't mean it should. Same exact rationale for: `lazy`, `=>`, scope(exit), etc. All of these coul've been done in pure library code, but the syntax sugar makes them particularly easy to use. Syntax sugar should be used sparingly, but it makes sense if * it simplifies syntax compared to best possible library code * it's for a common use case On Mon, Feb 13, 2017 at 9:03 PM, Jack Stouffer via Digitalmars-d < digitalmars-d puremagic.com> wrote:
 On Tuesday, 14 February 2017 at 03:49:28 UTC, Timothee Cour wrote:

 What about allowing syntax sugar as an alternative to relying on the new
 `from/Module` inline import idiom:

 ```
 void fun(T)(std.stdio::File input, T value) if (std.traits::isIntegral!T)
 {...}
 ```

 instead of:

 ```
 void fun(T)(Module!"std.stdio".File input, T value) if
 (Module!"std.traits".isIntegral!T) {...}
 ```

 Rationale:

 * this reads much better (less noise); same as `=>` syntax for lambdas

 * this is expected to be a very common pattern, so might as well make it
 as simple as possible

 * not particular on which symbol is used, could be something else, so
 long it doesn't involve writing a string such as from!"std.traits". But ::
 will be familiar to those coming from C++/rust etc.

 * from!"" is too loose and can be abused arbitrarily:

 ```
 // this compiles
 void fun(){
   from!"std.stdio; pragma(msg,`abuse...`); import std.stdio".File a;
 }
 ```

 Furthermore this is useful in other scenarios, namely when an import is
 used only once in a context:
 ```
 auto fun(){  return std.file::getcwd; }
 ```
 is more DRY; instead of:
 ```
 auto fun(){ static import std.file;  return std.file.getcwd; }
 auto fun(){ return Module!"std.file".getcwd; }
 ```

 NOTE: if :: is not feasible for whatever reason, let's consider other
 symbols without prejudice to this proposal.
The only thing from has going for it is that it doesn't require a language change. Yours does, so it should be compared to the proposals in DIP1005 and not just from. TBQH all the the proposed syntaxes for the DIP1005 problem kind of suck, because at the end of the day there's no good way to solve the problem. Every proposed solution either 1. Looks terrible (and some people highly underestimate the cost of a language looking bad), or 2. Complicate our already complex function definitions even further. How is a beginner not going to look at this and feel like barfing with (import std.datetime, std.range.primitives) void fun(R)(scope R r, const ref Systime value) if (isInputRange!R && !isFinite!R) { ... } But, I'm still of the opinion that the `with` syntax is currently the best proposal. Others have already shown the weaknesses of the `from!` approach.
Feb 13 2017
parent Walter Bright <newshound2 digitalmars.com> writes:
Please fix your newsreader client to send as text, not as html.

The html is both excessively large, and has randomly changing fonts embedded in
it.
Feb 14 2017
prev sibling next sibling parent reply Chris M <chrismohrfeld comcast.net> writes:
On Tuesday, 14 February 2017 at 03:49:28 UTC, Timothee Cour wrote:
 What about allowing syntax sugar as an alternative to relying 
 on the new `from/Module` inline import idiom:

 ```
 void fun(T)(std.stdio::File input, T value) if 
 (std.traits::isIntegral!T)
 {...}
 ```

 instead of:

 ```
 void fun(T)(Module!"std.stdio".File input, T value) if
 (Module!"std.traits".isIntegral!T) {...}
 ```

 Rationale:

 * this reads much better (less noise); same as `=>` syntax for 
 lambdas

 * this is expected to be a very common pattern, so might as 
 well make it as simple as possible

 * not particular on which symbol is used, could be something 
 else, so long it doesn't involve writing a string such as 
 from!"std.traits". But :: will be familiar to those coming from 
 C++/rust etc.

 * from!"" is too loose and can be abused arbitrarily:

 ```
 // this compiles
 void fun(){
   from!"std.stdio; pragma(msg,`abuse...`); import 
 std.stdio".File a;
 }
 ```

 Furthermore this is useful in other scenarios, namely when an 
 import is
 used only once in a context:
 ```
 auto fun(){  return std.file::getcwd; }
 ```
 is more DRY; instead of:
 ```
 auto fun(){ static import std.file;  return std.file.getcwd; }
 auto fun(){ return Module!"std.file".getcwd; }
 ```

 NOTE: if :: is not feasible for whatever reason, let's consider 
 other symbols without prejudice to this proposal.
Well, as Jack said there's no real clean way to do this without cluttering the function signatures more. However if this does happen through a language change, :: would look kind of awkward imo. I'd go with the following idea instead. void fun(T)(#std.stdio.File input, T value) if (#std.traits.isIntegral!T) {...} Doesn't necessarily have to be the octothorpe, but I think it looks a bit cleaner.
Feb 13 2017
parent Timothee Cour via Digitalmars-d <digitalmars-d puremagic.com> writes:
`#std.stdio.File` doesn't work, we don't know where is the module
separator:
that could mean `module std` with a class stdio with a field File.

so the syntax would have be:

`std.stdio SYMBOL File`

frankly I don't care which symbol, so long it looks like that. And as I
said, `std.stdio::File` is reasonable since it's used for similar purpose
in rust and C++



On Mon, Feb 13, 2017 at 10:21 PM, Chris M via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 On Tuesday, 14 February 2017 at 03:49:28 UTC, Timothee Cour wrote:

 What about allowing syntax sugar as an alternative to relying on the new
 `from/Module` inline import idiom:

 ```
 void fun(T)(std.stdio::File input, T value) if (std.traits::isIntegral!T)
 {...}
 ```

 instead of:

 ```
 void fun(T)(Module!"std.stdio".File input, T value) if
 (Module!"std.traits".isIntegral!T) {...}
 ```

 Rationale:

 * this reads much better (less noise); same as `=>` syntax for lambdas

 * this is expected to be a very common pattern, so might as well make it
 as simple as possible

 * not particular on which symbol is used, could be something else, so
 long it doesn't involve writing a string such as from!"std.traits". But ::
 will be familiar to those coming from C++/rust etc.

 * from!"" is too loose and can be abused arbitrarily:

 ```
 // this compiles
 void fun(){
   from!"std.stdio; pragma(msg,`abuse...`); import std.stdio".File a;
 }
 ```

 Furthermore this is useful in other scenarios, namely when an import is
 used only once in a context:
 ```
 auto fun(){  return std.file::getcwd; }
 ```
 is more DRY; instead of:
 ```
 auto fun(){ static import std.file;  return std.file.getcwd; }
 auto fun(){ return Module!"std.file".getcwd; }
 ```

 NOTE: if :: is not feasible for whatever reason, let's consider other
 symbols without prejudice to this proposal.
Well, as Jack said there's no real clean way to do this without cluttering the function signatures more. However if this does happen through a language change, :: would look kind of awkward imo. I'd go with the following idea instead. void fun(T)(#std.stdio.File input, T value) if (#std.traits.isIntegral!T) {...} Doesn't necessarily have to be the octothorpe, but I think it looks a bit cleaner.
Feb 13 2017
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2017-02-14 04:49, Timothee Cour via Digitalmars-d wrote:
 What about allowing syntax sugar as an alternative to relying on the new
 `from/Module` inline import idiom:

 ```
 void fun(T)(std.stdio::File input, T value) if
 (std.traits::isIntegral!T) {...}
 ```

 instead of:

 ```
 void fun(T)(Module!"std.stdio".File input, T value) if
 (Module!"std.traits".isIntegral!T) {...}
 ```

 Rationale:

 * this reads much better (less noise); same as `=>` syntax for lambdas

 * this is expected to be a very common pattern, so might as well make it
 as simple as possible
Why? It looks awful. The signatures we already have in Phobos is quite ridiculous, this will not improve. Isn't this and the whole idea of DIP 1005 just a workaround for the compiler not lazily analyzing the symbols. -- /Jacob Carlborg
Feb 14 2017
next sibling parent Daniel N <no public.email> writes:
On Tuesday, 14 February 2017 at 09:55:51 UTC, Jacob Carlborg 
wrote:
 Why? It looks awful. The signatures we already have in Phobos 
 is quite ridiculous, this will not improve. Isn't this and the 
 whole idea of DIP 1005 just a workaround for the compiler not 
 lazily analyzing the symbols.
Even those of you who favor... with(import std.stdio) ... wouldn't you agree that this is better? with(Module!"std.stdio") Less intrusive compiler change because the same syntax already works everywhere "with" is valid.
Feb 14 2017
prev sibling next sibling parent Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Tuesday, February 14, 2017 10:55:51 Jacob Carlborg via Digitalmars-d 
wrote:
 On 2017-02-14 04:49, Timothee Cour via Digitalmars-d wrote:
 What about allowing syntax sugar as an alternative to relying on the new
 `from/Module` inline import idiom:

 ```
 void fun(T)(std.stdio::File input, T value) if
 (std.traits::isIntegral!T) {...}
 ```

 instead of:

 ```
 void fun(T)(Module!"std.stdio".File input, T value) if
 (Module!"std.traits".isIntegral!T) {...}
 ```

 Rationale:

 * this reads much better (less noise); same as `=>` syntax for lambdas

 * this is expected to be a very common pattern, so might as well make it
 as simple as possible
Why? It looks awful. The signatures we already have in Phobos is quite ridiculous, this will not improve. Isn't this and the whole idea of DIP 1005 just a workaround for the compiler not lazily analyzing the symbols.
Mostly, but not just that. By having something like DIP 1005, all of the imports required for the function can be associated with the function. So, people reading the code then have an easier time figuring out where the symbols come from, and it could be that the compiler could take advantage of that somehow. It does seem though that the main motivator for DIP 1005 is the fact that we don't have lazy imports, and Walter has talked before about wanting to do fully lazy imports. So, I expect that we'll get them at some point, at which point, any performance improvements from DIP 1005 or the from template would likely become moot, and the only benefit left is essentially documentation. Personally, I might use DIP 1005 in some cases if we end up with it, but I won't use from unless I'm forced to. As cool as it is that we can do it with the language as-is, it's far too verbose and far too ugly. As it is, I'm getting sick of doing local and selective imports all over the place like everyone seems to think is best practice. I understand why they're theoretically good, but it results in longer code, and it gets very tedious to have to try and maintain the list of symbols that are imported instead of just importing the modules you need at the top of the module and forgetting about it. Doing what DIP 1005 and from push for just takes that pain to another level. It might be worth it in code used by tons of people (like Phobos), but for most code, I'm increasingly of the opinion that it's not worth it. And having proper lazy imports would largely fix the compilation cost of simply importing modules. So, I'm very much in favor of getting lazy imports, and while I do see _some_ value in documenting where stuff comes from via something like DIP 1005, I seriosuly question that it's worth the extra pain. And when you have all of those function attributes to worry about on top of that... It's just getting to be too much. - Jonathan M Davis
Feb 14 2017
prev sibling next sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Tuesday, 14 February 2017 at 09:55:51 UTC, Jacob Carlborg 
wrote:
 Why? It looks awful. The signatures we already have in Phobos 
 is quite ridiculous, this will not improve. Isn't this and the 
 whole idea of DIP 1005 just a workaround for the compiler not 
 lazily analyzing the symbols.
This.
Feb 14 2017
prev sibling next sibling parent Meta <jared771 gmail.com> writes:
On Tuesday, 14 February 2017 at 09:55:51 UTC, Jacob Carlborg 
wrote:
 On 2017-02-14 04:49, Timothee Cour via Digitalmars-d wrote:
 What about allowing syntax sugar as an alternative to relying 
 on the new
 `from/Module` inline import idiom:

 ```
 void fun(T)(std.stdio::File input, T value) if
 (std.traits::isIntegral!T) {...}
 ```

 instead of:

 ```
 void fun(T)(Module!"std.stdio".File input, T value) if
 (Module!"std.traits".isIntegral!T) {...}
 ```

 Rationale:

 * this reads much better (less noise); same as `=>` syntax for 
 lambdas

 * this is expected to be a very common pattern, so might as 
 well make it
 as simple as possible
Why? It looks awful. The signatures we already have in Phobos is quite ridiculous, this will not improve. Isn't this and the whole idea of DIP 1005 just a workaround for the compiler not lazily analyzing the symbols.
This is what I had thought as well when Andrei first posted DIP1005, but he said that his main goal is actually making it so a declaration can carry all of its imports with it in a self-contained unit. When you look at it that way it makes a bit more sense as to why we might want constructs such as introduced by DIP1005 or this self-important lookup idiom.
Feb 14 2017
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 02/14/2017 04:55 AM, Jacob Carlborg wrote:
 The signatures we already have in Phobos is quite ridiculous
How are they so, and what steps can we take to improve them. Could you please give a few examples on how to do things better? Thx! -- Andrei
Feb 14 2017
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2017-02-14 15:37, Andrei Alexandrescu wrote:

 How are they so,
Example [1]. That signature spans 8 lines, it took me 10 seconds to find the actual function name. Example [2], 5 lines. Adding attributes on top of that would increase the length of the signature even more. Fortunately templates will infer most attributes.
 and what steps can we take to improve them. Could you
 please give a few examples on how to do things better? Thx! -- Andrei
Well, I would prefer to have template constraints as its own entity in the language not not just a bunch of boolean conditions. This has been talked about before several times. Something like: constraint Foo { void foo(); } void bar(T : Foo)(T t); [1] https://github.com/dlang/phobos/blob/master/std/algorithm/mutation.d#L1682-L1689 [2] https://github.com/dlang/phobos/blob/master/std/algorithm/mutation.d#L2729-L2733 -- /Jacob Carlborg
Feb 14 2017
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 02/14/2017 10:49 AM, Jacob Carlborg wrote:
 On 2017-02-14 15:37, Andrei Alexandrescu wrote:

 How are they so,
Example [1]. That signature spans 8 lines, it took me 10 seconds to find the actual function name.
Copying here to make things easier: Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if (s != SwapStrategy.stable && isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1); The function name is on the first line. I think 10 seconds would be an exaggeration of an admittedly real issue.
 Example [2], 5 lines.
Copying here as well (reflowed for email): Tuple!(InputRange1, InputRange2) swapRanges(InputRange1, InputRange2)(InputRange1 r1, InputRange2 r2) if (isInputRange!(InputRange1) && isInputRange!(InputRange2) && hasSwappableElements!(InputRange1) && hasSwappableElements!(InputRange2) && is(ElementType!(InputRange1) == ElementType!(InputRange2))); One immediate matter here is redundant parens, of which elimination would lead to the marginally more palatable: Tuple!(InputRange1, InputRange2) swapRanges(InputRange1, InputRange2)(InputRange1 r1, InputRange2 r2) if (isInputRange!InputRange1 && isInputRange!InputRange2 && hasSwappableElements!InputRange1 && hasSwappableElements!InputRange2 && is(ElementType!InputRange1 == ElementType!InputRange2));
 and what steps can we take to improve them. Could you
 please give a few examples on how to do things better? Thx! -- Andrei
Well, I would prefer to have template constraints as its own entity in the language not not just a bunch of boolean conditions. This has been talked about before several times. Something like: constraint Foo { void foo(); } void bar(T : Foo)(T t);
My recollection is past discussions got stalled because the approach is combinatorially bankrupt. How would one express the constraints of the functions above with simple named constraints? (Not rhetorical; please do provide an example if possible.) Before long there is an explosion in names ("BidirectionalWithLvalueElementsAndLength", ...). This is what has buried not one, but two concepts proposals for C++, leading to the current Concepts Lite proposal. I haven't followed that lately but I remember it combines an engineering effort to reduce the number of names introduced (for the standard library only, which is a limitation) with adding support to Boolean logic (surprise, surprise) to the feature (see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4377.pdf), the most recent proposal, which was rejected for C++17. Over time, C++ concepts have moved from the broken combinatorially-bankrupt form slowly toward D's Boolean constraints (just with a gratuitously awkward notation). I presume they will be merged into the language when they'll have capabilities comparable to D's system. The question is, given this experience, do we want to move the opposite direction? Andrei
Feb 14 2017
next sibling parent reply bachmeier <no spam.net> writes:
On Tuesday, 14 February 2017 at 16:25:17 UTC, Andrei Alexandrescu 
wrote:
 Range remove
 (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
 (Range range, Offset offset)
 if (s != SwapStrategy.stable
     && isBidirectionalRange!Range
     && hasLvalueElements!Range
     && hasLength!Range
     && Offset.length >= 1);
[...]
 My recollection is past discussions got stalled because the 
 approach is combinatorially bankrupt. How would one express the 
 constraints of the functions above with simple named 
 constraints? (Not rhetorical; please do provide an example if 
 possible.) Before long there is an explosion in names 
 ("BidirectionalWithLvalueElementsAndLength", ...). This is what 
 has buried not one, but two concepts proposals for C++, leading 
 to the current Concepts Lite proposal. I haven't followed that 
 lately but I remember it combines an engineering effort to 
 reduce the number of names introduced (for the standard library 
 only, which is a limitation) with adding support to Boolean 
 logic (surprise, surprise) to the feature (see 
 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4377.pdf), the most
recent proposal, which was rejected for C++17. Over time, C++ concepts have
moved from the broken combinatorially-bankrupt form slowly toward D's Boolean
constraints (just with a gratuitously awkward notation). I presume they will be
merged into the language when they'll have capabilities comparable to D's
system.

 The question is, given this experience, do we want to move the 
 opposite direction?
I am not familiar with all of the past discussion of this issue, but something that I have wondered is why we can't do something like alias fooConstraint = (s != SwapStrategy.stable && isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1); Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if fooConstraint;
Feb 14 2017
next sibling parent reply bachmeier <no spam.net> writes:
On Tuesday, 14 February 2017 at 19:08:53 UTC, bachmeier wrote:

 I am not familiar with all of the past discussion of this 
 issue, but something that I have wondered is why we can't do 
 something like

 alias fooConstraint = (s != SwapStrategy.stable
   && isBidirectionalRange!Range
   && hasLvalueElements!Range
   && hasLength!Range
   && Offset.length >= 1);

 Range remove
   (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
   (Range range, Offset offset) if fooConstraint;
I mean, is there some reason that this shouldn't be allowed? Any feature can be abused, but it looks useful in many cases.
Feb 14 2017
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Feb 14, 2017 at 07:14:08PM +0000, bachmeier via Digitalmars-d wrote:
 On Tuesday, 14 February 2017 at 19:08:53 UTC, bachmeier wrote:
 
 I am not familiar with all of the past discussion of this issue, but
 something that I have wondered is why we can't do something like
 
 alias fooConstraint = (s != SwapStrategy.stable
   && isBidirectionalRange!Range
   && hasLvalueElements!Range
   && hasLength!Range
   && Offset.length >= 1);
 
 Range remove
   (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
   (Range range, Offset offset) if fooConstraint;
I mean, is there some reason that this shouldn't be allowed? Any feature can be abused, but it looks useful in many cases.
This is already possible: enum fooConstraint(R) = isBidirectionalRange!Range && hasLvalueElements!Range && ... ; Range remove(SwapStrategy s, R)(...) if (fooConstraint!R) { ... } T -- "Maybe" is a strange word. When mom or dad says it it means "yes", but when my big brothers say it it means "no"! -- PJ jr.
Feb 14 2017
parent bachmeier <no spam.net> writes:
On Tuesday, 14 February 2017 at 19:15:06 UTC, H. S. Teoh wrote:

 This is already possible:

 	enum fooConstraint(R) = isBidirectionalRange!Range &&
 				hasLvalueElements!Range && ... ;

 	Range remove(SwapStrategy s, R)(...) if (fooConstraint!R) { 
 ... }


 T
That's good to know. I didn't try that syntax, though, which explains why I couldn't get it to work.
Feb 14 2017
prev sibling next sibling parent Jack Stouffer <jack jackstouffer.com> writes:
On Tuesday, 14 February 2017 at 19:08:53 UTC, bachmeier wrote:
 I am not familiar with all of the past discussion of this 
 issue, but something that I have wondered is why we can't do 
 something like
Because at that point, it's no longer a set of lego's being put together (design by introspection) which build a larger whole. It's the genericism problem of requiring a name for everything. At the end of the day this would actually make constraints less *verbose*, but more *complicated*.
Feb 14 2017
prev sibling next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 14 February 2017 at 19:08:53 UTC, bachmeier wrote:
 I am not familiar with all of the past discussion of this 
 issue, but something that I have wondered is why we can't do 
 something like

 alias fooConstraint = (s != SwapStrategy.stable
You can do that, it would just be a helper function / template instead of an alias. You can use the shorthand enum syntax: enum removable(R) = isBidirectionalRange!R && isInputRange!R; Range remove(Range)(Range r) if(removable!Range) { return r; } That works today. But the problems is: what name do you give it? Quite often it would just be `fooConstraint` or `removable` or other non-reusable things... you'd just be *adding* complication because you'd need to forward all the arguments and write different ones for each function anyway. There's a few common cases we might combine, like Phobos often uses `isInputRange!Range && is(ElementType!Range == Something)` and perhaps that could be combined into `isInputRangeOf!(Range, Something)` or something like that, but in many cases, there'd be an equal number of aliases constraints to existing inline ones anyway....
Feb 14 2017
parent bachmeier <no spam.net> writes:
On Tuesday, 14 February 2017 at 19:27:36 UTC, Adam D. Ruppe wrote:
 But the problems is: what name do you give it? Quite often it 
 would just be `fooConstraint` or `removable` or other 
 non-reusable things... you'd just be *adding* complication 
 because you'd need to forward all the arguments and write 
 different ones for each function anyway.

 There's a few common cases we might combine, like Phobos often 
 uses `isInputRange!Range && is(ElementType!Range == Something)` 
 and perhaps that could be combined into `isInputRangeOf!(Range, 
 Something)` or something like that, but in many cases, there'd 
 be an equal number of aliases constraints to existing inline 
 ones anyway....
Makes sense. I had assumed there would be a lot of duplication of constraints.
Feb 14 2017
prev sibling parent reply Chris Wright <dhasenan gmail.com> writes:
On Tue, 14 Feb 2017 19:08:53 +0000, bachmeier wrote:
 I am not familiar with all of the past discussion of this issue, but
 something that I have wondered is why we can't do something like
 
 alias fooConstraint = (s != SwapStrategy.stable
    && isBidirectionalRange!Range && hasLvalueElements!Range &&
    hasLength!Range && Offset.length >= 1);
 
 Range remove
    (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
    (Range range, Offset offset) if fooConstraint;
Because now I have to look up the definition of fooConstraint and then I have to look up the definition of each element within it. If the entirety of the constraint is in the body of that helper template, that's a step up from what we have today. But that would involve code duplication, which people tend to dislike.
Feb 14 2017
parent bachmeier <no spam.net> writes:
On Wednesday, 15 February 2017 at 01:09:59 UTC, Chris Wright 
wrote:
 Because now I have to look up the definition of fooConstraint 
 and then I have to look up the definition of each element 
 within it.
To be honest, I don't often look at function definitions for the template constraints, but this thread suggests that's the main reason others read source code. My opinion is that inlining the constraints greatly detracts from readability and provides little benefit in return. I also don't understand the problem with using simple names.
Feb 15 2017
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/14/2017 8:25 AM, Andrei Alexandrescu wrote:
 Range remove
 (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
 (Range range, Offset offset)
 if (s != SwapStrategy.stable
     && isBidirectionalRange!Range
     && hasLvalueElements!Range
     && hasLength!Range
     && Offset.length >= 1);

 The function name is on the first line.
It could be improved slightly using indentation: Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if (s != SwapStrategy.stable && isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1); But there's another issue here. remove() has other overloads: Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if (s == SwapStrategy.stable && isBidirectionalRange!Range && hasLvalueElements!Range && Offset.length >= 1) Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range) (Range range) if (isBidirectionalRange!Range && hasLvalueElements!Range) Two constraints are common to all three, those are the only ones that actually need to be in the constraint. The others can go in the body under `static if`, as the user need not be concerned with them. This is a general issue in Phobos that too many constraints are user-facing when they don't need to be. A further improvement in the documentation would be to add links to isBidirectionalRange and hasLvalueElements.
Feb 14 2017
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/14/2017 12:46 PM, Walter Bright wrote:
 A further improvement in the documentation would be to add links to
 isBidirectionalRange and hasLvalueElements.
For reference:
Feb 14 2017
prev sibling next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Feb 14, 2017 at 12:46:17PM -0800, Walter Bright via Digitalmars-d wrote:
[...]
 Range remove
     (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
     (Range range, Offset offset)
     if (s != SwapStrategy.stable
         && isBidirectionalRange!Range
         && hasLvalueElements!Range
         && hasLength!Range
         && Offset.length >= 1);
 
 But there's another issue here. remove() has other overloads:
 
 Range remove
     (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
     (Range range, Offset offset)
     if (s == SwapStrategy.stable
         && isBidirectionalRange!Range
         && hasLvalueElements!Range
         && Offset.length >= 1)
 
 Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range)
     (Range range)
     if (isBidirectionalRange!Range
         && hasLvalueElements!Range)
 
 Two constraints are common to all three, those are the only ones that
 actually need to be in the constraint. The others can go in the body
 under `static if`, as the user need not be concerned with them.
 
 This is a general issue in Phobos that too many constraints are
 user-facing when they don't need to be.
+1. I've raised this point many times, but didn't seem to generate much response. Basically, there are two more-or-less equivalent ways of having multiple implementations of a function based on its arguments: (1) via signature constraints, and (2) via static if's inside the function (or template) body.
From an implementor's POV, (1) is more desirable, because it's easy to
add a new overload when you need to extend the function to handle new types. However, from a user's POV, (2) is much more friendly -- one only needs to look at the old overloads of std.conv.toImpl to see why: it was an obtuse mass of only slightly different sig constraints over a ridiculous number of overloads, each intended to work with very specific argument combinations, all of which are implementation details the user need not know about. Thankfully, the docs for std.conv.to have been greatly improved since the last time I had to work with the code -- everything is now consolidated under a single template function std.conv.to, and the implementation details are hidden behind module-private overloads. This is the way it should be. I argue that the same pattern should be applied to (most of the) other overloaded Phobos functions as well, such as remove() shown above. There really should only be *one* remove() function with the two constraints Walter indicated, and the current overloads should either be refactored under static if's inside the function body, or else renamed and made into module-private implementation functions called by remove(). Multiple user-facing overloads really only should be used where each overload represents a *conceptually-different* aspect of the function (which should be relatively rare). In general, if a template function (or set of overloads) conceptually does the same thing, it should be made into a *single* function. If the implementation differs, that's the problem of the Phobos maintainers, and should be handled as module-private symbols forwarded to by the single user-facing function. Another bonus to this approach is that it allows us to customize nicer error messages via static assert if none of the (internal) overloads match the passed argument types. E.g., if the user passes a swappable bidirectional range to remove() but for whatever reason the arguments fail to match any of its implementations, then instead of spewing out pages of inscrutable encrypted Klingon (argument types XYZ do not match any overloads of remove(), candidates are: [... snip 5 pages of unreadable function signatures...]), we can, say, do something like: static if (...) return removeImplA(args); else static if (...) return removeImplB(args); ... else static assert("Arguments to remove() must satisfy conditions X, Y, Z [insert explanation here]"); That's a 1-line error message that tells the user exactly what went wrong, instead of 5 pages of 10-line function signatures that the user must parse and then mentally evaluate each Boolean condition for, in order to deduce exactly why the passed arguments didn't work.
 A further improvement in the documentation would be to add links to
 isBidirectionalRange and hasLvalueElements.
This is a good idea. +1. T -- "I'm running Windows '98." "Yes." "My computer isn't working now." "Yes, you already said that." -- User-Friendly
Feb 14 2017
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/14/2017 2:01 PM, H. S. Teoh via Digitalmars-d wrote:
 This is a good idea. +1.
Please take this on! https://issues.dlang.org/show_bug.cgi?id=17183
Feb 14 2017
prev sibling parent Jack Stouffer <jack jackstouffer.com> writes:
On Tuesday, 14 February 2017 at 22:01:47 UTC, H. S. Teoh wrote:
 Thankfully, the docs for std.conv.to have been greatly improved 
 since the last time I had to work with the code -- everything 
 is now consolidated under a single template function 
 std.conv.to, and the implementation details are hidden behind 
 module-private overloads. This is the way it should be.
I'm currently trying to do this again in std.format, this is the first step: https://github.com/dlang/phobos/pull/5130
Feb 15 2017
prev sibling next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright wrote:
 A further improvement in the documentation would be to add 
 links to isBidirectionalRange and hasLvalueElements.
Kneel before your god! http://dpldocs.info/experimental-docs/std.algorithm.mutation.remove.1.html Take a look at this insane automatic cross referencing: http://dpldocs.info/experimental-docs/std.range.chain.html My ref thing still isn't perfect, but dpldocs is already streets ahead of anything dlang.org has to offer and continues to make bursts of strides.
Feb 14 2017
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2017-02-15 06:28, Adam D. Ruppe wrote:

 Kneel before your god!

 http://dpldocs.info/experimental-docs/std.algorithm.mutation.remove.1.html

 Take a look at this insane automatic cross referencing:

 http://dpldocs.info/experimental-docs/std.range.chain.html


 My ref thing still isn't perfect, but dpldocs is already streets ahead
 of anything dlang.org has to offer and continues to make bursts of strides.
Your documentation is an improvement but it doesn't help when reading the source code. -- /Jacob Carlborg
Feb 14 2017
next sibling parent reply Chris Wright <dhasenan gmail.com> writes:
On Wed, 15 Feb 2017 08:56:00 +0100, Jacob Carlborg wrote:
 Your documentation is an improvement but it doesn't help when reading
 the source code.
For me, it almost entirely obviates reading the source code. I only need to read it if I'm trying to modify it, at which point I'm already committing to spend at least thirty minutes on it. The greatest annoyance is if I have to read through several files of phobos sources just to figure out why there's no matching overload for this function call that looks right to me. This doc improvement means I only have to go to dpldocs.info (which I'm probably already at), search for the term, and click a few times. I don't have to pull out grep, peer myopically through the pages of results, and figure out which ones are the definitions of the template constraints I need.
Feb 15 2017
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 15 February 2017 at 16:20:30 UTC, Chris Wright 
wrote:
 The greatest annoyance is if I have to read through several 
 files of phobos sources just to figure out why there's no 
 matching overload for this function call that looks right to me.
I really really REALLY REALLY wish the compiler would tell you at least which part of the boolean expression failed. Then, at least, you could dig deeper with your own static asserts on those individual things or something. tbh I actually want opt-in XML error messages with obscene levels of detail. We'd pwn IDE integration with that and can really save programmers wads of time by giving them all the info they actually need. D used to promote its readable error messages as a strength over C++. We've fallen far behind in that category now. This doc improvement means I only have to go to
 dpldocs.info (which I'm probably already at), search for the 
 term, and click a few times.
indeed, I'm pretty happy with my navigation. And I'm slowly but surely fixing the automatic cross referencing. D name lookup across modules is kinda hard, this is one place dmd/ddoc would have a strong theoretical advantage since it understands the semantics and already knows the module graph. But, I'm already 80% there... and it is yielding pretty great results in practice. I'll do alias lookups next time I spend a few hours on this (tbh since it is good enough for me, it is all low priority relative to the other things I have to do, so it is moving slowly now.)
Feb 15 2017
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob Carlborg 
wrote:
 Your documentation is an improvement but it doesn't help when 
 reading the source code.
Yeah, I think there's a few things we can do in the source too. We should find the common combinations and abstract them out, like I said before, isInputRangeOf is potentially useful. Though, like Andrei, I'm skeptical on doing too much of that, since the combinations can quickly explode and then you just have to search through more to figure out wtf they mean. That'd help the source and docs if we find the right balance.
Feb 15 2017
next sibling parent reply Seb <seb wilzba.ch> writes:
On Wednesday, 15 February 2017 at 17:10:26 UTC, Adam D. Ruppe 
wrote:
 On Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob Carlborg 
 wrote:
 Your documentation is an improvement but it doesn't help when 
 reading the source code.
Yeah, I think there's a few things we can do in the source too. We should find the common combinations and abstract them out, like I said before, isInputRangeOf is potentially useful. Though, like Andrei, I'm skeptical on doing too much of that, since the combinations can quickly explode and then you just have to search through more to figure out wtf they mean. That'd help the source and docs if we find the right balance.
Speaking of right balance, there's currently a PR at Phobos that is a good candidate to get a common measure on how this balance should be set: https://github.com/dlang/phobos/pull/5132 The open question here is that for nearly every function in std.file & std.path the constraint block looks like this: uint getAttributes(R)(R name) if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && !isConvertibleToString!R); Now as this same block is used > 30x in Phobos one could argue that it makes sense to use a convenience trait like: enum isSomeInputRangeChar(R) = isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && !isConvertibleToString!R which would obviously lead to: uint getAttributes(R)(R name) if (isSomeInputRangeChar!R) What's your opinion on such a case?
Feb 15 2017
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 15 February 2017 at 17:18:15 UTC, Seb wrote:
 Now as this same block is used > 30x in Phobos
That tells me you already have an empirical clear win! If you wrote exactly the same thing 30x inside the functions, you'd move it out to a new function too.
 if (isSomeInputRangeChar!R)

 What's your opinion on such a case?
Yeah, I'd say do it, and it is similar to isSomeString in use elsewhere.
Feb 15 2017
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 02/15/2017 12:18 PM, Seb wrote:
 uint getAttributes(R)(R name)
 if (isInputRange!R && !isInfinite!R &&
 isSomeChar!(ElementEncodingType!R) && !isConvertibleToString!R);


 Now as this same block is used > 30x in Phobos one could argue that it
 makes sense to use a convenience trait like:
isStringLike. I wanted to add this for a while already. Please do! -- Andrei
Feb 15 2017
next sibling parent Seb <seb wilzba.ch> writes:
On Wednesday, 15 February 2017 at 18:51:40 UTC, Andrei 
Alexandrescu wrote:
 On 02/15/2017 12:18 PM, Seb wrote:
 uint getAttributes(R)(R name)
 if (isInputRange!R && !isInfinite!R &&
 isSomeChar!(ElementEncodingType!R) && 
 !isConvertibleToString!R);


 Now as this same block is used > 30x in Phobos one could argue 
 that it
 makes sense to use a convenience trait like:
isStringLike. I wanted to add this for a while already. Please do! -- Andrei
https://github.com/dlang/phobos/pull/5137
Feb 15 2017
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/15/2017 10:51 AM, Andrei Alexandrescu wrote:
 isStringLike. I wanted to add this for a while already. Please do! -- Andrei
What I've found messy and confusing with string overloads in Phobos is there are at least 6 kinds of strings: 1. auto decoding dynamic arrays 2. not auto decoding arrays 3. static arrays 4. aggregates with an 'alias this' to a string 5. ranges of characters 6. something convertible to a string These classifications seem to be tested for in a unique ad-hoc manner in every case. I'd like to take a step back and devise a consistent taxonomy of these things, based on how Phobos uses them, before adding more names.
Feb 15 2017
next sibling parent Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Wednesday, February 15, 2017 12:54:02 Walter Bright via Digitalmars-d 
wrote:
 On 2/15/2017 10:51 AM, Andrei Alexandrescu wrote:
 isStringLike. I wanted to add this for a while already. Please do! --
 Andrei
What I've found messy and confusing with string overloads in Phobos is there are at least 6 kinds of strings: 1. auto decoding dynamic arrays 2. not auto decoding arrays 3. static arrays 4. aggregates with an 'alias this' to a string 5. ranges of characters 6. something convertible to a string These classifications seem to be tested for in a unique ad-hoc manner in every case. I'd like to take a step back and devise a consistent taxonomy of these things, based on how Phobos uses them, before adding more names.
Yeah. It's a bit of a mess. And types with alias this and enums _really_ don't help things. - Jonathan M Davis
Feb 15 2017
prev sibling parent reply Jack Stouffer <jack jackstouffer.com> writes:
On Wednesday, 15 February 2017 at 20:54:02 UTC, Walter Bright 
wrote:
 I'd like to take a step back and devise a consistent taxonomy 
 of these things
Ok
 1. auto decoding dynamic arrays
Narrow strings
 2. not auto decoding arrays
Wide strings
 3. static arrays
Do these need to be called anything other than "static arrays"? Also, they're not ranges, so they're usually tested with isStaticArray
 4. aggregates with an 'alias this' to a string
isConvertibleToString
 5. ranges of characters
Character range
 6. something convertible to a string
Same as 4
Feb 15 2017
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/15/2017 1:03 PM, Jack Stouffer wrote:
 On Wednesday, 15 February 2017 at 20:54:02 UTC, Walter Bright wrote:
 I'd like to take a step back and devise a consistent taxonomy of these things
Ok
 1. auto decoding dynamic arrays
Narrow strings
 2. not auto decoding arrays
Wide strings
 3. static arrays
Do these need to be called anything other than "static arrays"? Also, they're not ranges, so they're usually tested with isStaticArray
 4. aggregates with an 'alias this' to a string
isConvertibleToString
 5. ranges of characters
Character range
 6. something convertible to a string
Same as 4
That's a good start. A test of that is to look at Phobos' actual usage of constraints and see if they fit in.
Feb 15 2017
prev sibling next sibling parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Wednesday, February 15, 2017 21:03:46 Jack Stouffer via Digitalmars-d 
wrote:
 On Wednesday, 15 February 2017 at 20:54:02 UTC, Walter Bright

 wrote:
 I'd like to take a step back and devise a consistent taxonomy
 of these things
Ok
 1. auto decoding dynamic arrays
Narrow strings
 2. not auto decoding arrays
Wide strings
 3. static arrays
Do these need to be called anything other than "static arrays"? Also, they're not ranges, so they're usually tested with isStaticArray
 4. aggregates with an 'alias this' to a string
isConvertibleToString
 5. ranges of characters
Character range
 6. something convertible to a string
Same as 4
Except that you're forgetting enums. Also, there's this mess: enum bool isNarrowString(T) = (is(T : const char[]) || is(T : const wchar[])) && !isAggregateType!T && !isStaticArray!T; enum bool isAutodecodableString(T) = (is(T : const char[]) || is(T : const wchar[])) && !isStaticArray!T; A type with alias this passes isAutodecodableString but not isNarrowString, making for really subtle difference. Also, enums of strings pass both, which is potentially a problem as they really should be treated the same as types with alias this given how they need to be used in a templated function. enums also pass isSomeString, which makes using isSomeString a no-go for any range-based function if it doesn't then test for enums - but aggregate types _don't_ pass isSomeString. And then there's template isConvertibleToString(T) { enum isConvertibleToString = (isAggregateType!T || isStaticArray!T || is(T == enum)) && is(StringTypeOf!T); } So, regardless of the exact terminology, we have a whole set of very similar but subtly different traits. And as it stands, they _will_ get screwed up unless someone is carefully looking at each to make sure that they actually use the right one as well as testing with various types that frequently get missed in unit tests - like types which use alias this or enums with a base type of string. - Jonathan M Davis
Feb 15 2017
parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/15/2017 1:24 PM, Jonathan M Davis via Digitalmars-d wrote:
 So, regardless of the exact terminology, we have a whole set of very similar
 but subtly different traits. And as it stands, they _will_ get screwed up
 unless someone is carefully looking at each to make sure that they actually
 use the right one as well as testing with various types that frequently get
 missed in unit tests - like types which use alias this or enums with a base
 type of string.
I suspect the only way forward is to go through Phobos and collect all the plethora of constraints used for strings, and examine them for commonalities. Not doing this will just result in more accumulation of confusing junk.
Feb 15 2017
prev sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Wed, Feb 15, 2017 at 09:03:46PM +0000, Jack Stouffer via Digitalmars-d wrote:
 On Wednesday, 15 February 2017 at 20:54:02 UTC, Walter Bright wrote:
 I'd like to take a step back and devise a consistent taxonomy of these
 things
Ok
 1. auto decoding dynamic arrays
Narrow strings
 2. not auto decoding arrays
Wide strings
 3. static arrays
Do these need to be called anything other than "static arrays"? Also, they're not ranges, so they're usually tested with isStaticArray
 4. aggregates with an 'alias this' to a string
isConvertibleToString
 5. ranges of characters
Character range
 6. something convertible to a string
Same as 4
This describes the current state of Phobos, but I think what Walter is driving at is, does it *have* to be this way? I think we can (and should) simplify this taxonomy to avoid needless duplication and also create a more consistent conceptual model of how Phobos deals with strings and string-like things. First of all, I think we should try out best to avoid treating arrays and character ranges differently. I know this is inevitable because we're still in a state where autodecoding can't be fully eliminated yet, but as far as possible, I think we should try to treat them the same way. (1) Functions that need to work with string contents (e.g., find, toUpperCase, etc.) should in general accept any input ranges whose elements are convertible in some way to dchar. Some of these functions may require forward ranges or bidirectional / random-access ranges. Most of these functions can allow infinite ranges (so we only need the occasional !isInfinite check). (2) Functions that don't need to work with string contents, i.e., the strings are treated as opaque blobs to be passed to, say, an underlying OS function, require finite ranges, but should be able to work with any type that converts to string in one form or another. So anything from (finite) ranges of char-like elements to aggregates with alias this to string ought to be accepted. They can be internally converted to strings if necessary (e.g., to pass to an OS call). I suspect that many of the functions in category (1) can be coalesced with generic range algorithms, so they should not even be exposing their sig constraints in the public API. Instead, they should take a generic range and then use static if or module-private helper functions to dispatch to the right implementation(s). Category (2) functions can internally call helpers (maybe in std.string or std.array) that convert any incoming type that can be converted to string in some way. As for static arrays, I think the consensus is (was?) that they are not ranges, and so the user is required to take a slice before handing it to a Phobos function expecting string or string-like arguments. Well, actually, not just string-like things, but anything to do with ranges. I don't think there's a need to treat static arrays of char separately from static arrays in general. T -- Bare foot: (n.) A device for locating thumb tacks on the floor.
Feb 15 2017
parent Walter Bright <newshound2 digitalmars.com> writes:
Also, as mentioned in the std.algorithm.mutation.remove case, constraints in 
Phobos often confuse "requirements" with "specializations".

Requirements should be user-facing constraints, while specializations are 
implementation details better handled with internal static if.
Feb 15 2017
prev sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 15 February 2017 at 17:10:26 UTC, Adam D. Ruppe 
wrote:
 On Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob Carlborg 
 wrote:
 Your documentation is an improvement but it doesn't help when 
 reading the source code.
Yeah, I think there's a few things we can do in the source too. We should find the common combinations and abstract them out, like I said before, isInputRangeOf is potentially useful. Though, like Andrei, I'm skeptical on doing too much of that, since the combinations can quickly explode and then you just have to search through more to figure out wtf they mean. That'd help the source and docs if we find the right balance.
I submitted a Phobos PR for `isInputRangeOf`. It was nuked from orbit. I understand why: as you mention, the combinatorial explosion of names is an issue. But: it's a mess rangeifying code when this: void fun(Foo[] foos); // yay! readable becomes: void fun(R)(R foos) if(isInputRange!R && is(Unqual!(ElementType!R) == Foo); Ugh. Atila
Feb 16 2017
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/16/17 7:11 AM, Atila Neves wrote:
 On Wednesday, 15 February 2017 at 17:10:26 UTC, Adam D. Ruppe wrote:
 On Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob Carlborg wrote:
 Your documentation is an improvement but it doesn't help when reading
 the source code.
Yeah, I think there's a few things we can do in the source too. We should find the common combinations and abstract them out, like I said before, isInputRangeOf is potentially useful. Though, like Andrei, I'm skeptical on doing too much of that, since the combinations can quickly explode and then you just have to search through more to figure out wtf they mean. That'd help the source and docs if we find the right balance.
I submitted a Phobos PR for `isInputRangeOf`. It was nuked from orbit. I understand why: as you mention, the combinatorial explosion of names is an issue. But: it's a mess rangeifying code when this: void fun(Foo[] foos); // yay! readable becomes: void fun(R)(R foos) if(isInputRange!R && is(Unqual!(ElementType!R) == Foo);
If you find a number of these, that would be good evidence. -- Andrei
Feb 16 2017
parent reply Atila Neves <atila.neves gmail.com> writes:
On Thursday, 16 February 2017 at 13:21:04 UTC, Andrei 
Alexandrescu wrote:
 On 2/16/17 7:11 AM, Atila Neves wrote:
 On Wednesday, 15 February 2017 at 17:10:26 UTC, Adam D. Ruppe 
 wrote:
 On Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob 
 Carlborg wrote:
 Your documentation is an improvement but it doesn't help 
 when reading
 the source code.
Yeah, I think there's a few things we can do in the source too. We should find the common combinations and abstract them out, like I said before, isInputRangeOf is potentially useful. Though, like Andrei, I'm skeptical on doing too much of that, since the combinations can quickly explode and then you just have to search through more to figure out wtf they mean. That'd help the source and docs if we find the right balance.
I submitted a Phobos PR for `isInputRangeOf`. It was nuked from orbit. I understand why: as you mention, the combinatorial explosion of names is an issue. But: it's a mess rangeifying code when this: void fun(Foo[] foos); // yay! readable becomes: void fun(R)(R foos) if(isInputRange!R && is(Unqual!(ElementType!R) == Foo);
If you find a number of these, that would be good evidence. -- Andrei
I found just over 70 similar lines, which is more than I expected given how generic Phobos is. The real issue is in client code though. I write less range-based code than I'd like, because it's a lot easier to make it a regular array and change it later if the function needs to be called with other ranges. Atila
Feb 22 2017
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 02/22/2017 06:53 AM, Atila Neves wrote:
 On Thursday, 16 February 2017 at 13:21:04 UTC, Andrei Alexandrescu wrote:
 On 2/16/17 7:11 AM, Atila Neves wrote:
 On Wednesday, 15 February 2017 at 17:10:26 UTC, Adam D. Ruppe wrote:
 On Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob Carlborg wrote:
 Your documentation is an improvement but it doesn't help when reading
 the source code.
Yeah, I think there's a few things we can do in the source too. We should find the common combinations and abstract them out, like I said before, isInputRangeOf is potentially useful. Though, like Andrei, I'm skeptical on doing too much of that, since the combinations can quickly explode and then you just have to search through more to figure out wtf they mean. That'd help the source and docs if we find the right balance.
I submitted a Phobos PR for `isInputRangeOf`. It was nuked from orbit. I understand why: as you mention, the combinatorial explosion of names is an issue. But: it's a mess rangeifying code when this: void fun(Foo[] foos); // yay! readable becomes: void fun(R)(R foos) if(isInputRange!R && is(Unqual!(ElementType!R) == Foo);
If you find a number of these, that would be good evidence. -- Andrei
I found just over 70 similar lines, which is more than I expected given how generic Phobos is. The real issue is in client code though. I write less range-based code than I'd like, because it's a lot easier to make it a regular array and change it later if the function needs to be called with other ranges.
If you submit a pull request that simplifies >70 constraints in Phobos and implicitly promises to simplify client-level constraints as much, that would have a good chance to be accepted. Destroy! -- Andrei
Feb 22 2017
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/14/2017 9:28 PM, Adam D. Ruppe wrote:
 On Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright wrote:
 A further improvement in the documentation would be to add links to
 isBidirectionalRange and hasLvalueElements.
Kneel before your god! http://dpldocs.info/experimental-docs/std.algorithm.mutation.remove.1.html Take a look at this insane automatic cross referencing: http://dpldocs.info/experimental-docs/std.range.chain.html My ref thing still isn't perfect, but dpldocs is already streets ahead of anything dlang.org has to offer and continues to make bursts of strides.
That looks pretty sweet!
Feb 15 2017
parent reply Jacob Carlborg <doob me.com> writes:
On 2017-02-15 09:19, Walter Bright wrote:
 On 2/14/2017 9:28 PM, Adam D. Ruppe wrote:
 On Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright wrote:
 A further improvement in the documentation would be to add links to
 isBidirectionalRange and hasLvalueElements.
Kneel before your god! http://dpldocs.info/experimental-docs/std.algorithm.mutation.remove.1.html Take a look at this insane automatic cross referencing: http://dpldocs.info/experimental-docs/std.range.chain.html My ref thing still isn't perfect, but dpldocs is already streets ahead of anything dlang.org has to offer and continues to make bursts of strides.
That looks pretty sweet!
Doesn't help when reading the source code :(. -- /Jacob Carlborg
Feb 15 2017
parent reply Daniel N <no public.email> writes:
On Wednesday, 15 February 2017 at 08:59:49 UTC, Jacob Carlborg 
wrote:
 On 2017-02-15 09:19, Walter Bright wrote:
 On 2/14/2017 9:28 PM, Adam D. Ruppe wrote:
 On Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright 
 wrote:
 A further improvement in the documentation would be to add 
 links to
 isBidirectionalRange and hasLvalueElements.
Kneel before your god! http://dpldocs.info/experimental-docs/std.algorithm.mutation.remove.1.html Take a look at this insane automatic cross referencing: http://dpldocs.info/experimental-docs/std.range.chain.html My ref thing still isn't perfect, but dpldocs is already streets ahead of anything dlang.org has to offer and continues to make bursts of strides.
That looks pretty sweet!
Doesn't help when reading the source code :(.
Something akin to this would improve readability at least for me, also more DRY. isInputRange!(InputRange1) && isInputRange!(InputRange2) && hasSwappableElements!(InputRange1) && hasSwappableElements!(InputRange2) => every!(InputRange1, InputRange2). satisfies!(isInputRange, hasSwappableElements); template every(T...) { template satisfies(U...) { enum satisfies = true; } }
Feb 15 2017
parent reply Daniel N <no public.email> writes:
On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:
 template every(T...)
 {
 	template satisfies(U...)
 	{
 		enum satisfies = true;
 	}
 }
(lunch-break => time to hack D!) template every(T...) { template satisfies(U...) { enum satisfies = { foreach(t; T) foreach(u; U) if(!u!t) return false; return true; }(); } }
Feb 15 2017
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 02/15/2017 06:20 AM, Daniel N wrote:
 On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:
 template every(T...)
 {
     template satisfies(U...)
     {
         enum satisfies = true;
     }
 }
(lunch-break => time to hack D!) template every(T...) { template satisfies(U...) { enum satisfies = { foreach(t; T) foreach(u; U) if(!u!t) return false; return true; }(); } }
That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andrei
Feb 15 2017
next sibling parent reply Timothee Cour via Digitalmars-d <digitalmars-d puremagic.com> writes:
This thread completely diverged from the original post, which was propsing
`::` instead of `from!`:

```
void fun(T)(std.stdio::File input, T value) if (std.traits::isIntegral!T)
{...}
```

instead of:

```
void fun(T)(Module!"std.stdio".File input, T value) if
(Module!"std.traits".isIntegral!T)
{...}
```

I see it as a clear improvment in readability (see original post for
details along with shortcomings of `from!` approach )


On Wed, Feb 15, 2017 at 11:39 AM, Andrei Alexandrescu via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 On 02/15/2017 06:20 AM, Daniel N wrote:

 On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:

 template every(T...)
 {
     template satisfies(U...)
     {
         enum satisfies = true;
     }
 }
(lunch-break => time to hack D!) template every(T...) { template satisfies(U...) { enum satisfies = { foreach(t; T) foreach(u; U) if(!u!t) return false; return true; }(); } }
That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andrei
Feb 15 2017
next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Wednesday, 15 February 2017 at 20:09:46 UTC, Timothee Cour 
wrote:
 This thread completely diverged from the original post, which 
 was propsing `::` instead of `from!`:

 ```
 void fun(T)(std.stdio::File input, T value) if 
 (std.traits::isIntegral!T)
 {...}
 ```

 instead of:

 ```
 void fun(T)(Module!"std.stdio".File input, T value) if
 (Module!"std.traits".isIntegral!T)
 {...}
 ```

 I see it as a clear improvment in readability (see original 
 post for details along with shortcomings of `from!` approach )
I really think allowing `with (module_!"std.foo")` before declarations is a better solution from a DRY perspective, plus it works for UFCS. This is a more general change that has other benefits that apply to any static aggregate e.g. enum names - `with(Enum)`, not just imports.
Feb 15 2017
parent reply timotheecour <timothee.cour2 gmail.com> writes:
On Wednesday, 15 February 2017 at 20:21:45 UTC, Nick Treleaven 
wrote:
 On Wednesday, 15 February 2017 at 20:09:46 UTC, Timothee Cour 
 wrote:
 This thread completely diverged from the original post, which 
 was propsing `::` instead of `from!`:

 ```
 void fun(T)(std.stdio::File input, T value) if 
 (std.traits::isIntegral!T)
 {...}
 ```

 instead of:

 ```
 void fun(T)(Module!"std.stdio".File input, T value) if
 (Module!"std.traits".isIntegral!T)
 {...}
 ```

 I see it as a clear improvment in readability (see original 
 post for details along with shortcomings of `from!` approach )
I really think allowing `with (module_!"std.foo")` before declarations is a better solution from a DRY perspective, plus it works for UFCS. This is a more general change that has other benefits that apply to any static aggregate e.g. enum names - `with(Enum)`, not just imports.
both are useful: * std.stdio::File is nice for 1-off symbols only used 1 or a few times; simple syntax and doesn't add indentation / nesting * with(module_!"std.foo") is useful for scoping imports to cover several declarations and being DRY; at the expense of adding indentation/nesting and less nice syntax
Feb 16 2017
parent reply Nick Treleaven <nick geany.org> writes:
On Friday, 17 February 2017 at 04:09:14 UTC, timotheecour wrote:
 * with(module_!"std.foo") is useful for scoping imports to 
 cover several declarations and being DRY; at the expense of 
 adding indentation/nesting and less nice syntax
Doesn't add indentation: with (module_!"std.stdio, std.traits") void fun(T)(File input, T value) if (isIntegral!T); with/module_ solves the UFCS member stand-in problem elegantly, how does your proposal solve it?: with (module_!"std.range.primitives") void func(alias pred, R)(R range) if (isInputRange!R && is(typeof(pred(range.front)) == bool); .front has to refer to *either* a member or an imported UFCS function.
Feb 17 2017
parent reply Timothee Cour via Digitalmars-d <digitalmars-d puremagic.com> writes:
 Doesn't add indentation:

 with (module_!"std.stdio, std.traits")
 void fun(T)(File input, T value)
 if (isIntegral!T);
* what is the implementation of `module_` ? `from` defined earlier doesn't allow for multiple modules as in from!"std.stdio, std.traits"`. But let's assume it can be done for now. * when `with` covers multiple declarations, it adds indentation: with(module_!"std.stdio"){ declaration1; declaration2; } * when `with` covers a single one, it doesn't add indentation, but then `::` can be used instead, with arguably simpler syntax: `void fun(T)(std.stdio::File input, T value);` vs `with (module_!"std.stdio") void fun(T)(File input, T value);`
 with/module_ solves the UFCS member stand-in problem elegantly, how does your
proposal solve it?:
 with (module_!"std.range.primitives")
 void func(alias pred, R)(R range)
 if (isInputRange!R && is(typeof(pred(range.front)) == bool);

 .front has to refer to *either* a member or an imported UFCS function.
UFCS equally affects the `::` proposal and the `module_!""`-without-`with` proposal. Besides the option of not using UFCS, we can actually use Alias for this, as I've proposed a while ago in http://forum.dlang.org/post/mailman.1002.1370829729.13711.digitalmars-d-learn puremagic.com: `with (module_!"std.range.primitives") ... pred(range.front)` vs: `... pred(range.Alias!(std.range.primitives::front))` or, if a feature that I've proposed earlier in http://forum.dlang.org/post/mailman.1453.1369099708.4724.digitalmars-d puremagic.com (support UFCS with fully qualified function names) is accepted, it becomes even simpler: `... pred(range.(std.range.primitives::front))` To reiterate what I said earlier: * `foo.bar::symol` is very well suited for the common case of 1-off imports, ie when the import is only needed at a particular location. This is a common case. * If we want to cover multiple declarations, using something like `with (module_!"std.stdio, std.traits")` makes sense and is more DRY.
Feb 18 2017
parent Nick Treleaven <nick geany.org> writes:
On Sunday, 19 February 2017 at 01:53:58 UTC, Timothee Cour wrote:
 Doesn't add indentation:

 with (module_!"std.stdio, std.traits")
 void fun(T)(File input, T value)
 if (isIntegral!T);
* what is the implementation of `module_` ? `from` defined earlier doesn't allow for multiple modules as in from!"std.stdio, std.traits"`. But let's assume it can be done for now.
I don't know how to implement it. Possibly with multiple alias this.
 * when `with` covers multiple declarations, it adds indentation:
 with(module_!"std.stdio"){
   declaration1;
   declaration2;
 }

 * when `with` covers a single one, it doesn't add indentation, 
 but then `::` can be used instead, with arguably simpler syntax:

 `void fun(T)(std.stdio::File input, T value);`
 vs
 `with (module_!"std.stdio") void fun(T)(File input, T value);`
What about when the 2nd argument uses File as well? That's the violation of DRY I meant.
 with/module_ solves the UFCS member stand-in problem 
 elegantly, how does your proposal solve it?:
 with (module_!"std.range.primitives")
 void func(alias pred, R)(R range)
 if (isInputRange!R && is(typeof(pred(range.front)) == bool);

 .front has to refer to *either* a member or an imported UFCS 
 function.
UFCS equally affects the `::` proposal and the `module_!""`-without-`with` proposal. Besides the option of not using UFCS, we can actually use Alias for this, as I've proposed a while ago in http://forum.dlang.org/post/mailman.1002.1370829729.13711.digitalmars-d-learn puremagic.com: `with (module_!"std.range.primitives") ... pred(range.front)` vs: `... pred(range.Alias!(std.range.primitives::front))` or, if a feature that I've proposed earlier in http://forum.dlang.org/post/mailman.1453.1369099708.4724.digitalmars-d puremagic.com (support UFCS with fully qualified function names) is accepted, it becomes even simpler: `... pred(range.(std.range.primitives::front))`
No, then front only refers to the std.range one, not R.front if it exists. The with() solution is like a lazy scoped import, affecting overloading, whereas using just from! or mod:: forces a lookup in that module scope only.
Feb 21 2017
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
Please fix your newsreader so it submits postings in text format, not html.
Feb 15 2017
prev sibling next sibling parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Wednesday, 15 February 2017 at 19:39:52 UTC, Andrei 
Alexandrescu wrote:
 On 02/15/2017 06:20 AM, Daniel N wrote:
 On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:
 template every(T...)
 {
     template satisfies(U...)
     {
         enum satisfies = true;
     }
 }
(lunch-break => time to hack D!) template every(T...) { template satisfies(U...) { enum satisfies = { foreach(t; T) foreach(u; U) if(!u!t) return false; return true; }(); } }
That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andrei
Slightly OT, but it is related so bear with me: Design by introspection is great and prevent having to make new names for every possible combo of features. Unfortunately, it doesn't work so well for introspecting multiple types at once: normally you have to then make a new symbol, with a new name, to pass to e.g. allSatisfy. That annoys me, so I've been wanting this sort of thing for a while (which would also make `every` way better to use, instead of having to make a name for every predicate: You know how we have named parameterised enums and aliases? enum isInt(a) = is(a == int); and alias sizeof(a) = a.sizeof; why not allow *anonymous* parameterised enums and aliases, like this: enum areAllInt(TL ...) = allSatisfy!(enum(a) => is(a == int), TL); void foo(TL...)(TL args) if (allSatisfy!(enum(T) => T.sizeof <= 4, TL)) { // ... } an example from phobos: bool ordered(alias less = "a < b", T...)(T values) if (T.length == 2 && is(typeof(binaryFun!less(values[1], values[0])) : bool) || T.length > 2 && is(typeof(ordered!less(values[0..1 + $ / 2]))) && is(typeof(ordered!less(values[$ / 2..$])))) becomes this: bool ordered(alias less = "a < b", T...)(T values) if (T.length > 2 && allSatisfy!(enum(size_t i) => is(typeof(binaryFun!less(values[i], values[i+1])) : bool), Iota!(T.length - 1)) I admit it's not a huge win, but hiding the log_2(N) template depth behind allSatisfy is great: you shouldn't have to (and people wont) think about that when writing a template constraint like this. There is a part of me that thinks all this meta-template stuff is madness though and that modifications to ctfe could make it mostly obsolete, but that's a story for later...
Feb 15 2017
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2017-02-16 01:40, John Colvin wrote:

 Slightly OT, but it is related so bear with me:

 Design by introspection is great and prevent having to make new names
 for every possible combo of features. Unfortunately, it doesn't work so
 well for introspecting multiple types at once: normally you have to then
 make a new symbol, with a new name, to pass to e.g. allSatisfy. That
 annoys me, so I've been wanting this sort of thing for a while (which
 would also make `every` way better to use, instead of having to make a
 name for every predicate:

 You know how we have named parameterised enums and aliases?
 enum isInt(a) = is(a == int);
 and
 alias sizeof(a) = a.sizeof;

 why not allow *anonymous* parameterised enums and aliases, like this:

 enum areAllInt(TL ...) = allSatisfy!(enum(a) => is(a == int), TL);

 void foo(TL...)(TL args)
 if (allSatisfy!(enum(T) => T.sizeof <= 4, TL))
 {
     // ...
 }
I've been thinking something similar. But this is the way I see it: A lambda is an anonymous template function which is not yet instantiated. But with the lambda syntax only one the different template parameters are allowed. Why not generalize the lambda syntax to allow different kind of template parameters. Example: What we have today: [1, 2, 3].map!(e => e * 2); Is basically the same as: T __anonymous(T)(T e) { return e * 2; } [1, 2, 3].map!(__anonymous!(int)); But today we cannot represent the following template with a lambda: size_t sizeof(alias a)() { return a.sizeof; } So why not allow this lambda syntax: alias sizeof = (alias a) => a.sizeof; It would be nice if we could reuse the existing syntax for regular template parameters as well: alias isInt = t => is(t == int); auto b = isInt!(int); If the above is not possible, we could add an additional parameter list to lambdas: alias isInt = (t)() => is(t == int); -- /Jacob Carlborg
Feb 15 2017
parent ZombineDev <petar.p.kirov gmail.com> writes:
On Thursday, 16 February 2017 at 07:48:37 UTC, Jacob Carlborg 
wrote:
 On 2017-02-16 01:40, John Colvin wrote:

 Slightly OT, but it is related so bear with me:

 Design by introspection is great and prevent having to make 
 new names
 for every possible combo of features. Unfortunately, it 
 doesn't work so
 well for introspecting multiple types at once: normally you 
 have to then
 make a new symbol, with a new name, to pass to e.g. 
 allSatisfy. That
 annoys me, so I've been wanting this sort of thing for a while 
 (which
 would also make `every` way better to use, instead of having 
 to make a
 name for every predicate:

 You know how we have named parameterised enums and aliases?
 enum isInt(a) = is(a == int);
 and
 alias sizeof(a) = a.sizeof;

 why not allow *anonymous* parameterised enums and aliases, 
 like this:

 enum areAllInt(TL ...) = allSatisfy!(enum(a) => is(a == int), 
 TL);

 void foo(TL...)(TL args)
 if (allSatisfy!(enum(T) => T.sizeof <= 4, TL))
 {
     // ...
 }
I've been thinking something similar. But this is the way I see it: A lambda is an anonymous template function which is not yet instantiated. But with the lambda syntax only one the different template parameters are allowed. Why not generalize the lambda syntax to allow different kind of template parameters. Example: What we have today: [1, 2, 3].map!(e => e * 2); Is basically the same as: T __anonymous(T)(T e) { return e * 2; } [1, 2, 3].map!(__anonymous!(int)); But today we cannot represent the following template with a lambda: size_t sizeof(alias a)() { return a.sizeof; } So why not allow this lambda syntax: alias sizeof = (alias a) => a.sizeof; It would be nice if we could reuse the existing syntax for regular template parameters as well: alias isInt = t => is(t == int); auto b = isInt!(int); If the above is not possible, we could add an additional parameter list to lambdas: alias isInt = (t)() => is(t == int);
You can sort of do this even today. All you need is struct with template-ed opCall. See https://github.com/ZombineDev/rxd/blob/v0.0.3/source/rxd/meta2/lambda.d#L12 and https://github.com/ZombineDev/rxd/blob/v0.0.3/source/rxd/xf/xform.d#L54 This one of the few (only?) places that C++14 is better than D, because in C++14 polymorphic lamdas are first-class values, whereas in D alias function literals are templates (i.e. you can refer to them only by alias, can't assign them to enum or auto constants / variables).
Feb 16 2017
prev sibling parent ZombineDev <petar.p.kirov gmail.com> writes:
On Thursday, 16 February 2017 at 00:40:19 UTC, John Colvin wrote:
 On Wednesday, 15 February 2017 at 19:39:52 UTC, Andrei 
 Alexandrescu wrote:
 On 02/15/2017 06:20 AM, Daniel N wrote:
 On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N 
 wrote:
 template every(T...)
 {
     template satisfies(U...)
     {
         enum satisfies = true;
     }
 }
(lunch-break => time to hack D!) template every(T...) { template satisfies(U...) { enum satisfies = { foreach(t; T) foreach(u; U) if(!u!t) return false; return true; }(); } }
That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andrei
Slightly OT, but it is related so bear with me: Design by introspection is great and prevent having to make new names for every possible combo of features. Unfortunately, it doesn't work so well for introspecting multiple types at once: normally you have to then make a new symbol, with a new name, to pass to e.g. allSatisfy. That annoys me, so I've been wanting this sort of thing for a while (which would also make `every` way better to use, instead of having to make a name for every predicate: You know how we have named parameterised enums and aliases? enum isInt(a) = is(a == int); and alias sizeof(a) = a.sizeof; why not allow *anonymous* parameterised enums and aliases, like this: enum areAllInt(TL ...) = allSatisfy!(enum(a) => is(a == int), TL); void foo(TL...)(TL args) if (allSatisfy!(enum(T) => T.sizeof <= 4, TL)) { // ... } [snip]
I have a library that allows just that, but with even shorter syntax :P https://github.com/ZombineDev/rxd/blob/v0.0.3/source/rxd/xf/xform.d#L50 https://github.com/ZombineDev/rxd/blob/v0.0.3/source/rxd/meta2/type.d#L241 Though I have feeling that it will make uplinkCoder cringe, because of the pressure it is probably putting on the compiler :D
Feb 16 2017
prev sibling parent Daniel Nielsen <daniel.nielsen.se gmail.com> writes:
On Wednesday, 15 February 2017 at 19:39:52 UTC, Andrei 
Alexandrescu wrote:
 On 02/15/2017 06:20 AM, Daniel N wrote:
 On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:
 template every(T...)
 {
     template satisfies(U...)
     {
         enum satisfies = true;
     }
 }
(lunch-break => time to hack D!) template every(T...) { template satisfies(U...) { enum satisfies = { foreach(t; T) foreach(u; U) if(!u!t) return false; return true; }(); } }
That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andrei
Thanks, I'll get back to you. Unfortunately I just contracted fever, not fit to leave bed. Nothing dangerous, just am K.O.
Feb 16 2017
prev sibling parent Chris Wright <dhasenan gmail.com> writes:
On Wed, 15 Feb 2017 05:28:11 +0000, Adam D. Ruppe wrote:

 On Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright wrote:
 A further improvement in the documentation would be to add links to
 isBidirectionalRange and hasLvalueElements.
Kneel before your god!
We're not worthy!
Feb 15 2017
prev sibling next sibling parent Dominikus Dittes Scherkl <Dominikus.Scherkl continental-corporation.com> writes:
On Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright wrote:
 Range remove
[...]
 But there's another issue here. remove() has other overloads:
[...]
 Two constraints are common to all three, those are the only 
 ones that actually need to be in the constraint. The others can 
 go in the body under `static if`, as the user need not be 
 concerned with them.

 This is a general issue in Phobos that too many constraints are 
 user-facing when they don't need to be.
This! Getting rid of overloads and at the same time simplify the constraints by moving them in the function with static if! That would really improve the function signatures!
Feb 15 2017
prev sibling parent Martin Nowak <code dawg.eu> writes:
On Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright wrote:
 On 2/14/2017 8:25 AM, Andrei Alexandrescu wrote:
 Range remove
 (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
 (Range range, Offset offset)
 if (s != SwapStrategy.stable
     && isBidirectionalRange!Range
     && hasLvalueElements!Range
     && hasLength!Range
     && Offset.length >= 1);

 The function name is on the first line.
It could be improved slightly using indentation: Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if (s != SwapStrategy.stable && isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1); But there's another issue here. remove() has other overloads: Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if (s == SwapStrategy.stable && isBidirectionalRange!Range && hasLvalueElements!Range && Offset.length >= 1) Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range) (Range range) if (isBidirectionalRange!Range && hasLvalueElements!Range) Two constraints are common to all three, those are the only ones that actually need to be in the constraint. The others can go in the body under `static if`, as the user need not be concerned with them.
Turns out that this advice is somewhat problematic. When the constraints are too general, it might easily attempt to hijack other calls, leading to conflicts between unrelated functions of the same name. See https://github.com/dlang/phobos/pull/5149 which introduced a conflict between std.algorithm.copy and std.file.copy for (string, string) arguments. Still your point is valid, separating different overloads using constraints unnecessarily complicates documentation.
May 23 2017
prev sibling next sibling parent reply Lurker <lurker gmail.com> writes:
On Tuesday, 14 February 2017 at 16:25:17 UTC, Andrei Alexandrescu 
wrote:
 Range remove
 (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
 (Range range, Offset offset)
 if (s != SwapStrategy.stable
     && isBidirectionalRange!Range
     && hasLvalueElements!Range
     && hasLength!Range
     && Offset.length >= 1);
Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if CanRemove!(s, range, offset);
 The function name is on the first line. I think 10 seconds 
 would be an exaggeration of an admittedly real issue.
Thought so, too, but then I *did* spend 10 seconds trying to find it myself!
 Tuple!(InputRange1, InputRange2)
 swapRanges(InputRange1, InputRange2)(InputRange1 r1, 
 InputRange2 r2)
 if (isInputRange!(InputRange1) && isInputRange!(InputRange2)
     && hasSwappableElements!(InputRange1)
     && hasSwappableElements!(InputRange2)
     && is(ElementType!(InputRange1) == 
 ElementType!(InputRange2)));
Tuple!(InputRange1, InputRange2) swapRanges(InputRange1, InputRange2)(InputRange1 r1, InputRange2 r2) if CanSwapRanges!(InputRange1, InputRange2);
Feb 14 2017
parent Chris Wright <dhasenan gmail.com> writes:
On Tue, 14 Feb 2017 21:33:00 +0000, Lurker wrote:

 On Tuesday, 14 February 2017 at 16:25:17 UTC, Andrei Alexandrescu wrote:
 Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
 (Range range, Offset offset)
 if (s != SwapStrategy.stable
     && isBidirectionalRange!Range && hasLvalueElements!Range &&
     hasLength!Range && Offset.length >= 1);
Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if CanRemove!(s, range, offset);
Which adds another layer to determine what sort of arguments the thing requires. Fewer layers is better. In the past, I tried to track down compilation errors in Phobos (due to my changes) relating to, I think, template overload selection in std.conv based on template constraints. It was hell. Multiple layers with `static if (__traits(compiles))` interspersed. I'm not too keen on template constraints in general. If you need them, keep them short and keep the entire definition in one place.
 The function name is on the first line. I think 10 seconds would be an
 exaggeration of an admittedly real issue.
Thought so, too, but then I *did* spend 10 seconds trying to find it myself!
Does your editor not have syntax highlighting? For me, I just look immediately below the giant block of blue comment text and check the second word in the line below.
Feb 14 2017
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2017-02-14 17:25, Andrei Alexandrescu wrote:

 My recollection is past discussions got stalled because the approach is
 combinatorially bankrupt. How would one express the constraints of the
 functions above with simple named constraints? (Not rhetorical; please
 do provide an example if possible.)
It would obviously take a while for me to figure that out. Is not like I have a fully defined new language feature in my head. I do see a possibility for a slightly improvement in a different area, in the example of "remove". If we look at the signature, without the constraints: Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) Why isn't it possible to use "auto" when declaring a parameter with a default argument? Range remove (auto s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) A minor improvement, but I also feel like it would lift an arbitrary limitation in the language. Alternatively something that Swift supports: Range remove (SwapStrategy s = .stable, Range, Offset...) (Range range, Offset offset) The compiler knows that the type of "s" is SwapStrategy, therefore "stable" has to be a member of SwapStrategy. In the case of D, the leading dot has a different meaning so another syntax would be required. Unfortunately this has already been discussed and rejected by Walter.
 Before long there is an explosion in
 names ("BidirectionalWithLvalueElementsAndLength", ...). This is what
 has buried not one, but two concepts proposals for C++, leading to the
 current Concepts Lite proposal.
I don't see that as a problem, but I would rather name it "Removable" or something that does not include all the conditions in the name. But I imagine that a constraint would consist of a combination of declarations (like an interface) and boolean conditions. Lets look it differently. For a regular (non-template) function with a user defined type (class, struct). If it was possible to define the type inline, would you? I hope that answer is no. Example: Standard D: struct Bar { int a; int b; } void foo(Bar bar); With inline user defined types: void foo(struct { int a; int b; } bar); Assuming the answer is no, why would it be any different for a template type? You can see all the template constraints for a type as a form of metatype.
 I haven't followed that lately but I
 remember it combines an engineering effort to reduce the number of names
 introduced (for the standard library only, which is a limitation) with
 adding support to Boolean logic (surprise, surprise) to the feature (see
 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4377.pdf), the
 most recent proposal, which was rejected for C++17. Over time, C++
 concepts have moved from the broken combinatorially-bankrupt form slowly
 toward D's Boolean constraints (just with a gratuitously awkward
 notation). I presume they will be merged into the language when they'll
 have capabilities comparable to D's system.

 The question is, given this experience, do we want to move the opposite
 direction?
I haven't followed the C++ concepts lately either and not very closely at all so I don't feel I can comment on the C++ concepts. But if I recall correctly, you're "static if" proposal wasn't well received [1]. But now with "if constexpr" it looks like they're changing their minds. [1] "The static if feature recently proposed for C++ [1, 2] is fundamentally flawed, and its adoption would be a disaster for the language" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3613.pdf -- /Jacob Carlborg
Feb 15 2017
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/15/17 3:53 AM, Jacob Carlborg wrote:
 I do see a possibility for a slightly improvement in a different area,
 in the example of "remove". If we look at the signature, without the
 constraints:

 Range remove
 (SwapStrategy s = SwapStrategy.stable, Range, Offset...)
 (Range range, Offset offset)

 Why isn't it possible to use "auto" when declaring a parameter with a
 default argument?

 Range remove
 (auto s = SwapStrategy.stable, Range, Offset...)
 (Range range, Offset offset)

 A minor improvement, but I also feel like it would lift an arbitrary
 limitation in the language.
That's nice, could you please submit as an enhancement request on bugzilla?
 I haven't followed the C++ concepts lately either and not very closely
 at all so I don't feel I can comment on the C++ concepts.
You may want to correct that if you want to make a serious proposal for D concepts.
 But if I
 recall correctly, you're "static if" proposal wasn't well received [1].
 But now with "if constexpr" it looks like they're changing their minds.

 [1] "The static if feature recently proposed for C++ [1, 2] is
 fundamentally flawed, and its adoption would be a disaster for the
 language" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3613.pdf
"I made a terrible mistake" is spelled http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0128r0.html here. It still has a cargo cult flavor because it introduces a new scope, which kinda misses the point of static if. Andrei
Feb 15 2017
next sibling parent Jack Stouffer <jack jackstouffer.com> writes:
On Wednesday, 15 February 2017 at 14:01:28 UTC, Andrei 
Alexandrescu wrote:
 It still has a cargo cult flavor because it introduces a new 
 scope, which kinda misses the point of static if.
That's an understatement. Having static if introduce a new scope makes static if useless. Creating a new scope means that code duplication inside of a function is now a must. At that point you might as well just make what you were going to put in the other static if branch as a different overload. It also means design by introspection/range composition is now impossible.
Feb 15 2017
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2017-02-15 15:01, Andrei Alexandrescu wrote:

 That's nice, could you please submit as an enhancement request on bugzilla?
https://issues.dlang.org/show_bug.cgi?id=17186 -- /Jacob Carlborg
Feb 15 2017
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 02/15/2017 02:22 PM, Jacob Carlborg wrote:
 On 2017-02-15 15:01, Andrei Alexandrescu wrote:

 That's nice, could you please submit as an enhancement request on
 bugzilla?
https://issues.dlang.org/show_bug.cgi?id=17186
Thanks. I'll take it up to Walter for preapproval. -- Andrei
Feb 15 2017
parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Wednesday, February 15, 2017 14:30:02 Andrei Alexandrescu via 
Digitalmars-d wrote:
 On 02/15/2017 02:22 PM, Jacob Carlborg wrote:
 On 2017-02-15 15:01, Andrei Alexandrescu wrote:
 That's nice, could you please submit as an enhancement request on
 bugzilla?
https://issues.dlang.org/show_bug.cgi?id=17186
Thanks. I'll take it up to Walter for preapproval. -- Andrei
It's one of those features that I was surprised when you couldn't do it. - Jonathan M Davis
Feb 15 2017
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 02/15/2017 03:31 PM, Jonathan M Davis via Digitalmars-d wrote:
 On Wednesday, February 15, 2017 14:30:02 Andrei Alexandrescu via
 Digitalmars-d wrote:
 On 02/15/2017 02:22 PM, Jacob Carlborg wrote:
 On 2017-02-15 15:01, Andrei Alexandrescu wrote:
 That's nice, could you please submit as an enhancement request on
 bugzilla?
https://issues.dlang.org/show_bug.cgi?id=17186
Thanks. I'll take it up to Walter for preapproval. -- Andrei
It's one of those features that I was surprised when you couldn't do it.
We agree. It's preapproved now. -- Andrei
Feb 15 2017
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/15/2017 12:31 PM, Jonathan M Davis via Digitalmars-d wrote:
 It's one of those features that I was surprised when you couldn't do it.
It was an oversight. We just never thought of it.
Feb 15 2017
parent reply ZombineDev <petar.p.kirov gmail.com> writes:
On Thursday, 16 February 2017 at 00:08:12 UTC, Walter Bright 
wrote:
 On 2/15/2017 12:31 PM, Jonathan M Davis via Digitalmars-d wrote:
 It's one of those features that I was surprised when you 
 couldn't do it.
It was an oversight. We just never thought of it.
What do you think about generalizing this feature to allow introducing template value parameters without default value, i.e.: // Note: `beg` and `end` have no default value auto bounded (auto beg, auto end, auto onErrPolicy = Enforce("Value out of range"), T) (T value) { return Bounded!(beg, end, Policy)(value); } struct Enforce { this(string) { /* */ } } int tmp; readf("%s", &x); enum policy = Enforce("User age must be between 18 and 150"); auto userAge = bounded!(18, 150, policy)(tmp); "Declaring non-type template arguments with auto" is also coming to C++17: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0127r1.html BTW, shouldn't we use `enum`, instead of `auto`, since everywhere else `enum` means guaranteed to be computed at compile-time whereas `auto` means the opposite?
Feb 15 2017
next sibling parent John Colvin <john.loughran.colvin gmail.com> writes:
On Thursday, 16 February 2017 at 00:37:00 UTC, ZombineDev wrote:
 On Thursday, 16 February 2017 at 00:08:12 UTC, Walter Bright 
 wrote:
 On 2/15/2017 12:31 PM, Jonathan M Davis via Digitalmars-d 
 wrote:
 It's one of those features that I was surprised when you 
 couldn't do it.
It was an oversight. We just never thought of it.
What do you think about generalizing this feature to allow introducing template value parameters without default value, i.e.: // Note: `beg` and `end` have no default value auto bounded (auto beg, auto end, auto onErrPolicy = Enforce("Value out of range"), T) (T value) { return Bounded!(beg, end, Policy)(value); }
note that this would still be usable with constraits via e.g. if(is(typeof(beg) : int))
Feb 15 2017
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2017-02-16 01:37, ZombineDev wrote:

 BTW, shouldn't we use `enum`, instead of `auto`, since everywhere else
 `enum` means guaranteed to be computed at compile-time whereas `auto`
 means the opposite?
Far enough, since it's not possible to change the parameter inside the function anyway. Question though, what happens with an array literal, example: void foo(int[] a = [1, 2, 3])() { auto b = a; // allocation ? auto c = a; // allocation ? } As far as I understand, if an array is declared as a manifest constant it will cause a new allocation for each time it's used. enum a = [1, 2, 3]; auto b = a; // new allocation auto c = a; // new allocation What happens when an array literal is a default value for a template parameter? -- /Jacob Carlborg
Feb 15 2017
parent ZombineDev <petar.p.kirov gmail.com> writes:
On Thursday, 16 February 2017 at 07:34:02 UTC, Jacob Carlborg 
wrote:
 On 2017-02-16 01:37, ZombineDev wrote:

 BTW, shouldn't we use `enum`, instead of `auto`, since 
 everywhere else
 `enum` means guaranteed to be computed at compile-time whereas 
 `auto`
 means the opposite?
Far enough, since it's not possible to change the parameter inside the function anyway. Question though, what happens with an array literal, example: void foo(int[] a = [1, 2, 3])() { auto b = a; // allocation ? auto c = a; // allocation ? } As far as I understand, if an array is declared as a manifest constant it will cause a new allocation for each time it's used. enum a = [1, 2, 3]; auto b = a; // new allocation auto c = a; // new allocation What happens when an array literal is a default value for a template parameter?
enums are just literals - values without identity. This means that if you need them at runtime, the compiler will need to allocate storage for each time they are used. In cases where such a values is used in more places, it may be more economical to assign it to a static immutable variable and reference it in place of the enum. In the case of template parameters I would expect this to be even more true, because an array by definition is a contiguous chunk of memory and a template parameter is something encoded in the mangled name of the symbol at runtime, so the compiler can't get away from allocating additional storage for each usage of the template value parameter at runtime. ( compiler devs, please correct me if I am wrong)
Feb 16 2017
prev sibling parent reply Jack Stouffer <jack jackstouffer.com> writes:
On Wednesday, 15 February 2017 at 08:53:30 UTC, Jacob Carlborg 
wrote:
 "The static if feature recently proposed for C++ [1, 2] is 
 fundamentally flawed, and its adoption would be a disaster for 
 the language" 
 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3613.pdf
I honestly feel bad for the C++ people here. Looks like someone didn't explain the benefits/uses of static if very well. It also looks like they failed to point to Phobos as an example of great static if usage. I imagine many heads would explode after seeing Andrei's `find` improvements with static if.
Feb 15 2017
parent Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Wednesday, February 15, 2017 14:35:40 Jack Stouffer via Digitalmars-d 
wrote:
 On Wednesday, 15 February 2017 at 08:53:30 UTC, Jacob Carlborg

 wrote:
 "The static if feature recently proposed for C++ [1, 2] is
 fundamentally flawed, and its adoption would be a disaster for
 the language"
 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3613.pdf
I honestly feel bad for the C++ people here. Looks like someone didn't explain the benefits/uses of static if very well. It also looks like they failed to point to Phobos as an example of great static if usage. I imagine many heads would explode after seeing Andrei's `find` improvements with static if.
LOL. It would be interesting to know which parts of D or Phobos blew people's minds. Personally, startsWith blew my mind. I remember taking up the challenge of making endsWith work with all of the same kind of arguments that startsWith did, and at the time, I had no idea how startsWith was implemented. Seeing how it used recursive template instantiations definitely took a bit to wrap my mind around and was _very_ eye opening. - Jonathan M Davis
Feb 15 2017
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2017-02-14 17:25, Andrei Alexandrescu wrote:

 My recollection is past discussions got stalled because the approach is
 combinatorially bankrupt. How would one express the constraints of the
 functions above with simple named constraints? (Not rhetorical; please
 do provide an example if possible.) Before long there is an explosion in
 names ("BidirectionalWithLvalueElementsAndLength", ...).
Forgot to say that I don't think it's unreasonable to have a named constraint for each function (regardless if using only existing language features or adding something new). That way you can lift out the constraint separately from the function declaration. One way to make something complex less complex, is to split it up. It's not like you would have the complete source code of an application in a single file, that would be too much to read in one place (yes, there are other reasons to have multiple files). Same idea here. -- /Jacob Carlborg
Feb 15 2017
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Tuesday, 14 February 2017 at 16:25:17 UTC, Andrei Alexandrescu 
wrote:
 On 02/14/2017 10:49 AM, Jacob Carlborg wrote:
 On 2017-02-14 15:37, Andrei Alexandrescu wrote:

 How are they so,
Example [1]. That signature spans 8 lines, it took me 10 seconds to find the actual function name.
Copying here to make things easier: Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if (s != SwapStrategy.stable && isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1); The function name is on the first line. I think 10 seconds would be an exaggeration of an admittedly real issue.
 Example [2], 5 lines.
Copying here as well (reflowed for email): Tuple!(InputRange1, InputRange2) swapRanges(InputRange1, InputRange2)(InputRange1 r1, InputRange2 r2) if (isInputRange!(InputRange1) && isInputRange!(InputRange2) && hasSwappableElements!(InputRange1) && hasSwappableElements!(InputRange2) && is(ElementType!(InputRange1) == ElementType!(InputRange2))); One immediate matter here is redundant parens, of which elimination would lead to the marginally more palatable: Tuple!(InputRange1, InputRange2) swapRanges(InputRange1, InputRange2)(InputRange1 r1, InputRange2 r2) if (isInputRange!InputRange1 && isInputRange!InputRange2 && hasSwappableElements!InputRange1 && hasSwappableElements!InputRange2 && is(ElementType!InputRange1 == ElementType!InputRange2));
 and what steps can we take to improve them. Could you
 please give a few examples on how to do things better? Thx! 
 -- Andrei
Well, I would prefer to have template constraints as its own entity in the language not not just a bunch of boolean conditions. This has been talked about before several times. Something like: constraint Foo { void foo(); } void bar(T : Foo)(T t);
My recollection is past discussions got stalled because the approach is combinatorially bankrupt. How would one express the constraints of the functions above with simple named constraints? (Not rhetorical; please do provide an example if possible.) Before long there is an explosion in names ("BidirectionalWithLvalueElementsAndLength", ...).
If I had my way, like so: Tuple!(InputRange1, InputRange2) swapRanges (InputRange1: (InputRange, HasSwappableElements), InputRange2: (InputRange, HasSwappableElements)) (InputRange1 r1, InputRange2 r2) if (is(ElementType!(InputRange1) == ElementType!(InputRange2))); Assuming I'm still having my way, the above would result in the compiler telling me that given: struct Foo { enum empty = false; enum front = 42; } If I tried to instantiate swapRanges!(Foo, Foo), I'd want to get something akin to: error: foo.Foo does not satisfy InputRange: no function `popFront` for `foo.Foo` For now I'm getting quite a bit of mileage from my concepts library. Atila
Feb 15 2017
prev sibling next sibling parent Jack Stouffer <jack jackstouffer.com> writes:
On Tuesday, 14 February 2017 at 14:37:39 UTC, Andrei Alexandrescu 
wrote:
 what steps can we take to improve them.
You really can't unfortunately. Verbose-ness is a nessesary component of type-safe duck typing.
Feb 14 2017
prev sibling parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Tuesday, February 14, 2017 09:37:39 Andrei Alexandrescu via Digitalmars-d 
wrote:
 On 02/14/2017 04:55 AM, Jacob Carlborg wrote:
 The signatures we already have in Phobos is quite ridiculous
How are they so, and what steps can we take to improve them. Could you please give a few examples on how to do things better? Thx! -- Andrei
Unfortunately, I'm not sure that there's much that we can do. Each of the features that clutter up a function signature is useful and valuable in and of itself, but when you add them all together, it can get pretty messy. We've managed to improve some aspects over time, but there has to be a limit to it. At some point, the information takes up however much space it takes up. Having auto functions helped considerably for cleaning up function signatures where templates were involved. Declaring eponymous templates to use as traits for commonly used idioms helps - and in some cases, maybe we should add more of those for common combinations, but the best we could do is clean up some common cases, because it ultimately doesn't scale well for the reasons that you explained why C++ has had issue with getting concepts into the standard. Having different defaults for function attributes would help in some cases (e.g. have safe and pure be the default), but I don't see how we could do that at this point without breaking a bunch of code. And attribute inference already takes care of the attributes on the most cluttered functions, since those are going to be templated functions with template constraints. I think that things just reach a point where if you want the functionality, you get a messy function signature, and you can't do much more than try and format it nicely. For better or worse, it's just one of the costs of what D provides. That being said, at some point, you have to ask whether each added feature is worth the cost when you consider how it's going to clutter up function signatures even further. And while I do think that there is value in DIP 1005 and the proposed from template, I also think that it's taking it too far. IMHO, it's just not worth marking functions even further - at least not in most code. Maybe it's worth it in something like Phobos where everyone is using it and benefiting from the compilation speed up, but Walter has been wanting to implement lazy imports anyway, and that would fix the problem without doing anything to any function signatures. It does lose the benefits of tying the imports to the function, but personally, I don't think that that's's worth the extra cost of further cluttering up the function signature. As it is, I'm increasingly of the opinion that local and selective imports aren't worth it. It's just so much nicer to able to slap the required imports at the beginning of the module and forget about them than having to worry about maintaining a list of selective imports or have all of the extra import lines inside of all of the functions. And adding imports to the function signatures is just making the whole local import situation that much worse. So, regardless of what happens with DIP 1005 or from, I'd very much like to see lazy imports implemented, and I don't expect use DIP 1005 or from in much code that I write. - Jonathan M Davis
Feb 14 2017
parent kinke <noone nowhere.com> writes:
On Tuesday, 14 February 2017 at 20:03:13 UTC, Jonathan M Davis 
wrote:
 That being said, at some point, you have to ask whether each 
 added feature is worth the cost when you consider how it's 
 going to clutter up function signatures even further. And while 
 I do think that there is value in DIP 1005 and the proposed 
 from template, I also think that it's taking it too far. IMHO, 
 it's just not worth marking functions even further - at least 
 not in most code. Maybe it's worth it in something like Phobos 
 where everyone is using it and benefiting from the compilation 
 speed up, but Walter has been wanting to implement lazy imports 
 anyway, and that would fix the problem without doing anything 
 to any function signatures. It does lose the benefits of tying 
 the imports to the function, but personally, I don't think that 
 that's's worth the extra cost of further cluttering up the 
 function signature. As it is, I'm increasingly of the opinion 
 that local and selective imports aren't worth it. It's just so 
 much nicer to able to slap the required imports at the 
 beginning of the module and forget about them than having to 
 worry about maintaining a list of selective imports or have all 
 of the extra import lines inside of all of the functions. And 
 adding imports to the function signatures is just making the 
 whole local import situation that much worse.
+1. D's beautiful syntax plays a key role for attracting new folks, and I see it endangered by recent developments.
Feb 14 2017
prev sibling parent Jack Stouffer <jack jackstouffer.com> writes:
On Tuesday, 14 February 2017 at 09:55:51 UTC, Jacob Carlborg 
wrote:
 Why? It looks awful. The signatures we already have in Phobos 
 is quite ridiculous, this will not improve. Isn't this and the 
 whole idea of DIP 1005 just a workaround for the compiler not 
 lazily analyzing the symbols.
https://github.com/dlang/DIPs/blob/master/DIPs/DIP1005.md#alternative-lazy-imports
Feb 14 2017
prev sibling parent reply Johannes Pfau <nospam example.com> writes:
Am Mon, 13 Feb 2017 19:49:28 -0800
schrieb Timothee Cour via Digitalmars-d <digitalmars-d puremagic.com>:

 What about allowing syntax sugar as an alternative to relying on the
 new `from/Module` inline import idiom:
 
 ```
 void fun(T)(std.stdio::File input, T value) if
 (std.traits::isIntegral!T) {...}
 ```
If you use a single ':' instead you can argue it's simply a shortened, inline version of a selective import: import std.stdio : File; //still valid D import std.stdio:File; //expression instead of statement => remove ; import std.stdio:File //We're not importing a symbol, we're simply referring to it std.stdio:File -- Johannes
Feb 14 2017
next sibling parent reply Daniel N <no public.email> writes:
On Tuesday, 14 February 2017 at 10:21:03 UTC, Johannes Pfau wrote:
 Am Mon, 13 Feb 2017 19:49:28 -0800
 schrieb Timothee Cour via Digitalmars-d 
 <digitalmars-d puremagic.com>:

 What about allowing syntax sugar as an alternative to relying 
 on the new `from/Module` inline import idiom:
 
 ```
 void fun(T)(std.stdio::File input, T value) if
 (std.traits::isIntegral!T) {...}
 ```
If you use a single ':' instead you can argue it's simply a shortened, inline version of a selective import: import std.stdio : File; //still valid D import std.stdio:File; //expression instead of statement => remove ; import std.stdio:File //We're not importing a symbol, we're simply referring to it std.stdio:File -- Johannes
my_module:File is it a label followed by File or File inside my_module?
Feb 14 2017
next sibling parent Daniel N <no public.email> writes:
On Tuesday, 14 February 2017 at 10:23:51 UTC, Daniel N wrote:
 -- Johannes
my_module:File is it a label followed by File or File inside my_module?
This would be unambiguous AFAIK, since labels can't start with "import." import.my_module:File
Feb 14 2017
prev sibling parent Johannes Pfau <nospam example.com> writes:
Am Tue, 14 Feb 2017 10:23:51 +0000
schrieb Daniel N <no public.email>:

 On Tuesday, 14 February 2017 at 10:21:03 UTC, Johannes Pfau wrote:
 Am Mon, 13 Feb 2017 19:49:28 -0800
 schrieb Timothee Cour via Digitalmars-d 
 <digitalmars-d puremagic.com>:
  
 What about allowing syntax sugar as an alternative to relying 
 on the new `from/Module` inline import idiom:
 
 ```
 void fun(T)(std.stdio::File input, T value) if
 (std.traits::isIntegral!T) {...}
 ```  
If you use a single ':' instead you can argue it's simply a shortened, inline version of a selective import: import std.stdio : File; //still valid D import std.stdio:File; //expression instead of statement => remove ; import std.stdio:File //We're not importing a symbol, we're simply referring to it std.stdio:File -- Johannes
my_module:File is it a label followed by File or File inside my_module?
This might be a problem. Labels can only occur in front of a statement though: https://dlang.org/spec/statement.html#LabeledStatement So it boils down to: ModuleFullyQualifiedName:ImportBind vs LabeledStatement -- Johannes
Feb 14 2017
prev sibling parent Daniel Kozak via Digitalmars-d <digitalmars-d puremagic.com> writes:
Dne 14.2.2017 v 11:21 Johannes Pfau via Digitalmars-d napsal(a):

 Am Mon, 13 Feb 2017 19:49:28 -0800
 schrieb Timothee Cour via Digitalmars-d <digitalmars-d puremagic.com>:

 What about allowing syntax sugar as an alternative to relying on the
 new `from/Module` inline import idiom:

 ```
 void fun(T)(std.stdio::File input, T value) if
 (std.traits::isIntegral!T) {...}
 ```
If you use a single ':' instead you can argue it's simply a shortened, inline version of a selective import: import std.stdio : File; //still valid D import std.stdio:File; //expression instead of statement => remove ; import std.stdio:File //We're not importing a symbol, we're simply referring to it std.stdio:File -- Johannes
Exactly! I was going to write the same
Feb 14 2017