If we really want to lean into the "kinda like they were macros", then macro fn
makes a lot of sense.
Semantically, I would describe macro fn
as expanding similar to the following:
macro fn mul_add(x, y, z) { x * y + z }
mul_add(f(), g(), h())
// roughly semantically equivalent to
match (f(), g(), h()) {
(x, y, z) => { x * y + z }
}
though with mul_add
(maybe? maybe not?) having its own call stack entry, its own monomorphized function site rather than strictly being always 100% inlined.
If the argument patterns are partial patterns (e.g. WonkaChocolateFactory { golden_tickets, .. }
), then this would semantically define macro fn
to disjointly capture the named fields only.
However, with that definition with the early-evaluation rebinding, the whole binding is always taken, rather than field wise usage being possible (without a partial pattern in the signature). This is IIUC a fundamental limitation of the surface language as it exists today; there's no way to both only evaluate a (place) expression once and do disjoint captures with its usages.
If we do add macro fn
/inline fn
/whatever, I expect the function-interface semantics where the arguments are evaluated once to be met (unlike with bang-macros, which can freely do expression rewriting), so I'm not exactly sure how macro fn
would exactly help the disjoint captures problem without supporting disjoint captures across a normal function interface first.