For the record, I agree that my interpretation of replacing arguments is super weird! It's just the best I could make of the current specification that at least does what I would expect for unary aggregate functions. My (somewhat) more sane proposal would be that for INTERMEDIATE_TO_X invocations the actual argument list is modified as follows:
- enumeration, type, and constant (well, "scalar" would be more accurate, see paragraph at end) value arguments are retained;
- non-constant (vector) value arguments are specified as type arguments instead;
- an additional argument is implicitly appended to the argument list, that must match the derived intermediate type for the above arguments.
The conversion from value to type argument is necessary for functions that use the argument type in their derivation expressions. Here's a very hypothetical example to illustrate:
weird_aggregate_fn(decimal<A,B>) -> decimal<A+6,B-6>
intermediate: decimal<A+3,B-3>
If we were to bind this using just its intermediate type, we would have to support expression evaluation reversal, i.e. you'd have to match a number to A+3, essentially rewriting it to a subtraction. You could do that for additions and subtractions I guess, but not for functions in general. The much, MUCH more straightforward approach is to just require the producer to write the original decimal<A,B> as a type argument. To complete the example with all the effective signatures:
INITIAL_TO_RESULT: weird_aggregate_fn:dec(decimal<A,B>) -> decimal<A+6,B-6>
INITIAL_TO_INTERMEDIATE: weird_aggregate_fn:dec(decimal<A,B>) -> decimal<A+3,B-3>
INTERMEDIATE_TO_INTERMEDIATE: weird_aggregate_fn:dec([type]decimal<A,B>, decimal<A+3,B-3>) -> decimal<A+3,B-3>
INTERMEDIATE_TO_RESULT: weird_aggregate_fn:dec([type]decimal<A,B>, decimal<A+3,B-3>) -> decimal<A+6,B-6>
In all cases we can now match A and B trivially and derive the remaining types per the normal rules.
Alternative proposal: just require that the YAML files specify what the intermediate-input argument list is explicitly. The above function would still be problematic to write that way, though; you'd still need the type argument.
I'm not following what you mean here exactly. Can you give an example?
Semi-related: when I was talking to Phillip the other day we realized there is another problem with aggregate function arguments that might be adding to the confusion here, namely what it means for an aggregate argument to be constant or be a literal. I always interpreted constant aggregate function arguments to be scalar, i.e., the function interprets them as an input to the aggregate as a whole. An example could be a regular expression string that's only compiled once for the entire aggregate, or the initialization value for a fold operation (if we would support higher-order functions). Such arguments would logically need to be repeated for INTERMEDIATE_TO_RESULT invocations (at least the regex, I guess a fold isn't decomposable to begin with so it might not be the best example in this context). Only vector arguments should logically be replaced by the intermediate type.