digitalmars.D - syntax sugar: std.path::buildPath instead of from!"std.path".buildPath
- Timothee Cour via Digitalmars-d (37/37) Feb 13 2017 What about allowing syntax sugar as an alternative to relying on the new
- Jack Stouffer (19/61) Feb 13 2017 The only thing from has going for it is that it doesn't require a
- Timothee Cour via Digitalmars-d (11/76) Feb 13 2017 language change. Yours does, so it should be compared to the proposals i...
- Timothee Cour via Digitalmars-d (6/91) Feb 13 2017 as for language change, I believe the proposed syntax (`::`) can be redu...
- Walter Bright (2/2) Feb 14 2017 Please fix your newsreader client to send as text, not as html.
- Chris M (10/52) Feb 13 2017 Well, as Jack said there's no real clean way to do this without
- Timothee Cour via Digitalmars-d (10/69) Feb 13 2017 `#std.stdio.File` doesn't work, we don't know where is the module
- Jacob Carlborg (6/21) Feb 14 2017 Why? It looks awful. The signatures we already have in Phobos is quite
- Daniel N (8/12) Feb 14 2017 Even those of you who favor...
- Jonathan M Davis via Digitalmars-d (30/55) Feb 14 2017 Mostly, but not just that. By having something like DIP 1005, all of the
- Sebastiaan Koppe (3/7) Feb 14 2017 This.
- Meta (8/37) Feb 14 2017 This is what I had thought as well when Andrei first posted
- Andrei Alexandrescu (3/4) Feb 14 2017 How are they so, and what steps can we take to improve them. Could you
- Jacob Carlborg (19/22) Feb 14 2017 Example [1]. That signature spans 8 lines, it took me 10 seconds to find...
- Andrei Alexandrescu (46/61) Feb 14 2017 Copying here to make things easier:
- bachmeier (14/37) Feb 14 2017 [...]
- bachmeier (3/14) Feb 14 2017 I mean, is there some reason that this shouldn't be allowed? Any
- H. S. Teoh via Digitalmars-d (8/25) Feb 14 2017 This is already possible:
- bachmeier (3/9) Feb 14 2017 That's good to know. I didn't try that syntax, though, which
- Jack Stouffer (6/9) Feb 14 2017 Because at that point, it's no longer a set of lego's being put
- Adam D. Ruppe (19/23) Feb 14 2017 You can do that, it would just be a helper function / template
- bachmeier (3/14) Feb 14 2017 Makes sense. I had assumed there would be a lot of duplication of
- Chris Wright (6/16) Feb 14 2017 Because now I have to look up the definition of fooConstraint and then I...
- bachmeier (8/11) Feb 15 2017 To be honest, I don't often look at function definitions for the
- Walter Bright (29/38) Feb 14 2017 It could be improved slightly using indentation:
- Walter Bright (3/5) Feb 14 2017 For reference:
- H. S. Teoh via Digitalmars-d (56/89) Feb 14 2017 +1. I've raised this point many times, but didn't seem to generate much
- Walter Bright (3/4) Feb 14 2017 Please take this on!
- Jack Stouffer (3/8) Feb 15 2017 I'm currently trying to do this again in std.format, this is the
- Adam D. Ruppe (8/10) Feb 14 2017 Kneel before your god!
- Jacob Carlborg (5/11) Feb 14 2017 Your documentation is an improvement but it doesn't help when reading
- Chris Wright (11/13) Feb 15 2017 For me, it almost entirely obviates reading the source code. I only need...
- Adam D. Ruppe (23/28) Feb 15 2017 I really really REALLY REALLY wish the compiler would tell you at
- Adam D. Ruppe (9/11) Feb 15 2017 Yeah, I think there's a few things we can do in the source too.
- Seb (19/30) Feb 15 2017 Speaking of right balance, there's currently a PR at Phobos that
- Adam D. Ruppe (6/9) Feb 15 2017 That tells me you already have an empirical clear win! If you
- Andrei Alexandrescu (2/7) Feb 15 2017 isStringLike. I wanted to add this for a while already. Please do! -- An...
- Seb (3/16) Feb 15 2017 https://github.com/dlang/phobos/pull/5137
- Walter Bright (12/13) Feb 15 2017 What I've found messy and confusing with string overloads in Phobos is t...
- Jonathan M Davis via Digitalmars-d (5/20) Feb 15 2017 Yeah. It's a bit of a mess. And types with alias this and enums _really_
- Jack Stouffer (11/19) Feb 15 2017 Ok
- Walter Bright (3/19) Feb 15 2017 That's a good start. A test of that is to look at Phobos' actual usage o...
- Jonathan M Davis via Digitalmars-d (30/49) Feb 15 2017 Except that you're forgetting enums. Also, there's this mess:
- Walter Bright (4/10) Feb 15 2017 I suspect the only way forward is to go through Phobos and collect all t...
- H. S. Teoh via Digitalmars-d (41/71) Feb 15 2017 This describes the current state of Phobos, but I think what Walter is
- Walter Bright (4/4) Feb 15 2017 Also, as mentioned in the std.algorithm.mutation.remove case, constraint...
- Atila Neves (12/23) Feb 16 2017 I submitted a Phobos PR for `isInputRangeOf`. It was nuked from
- Andrei Alexandrescu (2/22) Feb 16 2017 If you find a number of these, that would be good evidence. -- Andrei
- Atila Neves (8/45) Feb 22 2017 I found just over 70 similar lines, which is more than I expected
- Andrei Alexandrescu (4/38) Feb 22 2017 If you submit a pull request that simplifies >70 constraints in Phobos
- Walter Bright (2/11) Feb 15 2017 That looks pretty sweet!
- Jacob Carlborg (4/22) Feb 15 2017 Doesn't help when reading the source code :(.
- Daniel N (18/43) Feb 15 2017 Something akin to this would improve readability at least for me,
- Daniel N (15/22) Feb 15 2017 (lunch-break => time to hack D!)
- Andrei Alexandrescu (3/25) Feb 15 2017 That looks pretty neat. Can you find 4-5 cases in which this could be
- Timothee Cour via Digitalmars-d (16/46) Feb 15 2017 This thread completely diverged from the original post, which was propsi...
- Nick Treleaven (7/22) Feb 15 2017 I really think allowing `with (module_!"std.foo")` before
- timotheecour (8/34) Feb 16 2017 both are useful:
- Nick Treleaven (12/15) Feb 17 2017 Doesn't add indentation:
- Timothee Cour via Digitalmars-d (32/41) Feb 18 2017 * what is the implementation of `module_` ? `from` defined earlier
- Nick Treleaven (9/51) Feb 21 2017 I don't know how to implement it. Possibly with multiple alias
- Walter Bright (1/1) Feb 15 2017 Please fix your newsreader so it submits postings in text format, not ht...
- John Colvin (43/70) Feb 15 2017 Slightly OT, but it is related so bear with me:
- Jacob Carlborg (30/49) Feb 15 2017 I've been thinking something similar. But this is the way I see it:
- ZombineDev (12/74) Feb 16 2017 You can sort of do this even today. All you need is struct with
- ZombineDev (7/61) Feb 16 2017 I have a library that allows just that, but with even shorter
- Daniel Nielsen (4/31) Feb 16 2017 Thanks, I'll get back to you. Unfortunately I just contracted
- Chris Wright (2/7) Feb 15 2017 We're not worthy!
- Dominikus Dittes Scherkl (7/15) Feb 15 2017 [...]
- Martin Nowak (10/47) May 23 2017 Turns out that this advice is somewhat problematic. When the
- Lurker (12/30) Feb 14 2017 Range
- Chris Wright (12/29) Feb 14 2017 Which adds another layer to determine what sort of arguments the thing
- Jacob Carlborg (52/72) Feb 15 2017 It would obviously take a while for me to figure that out. Is not like I...
- Andrei Alexandrescu (9/30) Feb 15 2017 You may want to correct that if you want to make a serious proposal for
- Jack Stouffer (10/12) Feb 15 2017 That's an understatement.
- Jacob Carlborg (4/5) Feb 15 2017 https://issues.dlang.org/show_bug.cgi?id=17186
- Andrei Alexandrescu (2/6) Feb 15 2017 Thanks. I'll take it up to Walter for preapproval. -- Andrei
- Jonathan M Davis via Digitalmars-d (4/11) Feb 15 2017 It's one of those features that I was surprised when you couldn't do it.
- Andrei Alexandrescu (2/13) Feb 15 2017 We agree. It's preapproved now. -- Andrei
- Walter Bright (2/3) Feb 15 2017 It was an oversight. We just never thought of it.
- ZombineDev (24/28) Feb 15 2017 What do you think about generalizing this feature to allow
- John Colvin (3/22) Feb 15 2017 note that this would still be usable with constraits via e.g.
- Jacob Carlborg (18/21) Feb 15 2017 Far enough, since it's not possible to change the parameter inside the
- ZombineDev (15/36) Feb 16 2017 enums are just literals - values without identity. This means
- Jack Stouffer (7/11) Feb 15 2017 I honestly feel bad for the C++ people here. Looks like someone
- Jonathan M Davis via Digitalmars-d (9/20) Feb 15 2017 LOL. It would be interesting to know which parts of D or Phobos blew
- Jacob Carlborg (11/16) Feb 15 2017 Forgot to say that I don't think it's unreasonable to have a named
- Atila Neves (22/82) Feb 15 2017 If I had my way, like so:
- Jack Stouffer (4/5) Feb 14 2017 You really can't unfortunately. Verbose-ness is a nessesary
- Jonathan M Davis via Digitalmars-d (46/50) Feb 14 2017 Unfortunately, I'm not sure that there's much that we can do. Each of th...
- kinke (4/25) Feb 14 2017 +1. D's beautiful syntax plays a key role for attracting new
- Jack Stouffer (3/7) Feb 14 2017 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1005.md#alternative-la...
- Johannes Pfau (12/19) Feb 14 2017 If you use a single ':' instead you can argue it's simply a shortened,
- Daniel N (3/23) Feb 14 2017 my_module:File
- Daniel N (4/8) Feb 14 2017 This would be unambiguous AFAIK, since labels can't start with
- Johannes Pfau (8/38) Feb 14 2017 This might be a problem. Labels can only occur in front of a
- Daniel Kozak via Digitalmars-d (2/21) Feb 14 2017 Exactly! I was going to write the same
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
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
The only thing from has going for it is that it doesn't require alanguage 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
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 alanguage 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
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
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
`#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
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 possibleWhy? 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
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
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: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 DavisWhat 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 possibleWhy? 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.
Feb 14 2017
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
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: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.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 possibleWhy? 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.
Feb 14 2017
On 02/14/2017 04:55 AM, Jacob Carlborg wrote:The signatures we already have in Phobos is quite ridiculousHow 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
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! -- AndreiWell, 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
On 02/14/2017 10:49 AM, Jacob Carlborg wrote:On 2017-02-14 15:37, Andrei Alexandrescu wrote: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.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.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));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? Andreiand what steps can we take to improve them. Could you please give a few examples on how to do things better? Thx! -- AndreiWell, 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);
Feb 14 2017
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
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
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: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.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
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) { ... } TThat's good to know. I didn't try that syntax, though, which explains why I couldn't get it to work.
Feb 14 2017
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 likeBecause 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
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.stableYou 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
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
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
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
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
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
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 toadd 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
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
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
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
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
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
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 todpldocs.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
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
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: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?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
On Wednesday, 15 February 2017 at 17:18:15 UTC, Seb wrote:Now as this same block is used > 30x in PhobosThat 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
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
On Wednesday, 15 February 2017 at 18:51:40 UTC, Andrei Alexandrescu wrote:On 02/15/2017 12:18 PM, Seb wrote:https://github.com/dlang/phobos/pull/5137uint 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
On 2/15/2017 10:51 AM, Andrei Alexandrescu wrote:isStringLike. I wanted to add this for a while already. Please do! -- AndreiWhat 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
On Wednesday, February 15, 2017 12:54:02 Walter Bright via Digitalmars-d wrote:On 2/15/2017 10:51 AM, Andrei Alexandrescu wrote:Yeah. It's a bit of a mess. And types with alias this and enums _really_ don't help things. - Jonathan M DavisisStringLike. I wanted to add this for a while already. Please do! -- AndreiWhat 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
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 thingsOk1. auto decoding dynamic arraysNarrow strings2. not auto decoding arraysWide strings3. static arraysDo these need to be called anything other than "static arrays"? Also, they're not ranges, so they're usually tested with isStaticArray4. aggregates with an 'alias this' to a stringisConvertibleToString5. ranges of charactersCharacter range6. something convertible to a stringSame as 4
Feb 15 2017
On 2/15/2017 1:03 PM, Jack Stouffer wrote:On Wednesday, 15 February 2017 at 20:54:02 UTC, Walter Bright wrote:That's a good start. A test of that is to look at Phobos' actual usage of constraints and see if they fit in.I'd like to take a step back and devise a consistent taxonomy of these thingsOk1. auto decoding dynamic arraysNarrow strings2. not auto decoding arraysWide strings3. static arraysDo these need to be called anything other than "static arrays"? Also, they're not ranges, so they're usually tested with isStaticArray4. aggregates with an 'alias this' to a stringisConvertibleToString5. ranges of charactersCharacter range6. something convertible to a stringSame as 4
Feb 15 2017
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: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 DavisI'd like to take a step back and devise a consistent taxonomy of these thingsOk1. auto decoding dynamic arraysNarrow strings2. not auto decoding arraysWide strings3. static arraysDo these need to be called anything other than "static arrays"? Also, they're not ranges, so they're usually tested with isStaticArray4. aggregates with an 'alias this' to a stringisConvertibleToString5. ranges of charactersCharacter range6. something convertible to a stringSame as 4
Feb 15 2017
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
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: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.I'd like to take a step back and devise a consistent taxonomy of these thingsOk1. auto decoding dynamic arraysNarrow strings2. not auto decoding arraysWide strings3. static arraysDo these need to be called anything other than "static arrays"? Also, they're not ranges, so they're usually tested with isStaticArray4. aggregates with an 'alias this' to a stringisConvertibleToString5. ranges of charactersCharacter range6. something convertible to a stringSame as 4
Feb 15 2017
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
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: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. AtilaYour 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 16 2017
On 2/16/17 7:11 AM, Atila Neves wrote:On Wednesday, 15 February 2017 at 17:10:26 UTC, Adam D. Ruppe wrote:If you find a number of these, that would be good evidence. -- AndreiOn Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob Carlborg wrote: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);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 16 2017
On Thursday, 16 February 2017 at 13:21:04 UTC, Andrei Alexandrescu wrote:On 2/16/17 7:11 AM, Atila Neves wrote: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. AtilaOn Wednesday, 15 February 2017 at 17:10:26 UTC, Adam D. Ruppe wrote:If you find a number of these, that would be good evidence. -- AndreiOn Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob Carlborg wrote: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);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 22 2017
On 02/22/2017 06:53 AM, Atila Neves wrote:On Thursday, 16 February 2017 at 13:21:04 UTC, Andrei Alexandrescu wrote: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! -- AndreiOn 2/16/17 7:11 AM, Atila Neves wrote: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.On Wednesday, 15 February 2017 at 17:10:26 UTC, Adam D. Ruppe wrote:If you find a number of these, that would be good evidence. -- AndreiOn Wednesday, 15 February 2017 at 07:56:00 UTC, Jacob Carlborg wrote: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);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 22 2017
On 2/14/2017 9:28 PM, Adam D. Ruppe wrote:On Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright wrote:That looks pretty sweet!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 15 2017
On 2017-02-15 09:19, Walter Bright wrote:On 2/14/2017 9:28 PM, Adam D. Ruppe wrote:Doesn't help when reading the source code :(. -- /Jacob CarlborgOn Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright wrote:That looks pretty sweet!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 15 2017
On Wednesday, 15 February 2017 at 08:59:49 UTC, Jacob Carlborg wrote:On 2017-02-15 09:19, Walter Bright wrote: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; } }On 2/14/2017 9:28 PM, Adam D. Ruppe wrote:Doesn't help when reading the source code :(.On Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright wrote:That looks pretty sweet!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 15 2017
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
On 02/15/2017 06:20 AM, Daniel N wrote:On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andreitemplate 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
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:That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andreitemplate 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
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
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: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 syntaxThis 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 16 2017
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 syntaxDoesn'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
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
On Sunday, 19 February 2017 at 01:53:58 UTC, Timothee Cour wrote:I don't know how to implement it. Possibly with multiple alias this.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);`What about when the 2nd argument uses File as well? That's the violation of DRY I meant.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.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))`
Feb 21 2017
Please fix your newsreader so it submits postings in text format, not html.
Feb 15 2017
On Wednesday, 15 February 2017 at 19:39:52 UTC, Andrei Alexandrescu wrote:On 02/15/2017 06:20 AM, Daniel N 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)) { // ... } 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...On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andreitemplate 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
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
On Thursday, 16 February 2017 at 07:48:37 UTC, Jacob Carlborg wrote:On 2017-02-16 01:40, John Colvin wrote: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).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);
Feb 16 2017
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: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 :DOn 02/15/2017 06:20 AM, Daniel N 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)) { // ... } [snip]On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andreitemplate 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 16 2017
On Wednesday, 15 February 2017 at 19:39:52 UTC, Andrei Alexandrescu wrote:On 02/15/2017 06:20 AM, Daniel N wrote:Thanks, I'll get back to you. Unfortunately I just contracted fever, not fit to leave bed. Nothing dangerous, just am K.O.On Wednesday, 15 February 2017 at 09:22:14 UTC, Daniel N wrote:That looks pretty neat. Can you find 4-5 cases in which this could be used gainfully in Phobos? Thanks! -- Andreitemplate 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 16 2017
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:We're not worthy!A further improvement in the documentation would be to add links to isBidirectionalRange and hasLvalueElements.Kneel before your god!
Feb 15 2017
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
On Tuesday, 14 February 2017 at 20:46:17 UTC, Walter Bright wrote:On 2/14/2017 8:25 AM, Andrei Alexandrescu wrote: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.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.
May 23 2017
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
On Tue, 14 Feb 2017 21:33:00 +0000, Lurker wrote:On Tuesday, 14 February 2017 at 16:25:17 UTC, Andrei Alexandrescu wrote: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.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);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.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!
Feb 14 2017
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
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
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
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
On 02/15/2017 02:22 PM, Jacob Carlborg wrote:On 2017-02-15 15:01, Andrei Alexandrescu wrote:Thanks. I'll take it up to Walter for preapproval. -- AndreiThat's nice, could you please submit as an enhancement request on bugzilla?https://issues.dlang.org/show_bug.cgi?id=17186
Feb 15 2017
On Wednesday, February 15, 2017 14:30:02 Andrei Alexandrescu via Digitalmars-d wrote:On 02/15/2017 02:22 PM, Jacob Carlborg wrote:It's one of those features that I was surprised when you couldn't do it. - Jonathan M DavisOn 2017-02-15 15:01, Andrei Alexandrescu wrote:Thanks. I'll take it up to Walter for preapproval. -- AndreiThat's nice, could you please submit as an enhancement request on bugzilla?https://issues.dlang.org/show_bug.cgi?id=17186
Feb 15 2017
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:We agree. It's preapproved now. -- AndreiOn 02/15/2017 02:22 PM, Jacob Carlborg wrote:It's one of those features that I was surprised when you couldn't do it.On 2017-02-15 15:01, Andrei Alexandrescu wrote:Thanks. I'll take it up to Walter for preapproval. -- AndreiThat's nice, could you please submit as an enhancement request on bugzilla?https://issues.dlang.org/show_bug.cgi?id=17186
Feb 15 2017
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
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: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?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
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:note that this would still be usable with constraits via e.g. if(is(typeof(beg) : int))On 2/15/2017 12:31 PM, Jonathan M Davis via Digitalmars-d wrote: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); }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
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
On Thursday, 16 February 2017 at 07:34:02 UTC, Jacob Carlborg wrote:On 2017-02-16 01:37, ZombineDev wrote: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)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?
Feb 16 2017
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.pdfI 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
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: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"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.pdfI 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
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
On Tuesday, 14 February 2017 at 16:25:17 UTC, Andrei Alexandrescu wrote:On 02/14/2017 10:49 AM, Jacob Carlborg wrote: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. AtilaOn 2017-02-14 15:37, Andrei Alexandrescu wrote: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.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.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));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", ...).and what steps can we take to improve them. Could you please give a few examples on how to do things better? Thx! -- AndreiWell, 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);
Feb 15 2017
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
On Tuesday, February 14, 2017 09:37:39 Andrei Alexandrescu via Digitalmars-d wrote:On 02/14/2017 04:55 AM, Jacob Carlborg wrote: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 DavisThe signatures we already have in Phobos is quite ridiculousHow 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
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
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
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
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>:my_module:File is it a label followed by File or File inside my_module?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
On Tuesday, 14 February 2017 at 10:23:51 UTC, Daniel N wrote:This would be unambiguous AFAIK, since labels can't start with "import." import.my_module:File-- Johannesmy_module:File is it a label followed by File or File inside my_module?
Feb 14 2017
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: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 -- JohannesAm Mon, 13 Feb 2017 19:49:28 -0800 schrieb Timothee Cour via Digitalmars-d <digitalmars-d puremagic.com>:my_module:File is it a label followed by File or File inside my_module?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
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>:Exactly! I was going to write the sameWhat 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