Macros with left-to-right dependent types
This idea is to allow macros to use the already used parameters to infer the type of the next parameters:
// Today
macro add(a, b) => a + b;
The idea would be to allow using the types of arguments to infer the type of other parameters. For example:
macro $typeof(a) add(a, $typeof(a) b)
{
return a + b;
}
It could also be expressed in a more complex way like this:
macro $Ty add($Ty a, $Ty b)
{
return a + b;
}
Where we'd do a more conventional capture.
But in this case we might perhaps do some more clear inference:
macro $Ty add([$Ty] = a, $Ty b)
{
return a + b;
}
Thinking a bit more on this: the main usefulness is not creating type constraints but rather to allow type inference and implicit conversion based on the first element. And for that reason $typeof is enough.
$typeof(a) is a dead end.
being able to access previous arguments in a macro could also be useful for automatically setting defaults based on previous params that can be overwritten manually. this is a (slightly simplified) usecase I came up with when thinking about an internal rework of my emulator:
enum Width
{
W8,
W16,
W32,
}
macro Width @width_of(#val) @const
{
$switch $typeof(#val):
$case char:
return W8;
...
$endswitch
}
macro void uint.set(&self, val, Width $tgt_width = @width_of(val))
{
$switch $tgt_width:
$case W8:
*self = (*self & ~0x000000ff) | (uint)data;
...
$endswitch
}
uint x = ...;
char c = ...;
x.set(c); // sets lowest byte of x to c
x.set(c, W16); // sets lowest byte of x to c & zeroes second lowest byte
uint y = ...;
x.set(y, W16); // sets lowest 2 bytes of x to lowest 2 bytes of y
ofc this can already be done with EMPTY_MACRO_SLOT, and can have type inference using a #expr parameter but it is a little less convenient & more confusing to work with:
// this should be equivalent to the above one
macro void uint.set(&self, val, #tgt_width = EMPTY_MACRO_SLOT) @safemacro
{
Width $tgt_width;
$switch:
$case $defined(Size.$eval($stringify(#tgt_width))):
$case @typeis(#tgt_width, Width):
$tgt_width = #tgt_width;
$case @is_empty_macro_slot(#width):
$tgt_width = @width_of(val);
$default:
$error "Invalid type for #tgt_width: " +++ $typeof(#tgt_width).nameof;
$endswitch
$switch $tgt_width:
$case W8:
*self = (*self & ~0x000000ff) | (uint)data;
...
$endswitch
}
I don't feel particularly strongly about this as there are other ways to do the same thing, but maybe it could be useful in other places as well.
It seems to be too complex in the current model. Will close this and revisit it later.