www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Trying to understand map being a template

reply =?UTF-8?B?Tm/DqQ==?= Falzon <falzon.noe gmail.com> writes:
On the subject of `map` taking the function as template 
parameter, I was surprised to see it could still be used with 
functions determined at runtime, even closures, etc. I am trying 
to understand the mechanism behind it.

The commented out line causes the error that `choice(funcs)` 
cannot be determined at compile time, which is fair, but how is 
it not the same case for `func` two lines above? I thought it 
might be because the functions are literals visible at compile 
time, but the closure makes that dubious.

```
import std.stdio;
import std.algorithm;
import std.random;

void main()
{
	int r = uniform(0,100);

	int delegate(int)[] funcs = [
		x => x * 2,
		x => x * x,
		x => 3,
		x => x * r		// Closure
	];

	auto foo = [1,2,3,4,5];

	foreach(i; 0..10)
	{
		// This is fine:
		auto func = funcs.choice;
		writeln(foo.map!func);

		// This is not?
		// writeln(foo.map!(funcs.choice));
	}
}
```

In fact, how can the template be instantiated at all in the 
following example, where no functions can possibly be known at 
compile time:

```
auto do_random_map(int delegate(int)[] funcs, int[] values)
{
	auto func = funcs.choice;
	return values.map!func;
}
```

Thank you for the insights!
Jan 05
next sibling parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Fri, Jan 05, 2024 at 08:41:53PM +0000, Noé Falzon via Digitalmars-d-learn
wrote:
 On the subject of `map` taking the function as template parameter, I
 was surprised to see it could still be used with functions determined
 at runtime, even closures, etc. I am trying to understand the
 mechanism behind it.
That's simple, if the argument is a runtime function, it is treated as a function pointer (or delegate). [...]
 In fact, how can the template be instantiated at all in the following
 example, where no functions can possibly be known at compile time:
 
 ```
 auto do_random_map(int delegate(int)[] funcs, int[] values)
 {
 	auto func = funcs.choice;
 	return values.map!func;
 }
 ```
[...] The argument is taken to be a delegate to be bound at runtime. In the instantiation a shim is inserted to pass along the delegate from the caller's context. T -- Creativity is not an excuse for sloppiness.
Jan 05
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 5 January 2024 at 20:41:53 UTC, Noé Falzon wrote:
 In fact, how can the template be instantiated at all in the 
 following example, where no functions can possibly be known at 
 compile time:

 ```
 auto do_random_map(int delegate(int)[] funcs, int[] values)
 {
 	auto func = funcs.choice;
 	return values.map!func;
 }
 ```

 Thank you for the insights!
It works for the same reason this example works: ```d void printVar(alias var)() { import std.stdio; writeln(__traits(identifier, var), " = ", var); } void main() { int x = 123; int y = 456; printVar!x; // x = 123 printVar!y; // y = 456 x = 789; printVar!x; // x = 789 } ```
Jan 06
parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Saturday, 6 January 2024 at 17:57:06 UTC, Paul Backus wrote:
 On Friday, 5 January 2024 at 20:41:53 UTC, Noé Falzon wrote:
 In fact, how can the template be instantiated at all in the 
 following example, where no functions can possibly be known at 
 compile time:

 ```
 auto do_random_map(int delegate(int)[] funcs, int[] values)
 {
 	auto func = funcs.choice;
 	return values.map!func;
 }
 ```

 Thank you for the insights!
It works for the same reason this example works: ```d void printVar(alias var)() { import std.stdio; writeln(__traits(identifier, var), " = ", var); } void main() { int x = 123; int y = 456; printVar!x; // x = 123 printVar!y; // y = 456 x = 789; printVar!x; // x = 789 } ```
To clarify, what this actually compiles to is: ```d void main() { int x = 123; int y = 456; void printVar_x() { import std.stdio; writeln(__traits(identifier, x), " = ", x); } void printVar_y() { import std.stdio; writeln(__traits(identifier, y), " = ", y); } printVar_x; printVar_y; x = 789; printVar_x; } ``` Which lowers to: ```d struct mainStackframe { int x; int y; } void printVar_main_x(mainStackframe* context) { import std.stdio; writeln(__traits(identifier, context.x), " = ", context.x); } void printVar_main_y(mainStackframe* context) { import std.stdio; writeln(__traits(identifier, context.y), " = ", context.y); } void main() { // this is the only "actual" variable in main() mainStackframe frame; frame.x = 123; frame.y = 456; printVar_main_x(&frame); printVar_main_y(&frame); frame.x = 789; printVar_main_x(&frame); } Same with `map`.
Jan 06
parent =?UTF-8?B?Tm/DqQ==?= Falzon <falzon.noe gmail.com> writes:
Thank you very much for your answers. The point I had mostly 
misunderstood was alias template parameters, which make this 
possible.
Jan 09