digitalmars.D - Why I love D: function parameter reification
- H. S. Teoh (72/72) Jun 08 2022 Given the volume of negative posts about D around these parts, I thought
- forkit (9/9) Jun 08 2022 On Wednesday, 8 June 2022 at 21:47:16 UTC, H. S. Teoh wrote:
- H. S. Teoh (19/31) Jun 08 2022 LOL... I've been around here since 2011, it's obvious that certain
- forkit (16/51) Jun 08 2022 Well, look, in the end, the argument for co-operative mutability
- Adam D Ruppe (4/7) Jun 08 2022 You probably don't need that either.
- Paolo Invernizzi (3/10) Jun 09 2022 I still think that being able to reflect ddoc comments will be a
- Adam D Ruppe (5/7) Jun 09 2022 Yes, indeed, I've requested this several times, it is useful for
- bauss (7/15) Jun 09 2022 I really can't see any reason to reject it, it's not like it'll
- Adam D Ruppe (12/18) Jun 09 2022 The stated reason was an ideology that someone might misuse it to
- bauss (3/5) Jun 09 2022 I mean you can abuse and misuse a lot of things, you just trust
- H. S. Teoh (8/25) Jun 09 2022 In theory, you could insert code in your ddoc and use introspection to
- Steven Schveighoffer (5/13) Jun 09 2022 An update to ddoc to also parse e.g. `@ddoc(string)` as ddoc comments
- Adam Ruppe (11/15) Jun 09 2022 Yeah, I did that for some special purposes already (it is nice to
- Andrey Zherikov (5/18) Jun 08 2022 If you change your functions to callable structs moving function
Given the volume of negative posts about D around these parts, I thought I'd offer some counterpoint: why I love D. I'm planning to post this every now and then, as a sort of informal series. Today, I grappled with the following situation: I have a bunch of functions with parameters that perform certain operations, that I want to expose to the user via the program's command-line arguments. Also, for maximum user-friendliness, the program's help text should accurately describe each function (treated as a subcommand on the command line) and what parameters each takes. The traditional solution, of course, is simple: write a showHelp() function that prints some text describing each function and its parameters, then in main() write a big switch statement over function name, and each case block parses the program arguments, converts them to a suitable form, and invokes the function. It's not a bad solution, but involves a lot of tedious work that, because of their repetitious nature, is liable to human error. Every time I added a new function, I have to change two other places in the code (update the help text, add a new case block). Raise your hand, whoever has encountered out-of-sync/outdated help texts in programs. :-P So I sought for a better solution, in the spirit of DRY: put said functions into a wrapper struct, mark them static, and add a string UDA that will be introspected by showHelp() to generate a list of functions that's guaranteed to be up-to-date, and by main() to generate the requisite case blocks. The help text part is simple: just introspect the wrapper struct, extract the function name, the string UDA for the description, and the list of parameter names, print them out. Easy. The case blocks are a little trickier. It would have been simple if every function had the same set of parameters: then it's just a matter of mixing in the function name in a standard call. But what if each function had a *different* set of parameters? My initial attempt was to iterate over the parameters and generate code for parsing each one, assign them to temporary variables, then create a list containing the list of temporary variables to use as a mixin to pass them all to the target function. Would work, but would involve a lot of hairy, ugly code. And then inspiration struck: why do I need to individually declare those variables (and invent names for each one)? I don't need to. I can get a hold of the function's parameters as a parameter tuple using `is(typeof(func) Params : __parameters)`, then declare a compound variable with this parameter tuple as its type: void main(string[] args) { ... static if (is(typeof(myfunc) Params : __parameters)) { Params funcArgs; // use a single name for ALL function arguments // Now populate the arguments by converting command-line // arguments to the right types: foreach (i, T; Params) { // std.conv.to is Da Bomb import std.conv : to; funcArgs[i] = args[i+2].to!T; } // To call the function, we just pass the entire // aggregate to it in one shot: myfunc(funcArgs); // thanks to the magic tuple // type, this auto-expands // funcArgs into multiple // arguments } } The compound variable 'funcArgs' is a reification of the target function's arguments; this allows us to manipulate it like a pseudo-array in the loop that parses command-line arguments. We don't need to construct any mixins involving cumbersome parameter lists! (Of course, in the actual code a mixin is still needed in order to bind `myfunc` to the actual target function, which is iterated over by name.) I couldn't even begin to imagine how to pull off something like this in C++... for sure, it will be NOWHERE near as elegant as the above. D r0x0rs!!! T -- War doesn't prove who's right, just who's left. -- BSD Games' Fortune
Jun 08 2022
On Wednesday, 8 June 2022 at 21:47:16 UTC, H. S. Teoh wrote:as I recall, it was *your* post about "Using closure in function scope to make "real" private class members" that started this whole thing. ;-) Sadly, this idea is *always* responded to, by others, in a typical passive/aggressive manner (which is what causes all the controversy): https://medium.com/the-mission/5-tactics-used-by-passive-aggressive-arguers-and-the-best-forms-of-defense-42a9348b60ed
Jun 08 2022
On Wed, Jun 08, 2022 at 10:02:01PM +0000, forkit via Digitalmars-d wrote:On Wednesday, 8 June 2022 at 21:47:16 UTC, H. S. Teoh wrote:LOL... I've been around here since 2011, it's obvious that certain personages are around for the sole purpose of stirring up the mud. Others are good faith complainants who have been struggling with long-term unresolved issues (sometimes I'm among them). Put these two together, and this is what you get. :-/ If one tries hard enough, one can always find *something* to complain about. For example, in the code I posted in this thread, there's that is(... : __parameters) construct which has some, shall we say, quirky, behaviours, about which I wrote years ago: https://forum.dlang.org/thread/vpjpqfiqxkmeavtxhyla forum.dlang.org If you really wanted to, that thread could be the basis of another interminable complaint thread about is(...). Instead, I found something to like about it, which is the whole point of *this* thread. What can I say? It's one of those glass-half-full vs. glass-half-empty scenarios. It speaks more about the person(s) than about the D language itself. :-D T -- Life would be easier if I had the source code. -- YHLas I recall, it was *your* post about "Using closure in function scope to make "real" private class members" that started this whole thing. ;-) Sadly, this idea is *always* responded to, by others, in a typical passive/aggressive manner (which is what causes all the controversy): https://medium.com/the-mission/5-tactics-used-by-passive-aggressive-arguers-and-the-best-forms-of-defense-42a9348b60ed
Jun 08 2022
On Wednesday, 8 June 2022 at 23:06:52 UTC, H. S. Teoh wrote:On Wed, Jun 08, 2022 at 10:02:01PM +0000, forkit via Digitalmars-d wrote:Well, look, in the end, the argument for co-operative mutability for code within a module, is valid - in some cases. In some cases, it's not valid. Both points of view are valid. So, yes, it's a glass half full...thing. There is something to 'like' about how the D module implements private, and there is also something to 'not like' about it. That only one side can be correct in this argument, is (as Spock would say).. illogical. D is missing the feature I find to be one of the most important features in my design considerations. That's worth speaking up about. So I'd encourage everyone to 'speak up' about the features that are important to them. Life is change. No change. No life.On Wednesday, 8 June 2022 at 21:47:16 UTC, H. S. Teoh wrote:LOL... I've been around here since 2011, it's obvious that certain personages are around for the sole purpose of stirring up the mud. Others are good faith complainants who have been struggling with long-term unresolved issues (sometimes I'm among them). Put these two together, and this is what you get. :-/ If one tries hard enough, one can always find *something* to complain about. For example, in the code I posted in this thread, there's that is(... : __parameters) construct which has some, shall we say, quirky, behaviours, about which I wrote years ago: https://forum.dlang.org/thread/vpjpqfiqxkmeavtxhyla forum.dlang.org If you really wanted to, that thread could be the basis of another interminable complaint thread about is(...). Instead, I found something to like about it, which is the whole point of *this* thread. What can I say? It's one of those glass-half-full vs. glass-half-empty scenarios. It speaks more about the person(s) than about the D language itself. :-D Tas I recall, it was *your* post about "Using closure in function scope to make "real" private class members" that started this whole thing. ;-) Sadly, this idea is *always* responded to, by others, in a typical passive/aggressive manner (which is what causes all the controversy): https://medium.com/the-mission/5-tactics-used-by-passive-aggressive-arguers-and-the-best-forms-of-defense-42a9348b60ed
Jun 08 2022
On Wednesday, 8 June 2022 at 21:47:16 UTC, H. S. Teoh wrote:(Of course, in the actual code a mixin is still needed in order to bind `myfunc` to the actual target function, which is iterated over by name.)You probably don't need that either. But yeah getting udas off function parameters is a bit... fun, but it sure does allow a lot of cool things.
Jun 08 2022
On Wednesday, 8 June 2022 at 23:09:49 UTC, Adam D Ruppe wrote:On Wednesday, 8 June 2022 at 21:47:16 UTC, H. S. Teoh wrote:I still think that being able to reflect ddoc comments will be a huge win ... a-la-python-"__doc__"(Of course, in the actual code a mixin is still needed in order to bind `myfunc` to the actual target function, which is iterated over by name.)You probably don't need that either. But yeah getting udas off function parameters is a bit... fun, but it sure does allow a lot of cool things.
Jun 09 2022
On Thursday, 9 June 2022 at 08:19:05 UTC, Paolo Invernizzi wrote:I still think that being able to reflect ddoc comments will be a huge win ... a-la-python-"__doc__"Yes, indeed, I've requested this several times, it is useful for a bunch of things like this. There were PRs adding `__traits(docComment, symbol)` to get it too, but rejected by people who don't use D in the real world. Alas.
Jun 09 2022
On Thursday, 9 June 2022 at 11:39:35 UTC, Adam D Ruppe wrote:On Thursday, 9 June 2022 at 08:19:05 UTC, Paolo Invernizzi wrote:I really can't see any reason to reject it, it's not like it'll impact anything at all really. But it really would be useful for tooling, error messages, validation etc. Could in theory be used to create automated unittests by extracting code examples from the docs.I still think that being able to reflect ddoc comments will be a huge win ... a-la-python-"__doc__"Yes, indeed, I've requested this several times, it is useful for a bunch of things like this. There were PRs adding `__traits(docComment, symbol)` to get it too, but rejected by people who don't use D in the real world. Alas.
Jun 09 2022
On Thursday, 9 June 2022 at 11:43:36 UTC, bauss wrote:I really can't see any reason to reject it, it's not like it'll impact anything at all really.The stated reason was an ideology that someone might misuse it to sneak semantic info into comments. The compiler implementation also discards them unless you use the -D switch, but that'd be fairly easy to fix (or even letting it stay blank unless you passed the switch would probably be acceptable too) so implementation shouldn't be a big deal, but then we're told that'd increase memory usage and compile times. Cuz I'm sure some text comments are a big concern.But it really would be useful for tooling, error messages, validation etc. Could in theory be used to create automated unittests by extracting code examples from the docs.Yeah, or even just pulling in for for wrapped script languages, auto-generated command line help or web site forms. Lots of nice little things.
Jun 09 2022
On Thursday, 9 June 2022 at 11:49:32 UTC, Adam D Ruppe wrote:The stated reason was an ideology that someone might misuse it to sneak semantic info into comments.I mean you can abuse and misuse a lot of things, you just trust users not to do it and when they do it then let them deal with it.
Jun 09 2022
On Thu, Jun 09, 2022 at 11:43:36AM +0000, bauss via Digitalmars-d wrote:On Thursday, 9 June 2022 at 11:39:35 UTC, Adam D Ruppe wrote:In theory, you could insert code in your ddoc and use introspection to mix it into the program and execute it at runtime.On Thursday, 9 June 2022 at 08:19:05 UTC, Paolo Invernizzi wrote:I really can't see any reason to reject it, it's not like it'll impact anything at all really.I still think that being able to reflect ddoc comments will be a huge win ... a-la-python-"__doc__"Yes, indeed, I've requested this several times, it is useful for a bunch of things like this. There were PRs adding `__traits(docComment, symbol)` to get it too, but rejected by people who don't use D in the real world. Alas.But it really would be useful for tooling, error messages, validation etc. Could in theory be used to create automated unittests by extracting code examples from the docs.Yes, this would be an awesome application. Though, for the most part, it has already been covered by ddoc'd unittest blocks. T -- Freedom: (n.) Man's self-given right to be enslaved by his own depravity.
Jun 09 2022
On 6/9/22 7:39 AM, Adam D Ruppe wrote:On Thursday, 9 June 2022 at 08:19:05 UTC, Paolo Invernizzi wrote:An update to ddoc to also parse e.g. ` ddoc(string)` as ddoc comments would be fine. Allowing actual comments to affect the compilation of code is not fine. -SteveI still think that being able to reflect ddoc comments will be a huge win ... a-la-python-"__doc__"Yes, indeed, I've requested this several times, it is useful for a bunch of things like this. There were PRs adding `__traits(docComment, symbol)` to get it too, but rejected by people who don't use D in the real world. Alas.
Jun 09 2022
On Thursday, 9 June 2022 at 16:39:44 UTC, Steven Schveighoffer wrote:An update to ddoc to also parse e.g. ` ddoc(string)` as ddoc comments would be fine.Yeah, I did that for some special purposes already (it is nice to have your own doc generator). But doc(q"( )") is quite a bit wordier than /++ +/ lolAllowing actual comments to affect the compilation of code is not fine.Oh please.
Jun 09 2022
On Wednesday, 8 June 2022 at 21:47:16 UTC, H. S. Teoh wrote:Today, I grappled with the following situation: I have a bunch of functions with parameters that perform certain operations, that I want to expose to the user via the program's command-line arguments. Also, for maximum user-friendliness, the program's help text should accurately describe each function (treated as a subcommand on the command line) and what parameters each takes. The traditional solution, of course, is simple: write a showHelp() function that prints some text describing each function and its parameters, then in main() write a big switch statement over function name, and each case block parses the program arguments, converts them to a suitable form, and invokes the function.If you change your functions to callable structs moving function arguments to struct members then you can get command line parsing, help text generation and much more with just few lines of code using [argparse](https://code.dlang.org/packages/argparse)
Jun 08 2022