digitalmars.D - Simplifying conversion and formatting code in Phobos
- Andrei Alexandrescu (32/32) Sep 06 2016 We've learned a lot about good D idioms since std.conv was initiated.
- Jack Stouffer (15/22) Sep 07 2016 Yeah, this problem ultimately comes down to healthy use of DRY in
- Walter Bright (21/28) Sep 07 2016 Consider a couple pulls I made for dup():
- Andrei Alexandrescu (2/25) Sep 08 2016 This looks like a nice guideline. Good work. Pushing the roof now. -- An...
- Walter Bright (6/13) Sep 08 2016 An example of doing it better, the put() template:
- Dominikus Dittes Scherkl (9/28) Sep 09 2016 Yay! Finally!
We've learned a lot about good D idioms since std.conv was initiated. And of course it was always the case that writing libraries is quite different from writing code to be used within one sole application. Consider: * to!T(x) must work for virtually all types T and typeof(x) that are sensible. The natural way of doing so is to have several semi-specialized overloads. * Along the way it makes sense to delegate work from the user-level convenient syntax to a more explicit but less convenient syntax. Hence the necessity of toImpl. * The need to "please format all arguments as a string" was a natural necessity e.g. as a second argument to assert or enforce. Hence the necessity of text(x, y, z) as the concatenation of to!string(x), to!string(y), and to!string(z). * FormattedWrite was necessary for fwriteln and related. * All of these have similarities and distinctions so they may use one another opportunistically. The alternative is to write the same code in different parts for the sake of artificially separating things that in fact are related. The drawback of this is taking this in as a reader and maintainer. We have the 'text' template which calls the 'textImpl' template which calls the 'to' template which calls the 'toImpl' template which calls the 'parse' template which calls the 'FormattedWrite' template which calls the 'to' template. Not easy to find where the work is ultimately done. It is a challenge to find the right balance among everything. But I'm sure we can do better than what we have now because of the experience we've gained. If anyone would like to take a fresh look at simplifying the code involved, it would be quite interesting. The metrics here are simpler code, fewer names, simpler documentation (both internal and external), and less code. Andrei
Sep 06 2016
On Tuesday, 6 September 2016 at 10:04:06 UTC, Andrei Alexandrescu wrote:The drawback of this is taking this in as a reader and maintainer. We have the 'text' template which calls the 'textImpl' template which calls the 'to' template which calls the 'toImpl' template which calls the 'parse' template which calls the 'FormattedWrite' template which calls the 'to' template. Not easy to find where the work is ultimately done.Yeah, this problem ultimately comes down to healthy use of DRY in Phobos in regards to string handling code. This was always the tradeoff with DRY: with small pieces of reused code being put into functions, it makes maintenance and optimization easier, but code complexity increases. While it can be a bit hard to wrap one's head around it, once I did I found that for the most part, std.conv is correct in delegating a lot of its functionality to other functions in std.conv and std.format in particular in order to stay fast.fewer names, simpler documentationI've done this a little by making all of the toImpl overloads private and therefore not show in the docs anymore. That change will appear if we have another point release some time this decade :).
Sep 07 2016
On 9/7/2016 8:46 AM, Jack Stouffer wrote:Yeah, this problem ultimately comes down to healthy use of DRY in Phobos in regards to string handling code. This was always the tradeoff with DRY: with small pieces of reused code being put into functions, it makes maintenance and optimization easier, but code complexity increases. While it can be a bit hard to wrap one's head around it, once I did I found that for the most part, std.conv is correct in delegating a lot of its functionality to other functions in std.conv and std.format in particular in order to stay fast.Consider a couple pulls I made for dup(): https://github.com/dlang/druntime/pull/1642 https://github.com/dlang/druntime/pull/1640 https://github.com/dlang/druntime/pull/1639 Three templates were removed with no loss in functionality. I'm pretty sure the number of dup() templates can be reduced still further. Consider the pattern of overloads: template foo(T) if (condition!T) { } template foo(T) if (!condition!T) { } It makes condition!T a user-facing constraint, which it should not be. A better pattern would be: template foo(T) { static if (condition!T) { } else { } }
Sep 07 2016
On 9/7/16 9:49 PM, Walter Bright wrote:On 9/7/2016 8:46 AM, Jack Stouffer wrote:This looks like a nice guideline. Good work. Pushing the roof now. -- AndreiYeah, this problem ultimately comes down to healthy use of DRY in Phobos in regards to string handling code. This was always the tradeoff with DRY: with small pieces of reused code being put into functions, it makes maintenance and optimization easier, but code complexity increases. While it can be a bit hard to wrap one's head around it, once I did I found that for the most part, std.conv is correct in delegating a lot of its functionality to other functions in std.conv and std.format in particular in order to stay fast.Consider a couple pulls I made for dup(): https://github.com/dlang/druntime/pull/1642 https://github.com/dlang/druntime/pull/1640 https://github.com/dlang/druntime/pull/1639 Three templates were removed with no loss in functionality. I'm pretty sure the number of dup() templates can be reduced still further. Consider the pattern of overloads: template foo(T) if (condition!T) { } template foo(T) if (!condition!T) { } It makes condition!T a user-facing constraint, which it should not be.
Sep 08 2016
On 9/8/2016 5:10 AM, Andrei Alexandrescu wrote:An example of doing it better, the put() template: https://github.com/dlang/phobos/blob/master/std/range/primitives.d#L295 Something we need to move away from, the 17 overloads of formatValue() in: https://github.com/dlang/phobos/blob/master/std/format.d#L1319 all that differ only in the constraint.Consider the pattern of overloads: template foo(T) if (condition!T) { } template foo(T) if (!condition!T) { } It makes condition!T a user-facing constraint, which it should not be.This looks like a nice guideline. Good work. Pushing the roof now. -- Andrei
Sep 08 2016
On Thursday, 8 September 2016 at 23:34:13 UTC, Walter Bright wrote:On 9/8/2016 5:10 AM, Andrei Alexandrescu wrote:Yay! Finally! This is exactly what I said in another recent thread: overloads should be avoided, especially if something is meant to be for all types - do it with static if inside one function, which in turn will have MUCH MUCH easier constraints (or none at all). I think overload is a most of the time useless C++ relict.An example of doing it better, the put() template: https://github.com/dlang/phobos/blob/master/std/range/primitives.d#L295 Something we need to move away from, the 17 overloads of formatValue() in: https://github.com/dlang/phobos/blob/master/std/format.d#L1319 all that differ only in the constraint.Consider the pattern of overloads: template foo(T) if (condition!T) { } template foo(T) if (!condition!T) { } It makes condition!T a user-facing constraint, which it should not be.This looks like a nice guideline. Good work. Pushing the roof now. -- Andrei
Sep 09 2016