Jump to page: 1 2
Thread overview
Why I love D: function parameter reification
Jun 08, 2022
H. S. Teoh
Jun 08, 2022
forkit
Jun 08, 2022
Adam D Ruppe
Jun 09, 2022
Paolo Invernizzi
Jun 09, 2022
Adam D Ruppe
Jun 09, 2022
bauss
Jun 09, 2022
Adam D Ruppe
Jun 09, 2022
bauss
Jun 09, 2022
H. S. Teoh
Jun 09, 2022
Adam Ruppe
Jun 08, 2022
Andrey Zherikov
June 08, 2022
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
June 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

June 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.
June 08, 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

June 09, 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:
>> (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.

I still think that being able to reflect ddoc comments will be a huge win ... a-la-python-"__doc__"
June 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.
June 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 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.

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.
June 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.
June 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.
June 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:
> > 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.
> 
> I really can't see any reason to reject it, it's not like it'll impact anything at all really.

In theory, you could insert code in your ddoc and use introspection to mix it into the program and execute it at runtime.


> 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.
« First   ‹ Prev
1 2