qt4cg / qtspecs

QT4 specifications
https://qt4cg.org/
Other
29 stars 15 forks source link

Editorial: optional position argument in function signature for for-each and other HOF #1514

Open fsteimke opened 1 month ago

fsteimke commented 1 month ago

The change in 4.0 for the higher order function for-each is that "the $action callback function accepts an optional position argument". But the function signature is

fn:for-each(
  $input as item()*,    
  $action as fn(item(), xs:integer) as item()*  
) as item()*

I read $action as fn(item(), xs:integer) as item()* as a function with two mandatory parameters item() and xs:integer. Since the second (position) argument should be optional, i would expect:

fn:for-each(
  $input as item()*,    
  $action as fn(item(), xs:integer?) as item()*     
) as item()*

There are several HOFs with this new optional position argument which seems to be mandatory in the function signature.

ChristianGruen commented 1 month ago

Closely related: https://github.com/qt4cg/qtspecs/issues/981.

This is due to the updated function coercion rules, which say:

If F has lower arity than T, then F is wrapped in a new function that declares and ignores the additional argument […].

In general, all parameters are now optional for this and all other callback findings; you can also supply true#0 as action argument.

JavaScript has chosen a similar approach (an example: forEach).

michaelhkay commented 1 month ago

In response to a number of issues including #981, #1136, and #1175 I'm leaning towards:

  1. Allowing parameter names in function signatures, initially with no meaning attached to them: function($item as item()*, $position as xs:integer) as xs:boolean is an alternative designation of the type function(item(), xs:integer) as xs:boolean.
  2. Marking optional parameters in a function signature. Perhaps with a ? after the name, so function($item as item(), $position? as xs:integer) as xs:boolean indicates that the $position argument is optional. If any argument is optional then all subsequent arguments must be optional. Or perhaps with the syntax function($item as item(), $position as xs:integer := ?); though the similarity of syntax to optional parameters in function declarations hides a significant difference in semantics.
  3. Dropping the "arity increasing" function coercion, and replacing it with changes to the type matching rules. Specifically, a function declared as fn($i as item()) as xs:boolean {true()} matches the type fn($item as item(), $pos? as xs:integer) as xs:boolean. As a consequence, both types fn($a) and fn($a, $b) are subtypes of fn($a, $b?).
  4. Allowing parameters of a function item to be optional, using the syntax function($item as item(), $pos as xs:integer := -1) {...} to indicate the fact
  5. Changing the rules on dynamic function calls so that optional parameters can be omitted.

This is of course just a sketch, there is a lot of detail to be worked out. But it feels feasible.

The one thing I haven't included is allowing argument keywords in dynamic function calls. That's tricky because we don't want to disallow supplying a callback function that happens to use different parameter names. However, it might be possible to solve that if function coercion performs a remapping of parameter names.