Open rhdunn opened 3 months ago
Thanks for the separate proposal.
I’m not 100% sure I understand how 4. could look like in practice. Could you add a little example for it?
declare function fn:index-where(
$input as item()*,
$predicate as function(
$item as item(),
$position as xs:integer
) as xs:boolean
) as xs:integer* {
for $it at $pos in $input
where $predicate(item := $it, position := $pos)
return $pos
};
Here, the processor resolves $predicate
in the function body to the parameter declaration. That has a static type which defines the parameter names of the function bound to that parameter. That static type is used to resolve the named keywords.
I wonder how that would go hand in hand with the existing semantics:
fn:count#1
has the type function(item()*) as xs:integer
.function($input as item()*) as xs:integer
.function($value as item()*) as xs:integer
?This proposal introduces some static type inferencing into the language, something which we have previously avoided (except for the limited purpose of streamability analysis in XSLT). If we keep it very tightly constrained -- associating the explicitly declared type of a variable or parameter with references to that variable or parameter -- then this might be manageable. But I worry that it could easily feature-creep into something more complex.
Consider
declare function fn:index-where(
$input as item()*,
$predicate as (function(
$item as item(),
$position as xs:integer
) as xs:boolean)?
) as xs:integer* {
let $actual-predicate := $predicate otherwise true#0
for $it at $pos in $input
where $actual-predicate(item := $it, position := $pos)
return $pos
};
This isn't going to work because $actual-predicate doesn't have a type declaration providing the parameter names.
He could try:
let $actual-predicate as function(
$item as item(),
$position as xs:integer
) as xs:boolean := $predicate otherwise true#0
but is this actually usable?
To make it more usable, we have to start introducing more complex type inferencing rules.
Even without this, we're making life tougher for implementations -- for example you can't now inline a variable reference without retaining information from its type declaration. To what benefit?
It would make sense for fn:count#1
, etc. to keep the parameter names as part of the typed function test. -- This would allow the use of keyword arguments in that case as I discussed in the other thread.
I think it makes sense for function coercion rules, subsumption rules, etc. to allow different names -- otherwise there would be incompatibilities with 3.1 -- and use the names in the nearest scope.
That is, given:
let $f := fn:count#1 return $f(input := (1, 2))
the fn:count#1
expression resolves to the static type function($input as item()*) as xs:integer
as you note. The variable $f
inherits that inferred static type. And the dynamic call to $f(...)
makes use of that static type.
And given:
let $f as function($values as item()*) as xs:integer := fn:count#1
return $f(values := (1, 2))
the variable $f
provides a specific static type. And the dynamic call to $f(...)
makes use of that specific static type.
@michaelhkay I'm happy to keep this constrained, such that your $actual-predicate
example would not be able to use named parameters without the dynamic proposal from @ChristianGruen. -- This goes along the lines of your approach of introducing small, constrained features rather than trying to do everything at once.
There are two parts to this proposal:
I don't expect this specific proposal to go any/much farther than that. Doing so would require the dynamic proposal that @ChristianGruen is putting forward.
@ChristianGruen. -- This goes along the lines of your approach of introducing small, constrained features rather than trying to do everything at once.
An even smaller step (hopefully not too small) would be to introduce optional parameter names, but to utilize them only for documentation and error messages. This would address reason 1-3 of this proposal.
Closely related: #981.
I'm happy to treat 1-3 and 4 as two separate proposals.
I do think we should avoid the "thin end of the wedge" effect of static type inferencing. I don't think it would be acceptable to allow parameter names on $param(....)
but not on $param[3](....)
or $param()(....)
or $record?callback(....)
.
I agree that allowing names purely for documentation purposes seems a sensible idea.
It's hard to separate this, however, from the fact that according to the data model, a function item has parameters with names. Currently there is no way of determining what the names are, and I don't think there is anything in the language that would be any different if they didn't have names, but we should make a decision whether to retain the names and make use of them, or to drop them.
I agree that allowing names purely for documentation purposes seems a sensible idea.
It's hard to separate this, however, from the fact that > we should make a decision whether to retain the names and make use of them, or to drop them.
Now that we introduce keyword parameters with version 4, I think it would be just consistent to keep the names as well.
When defining the type of a higher-order function parameter, you cannot currently specify the names of the parameters of that higher-order function.
Allowing this can be useful for variou reasons:
Thus, you could declare e.g. index-where like this: