Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function declarations: static and dynamic context for default parameter values #256

Closed
michaelhkay opened this issue Nov 18, 2022 · 11 comments
Labels
Bug Something that doesn't work in the current specification XPath An issue related to XPath XQuery An issue related to XQuery XSLT An issue related to XSLT

Comments

@michaelhkay
Copy link
Contributor

In the new text for default values on parameters in XQuery function declarations, we don't say clearly what the static context for the default value expression is. In particular we don't say that it excludes the other parameters of the function. The XSLT spec has similar (though slightly different) shortcomings.

There's a slight complication in that we say the dynamic context for the default value expression is the dynamic context of the function call. But what if the default value is a variable reference $x? Statically, this will be (presumably, though we don't currently say) be bound to a global variable $x. Now, we say (under "dynamic context") that the dynamic "variable values" contains the same [expanded QNames] as the [in-scope variables] in the [static context] for the expression. But, the static context for the default value expression and the static context for the function call have different in-scope variables and they must therefore have different variable values in the dynamic context, so it's wrong to say that the dynamic context for the default value expression is the same as that of the function call.

My first instinct would be to restrict the default value to being what XSLT calls a "static expression" (this isn't defined in XQuery, but it could be defined easily enough). However, that would disallow using "." as the default value expression, which is something we wanted to permit.

The next option would be to say that the dynamic context for a default value expression is the same as the dynamic context of the function call except that "variable values" contains bindings for global variables only. This feels rather kludgey, but it's workable in principle. (It's worth noting, and might be worth noting in the spec, that in XQuery all components of the dynamic context except the focus and the variable values are typically immutable within an execution scope. The same isn't true in XSLT, where we have additional dynamic context components like regex-group() and current-output-uri() to worry about.).

@michaelhkay
Copy link
Contributor Author

michaelhkay commented Nov 18, 2022

We should perhaps think ahead and ask what we would want to do if we extend inline functions to allow default parameter values. How would we bind any variable references appearing in the default value expression in this case?

One solution to the problem might be to say that when the user writes a default value expression as

$arg := . + $var

the actual interpretation is that we create a default value function defined as ->{. + $var), and at the point of the function call, the default value to be used is obtained by calling this function with the context item (from the dynamic context of the function call) as the supplied argument. This seems to get around the problem that the static and dynamic context of the expression are disconnected, while still allowing reference to the context item of the caller.

@dnovatchev
Copy link
Contributor

the actual interpretation is that we create a default value function defined as ->{. + $var), and at the point of the function call, the default value to be used is obtained by calling this function with the context item (from the dynamic context of the function call) as the supplied argument

I clearly remember proposing exactly this in the past few days -- must see where ...

@michaelhkay michaelhkay added Bug Something that doesn't work in the current specification XQuery An issue related to XQuery XSLT An issue related to XSLT labels Nov 18, 2022
@dnovatchev
Copy link
Contributor

dnovatchev commented Nov 18, 2022

the actual interpretation is that we create a default value function defined as ->{. + $var), and at the point of the function call, the default value to be used is obtained by calling this function with the context item (from the dynamic context of the function call) as the supplied argument

I clearly remember proposing exactly this in the past few days -- must see where ...

Found it: here

@dnovatchev dnovatchev added XPath An issue related to XPath and removed XQuery An issue related to XQuery labels Nov 18, 2022
@michaelhkay
Copy link
Contributor Author

I've been giving further thought to this.

We have a number of built-in functions where the default for the $collation argument is "the default collation in the static context of the function call". (Recall that in XSLT, the default collation can vary from one instruction to another, using the [xsl:]default-collation attribute.)

While it's intuitive to allow the syntax <xsl:param name="collation" select="default-collation()"/> in an xsl:function signature, it's hard to give this the right semantics: anywhere else, this would be the default collation in the static context of the xsl:param element, not the static context of the function caller. The proposal in an earlier comment to interpret this as an expression to be evaluated in the context of the caller leaves many questions unanswered, because we don't have a mechanism for a function to discover properties of the static context of the caller. (There's also a compatibility issue, because <xsl:param name="collation" select="default-collation()"/> is already legal for an xsl:template parameter, and it gives you the default collation of the callee. )

The same is true for a default based on the dynamic context: the idea of making the default a function that uses the dynamic context of the caller doesn't work, because there's no way of writing a user-defined function that returns the context item of the caller. There's a circularity here: accessing properties of the static or dynamic context of the caller is currently a magic capability available only to built-in functions, and I think we can only provide the same capability to user-defined functions by invoking similar magic.

My next idea is to interpret the default value as a macro, causing a function call to be statically expanded. So specifying "." as the default value for $collation causes a function call f() that doesn't specify a value for $collation to be lexically expanded to f(default-collation=$collation). I don't particularly like this as a general solution; it doesn't play well with function references and partial function application, and it's very messy if the default value expression contains variable references or function calls, for example.

At present a function item referencing a context-dependent function always contains a binding of the context properties that it depends on. For example default-collation#1 binds the context at the point where the function reference is evaluated. We could consider allowing a function item to have an unbound context, for example default-collation##1 gives you a function that uses the context of the caller when the function item is eventually invoked. But this seems a lot of complexity for a simple requirement.

A pragmatic solution to the dilemma might be:

  • Specifying "." as the default value of a function parameters has a magic meaning: it means use the context item of the caller
  • Accessing any other properties from the static or dynamic context of the caller is a privilege reserved to system functions; there is no user-accessible syntax to achieve it
  • Except for the special case of ".", the default value expression is evaluated in the static and dynamic context of the function declaration.

@michaelhkay michaelhkay added the XQuery An issue related to XQuery label Feb 17, 2023
@michaelhkay
Copy link
Contributor Author

I propose to resolve this as follows. XQuery and XSLT might use slightly different wording, but the technical effect is the same.

The static context for the default value expression is the same as the static context for an initializing expression of a global variable except that (a) in-scope-variables is empty, (b) context item static type is item().

The dynamic context for the default value expresion is the dynamic context of the function call or function reference, except that available documents, available text resources, available collections, default collection, available URI collections, and default URI collection are all empty.

@michaelhkay
Copy link
Contributor Author

I'm really struggling to see how to make default-collation() work as a default value, given that it's part of the static context rather than the dynamic context. (This is much more of a problem for XSLT than XQuery, as XSLT allows the default collation to be set in a fine-grained way using the xsl:default-collation attribute.) And of course it's not just the default collation itself, it's any expression that uses the default collation, for example a default parameter value that uses the eq operator. The only way I can think to do it is to provide some other function such as default-collation-of-caller() - but that's horrible.

I think I'm going to recommend scaling back our ambitions here, and propose that default values for functions should be evaluated in the same way as initialisers for global variables. This loses all ability to make the default depend on the context of the caller of the function.

@michaelhkay
Copy link
Contributor Author

michaelhkay commented Feb 27, 2023

One disadvantage is that the notation we use in functions and operators, for example declaring a parameter as $collation as xs:string := default-collation() becomes somewhat ill-defined.

Perhaps we could formalise the "magic" as follows: we define a function fn:caller-context(). This function is present in the static context of a function parameter default value expression, and nowhere else. It returns a map whose fields are zero-arity functions. fn:caller-context()?context-item() returns the caller's context item. fn:caller-context()?default-collation() return's the caller's default collation. Perhaps allow ?position() and ?last() and ?in-scope-namespaces() as well. To define a parameter whose default value is the context item of the caller, you now define it as $input := caller-context()?context-item() rather than $input := ..

(Alternative: rather than a magic function fn:caller-context(), we could define a magic variable $fn:caller-context. There are precedents for both. Perhaps the variable is better, because we don't normally exercise fine-grained control over what functions are available.)

@ChristianGruen
Copy link
Contributor

Some feedback from the XQuery perspective:

I would be satisfied if we restricted parameter default values to literals. It feels confusing to me that something that is called a “default value” can be dynamic.

Perhaps it’s still tolerable though to use . as an alias for the context item in the function signatures of the XQFO function definitions (or we use some special syntax). Occurrences of fn:default-collation() could be replaced with the empty sequence, and the rules could indicate that the default collation is to be used if an empty sequence is supplied as collation (this is related to #343).

I see an analogy with annotations: Annotation values are limited to literals. With RESTXQ, for example, annotations are also used to define default values. If we lift the restriction, it seems consistent to me to treat annotation values and parameter default values similarly.

By the way, It’s inconvenient that there’s currently no way to supply Boolean annotation values. Maybe we could allow certain alias constructs in the syntax for both annotations and parameter defaults:

  • true(), false(): boolean items
  • (): empty sequence
  • .: context item
  • ~: context value

@michaelhkay
Copy link
Contributor Author

My starting point here has always been that user functions should be able to do anything that built-in functions can do (or at any rate, anything that we find it useful for them to do).

Note that XSLT allows the default collation to be changed with much finer granularity than XQuery, so it's much more likely that the default collation for a function call will be different from the default collation of the function declaration.

@ChristianGruen
Copy link
Contributor

ChristianGruen commented Mar 5, 2023

It could be helpful to have one or two examples in the specification for function items. Is the following expression correct?

let $filter := function(
  $item := caller-context()?context-item()
) {
  $item = 'Jupiter'
}
return //planet[$filter()]

Next, the naming is pretty technical. Could caller-context()?context-item() possibly be shortened to context()?item()?

michaelhkay added a commit to michaelhkay/qtspecs that referenced this issue Mar 10, 2023
@ChristianGruen ChristianGruen changed the title XQuery and XSLT function declarations: static and dynamic context for default parameter values Function declarations: static and dynamic context for default parameter values Apr 27, 2023
michaelhkay added a commit to michaelhkay/qtspecs that referenced this issue May 19, 2023
@michaelhkay
Copy link
Contributor Author

Resolved by PR #512

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something that doesn't work in the current specification XPath An issue related to XPath XQuery An issue related to XQuery XSLT An issue related to XSLT
Projects
None yet
Development

No branches or pull requests

3 participants