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

Support named arguments on dynamic function calls #160

Closed
rhdunn opened this issue Sep 27, 2022 · 8 comments
Closed

Support named arguments on dynamic function calls #160

rhdunn opened this issue Sep 27, 2022 · 8 comments
Labels
Feature A change that introduces a new feature Propose Closing with No Action The WG should consider closing this issue with no action XPath An issue related to XPath

Comments

@rhdunn
Copy link
Contributor

rhdunn commented Sep 27, 2022

This proposal extends #159 to dynamic functions. For example, let $x := fn:tokenize#3 return $x("abcbdCef", pattern: "c", flags: "i").

Motivation

This is the same as the motivation for #159, but in the context of a) inline functions, and b) named function references. This is specifically useful for XPath expressions where defining the functions in XSLT is cumbersome and makes it harder to share the implementation between XSLT and XQuery.

Notes

If the resolved function (static or inline) from the dynamic expression does not have a parameter with the specified name, a suitable error should be raised.

@rhdunn rhdunn added XPath An issue related to XPath Feature A change that introduces a new feature labels Sep 27, 2022
@michaelhkay
Copy link
Contributor

In order to achieve this, I think that parameter names need to become part of the function type system. For example if you write a function my:filter that expects an argument of type function(int, int) as int, then one caller might supply a function with parameter names (x, y) while another caller supplies a function with parameter names (a, b). So within my:filter you have no way of knowing what the parameter names actually are. To make this work, the argument type would have to include parameter names so the type becomes function($p as int, $q as int), and within my:filter you can then refer to the arguments as p and q, with these being somehow mapped to the parameter names of the actual supplied function. (Or perhaps you would require the supplied function to use the expected parameter names? But that seems hopelessly impractical.) To make this happen, you're opening up the type system for functions and the substitutability rules.

@rhdunn
Copy link
Contributor Author

rhdunn commented Sep 28, 2022

In the Data Model 3.1, 2.8.1 Functions (https://www.w3.org/TR/xpath-datamodel-31/#function-items) specifies the following function property:

parameter names (xs:QName*): A list of distinct names, one for each of the function's parameters.

As such, function names should already be part of the type system.

The way I was thinking this would work is that it would use the resolved function's parameter names. Here, if you tried using names that did not exist on the resolved function then that would be a dynamic error.

Note: Doing it that way wouldn't require changing FunctionTest, and would make the behaviour and logic for typed and untyped variables the same.

@michaelhkay
Copy link
Contributor

michaelhkay commented Sep 28, 2022

I've never quite understood why parameter names are retained in dynamic function values, but they aren't part of the type system and play no role in deciding whether a particular function conforms to a particular type. In a FunctionTest, the parameters are unnamed. If you write a higher-order function that requires a function to be supplied as an argument, you can't constrain what the parameter names of the supplied function should be, and you therefore have no way of knowing what keywords should be used when calling the function. Your proposal that you should get an error if you use the wrong name seems to break substitutability, by putting a constraint on what functions can be supplied that's outside the scope of the type system. If I'm a caller of a higher-order function and I need to supply a function as a parameter, I want the signature of the higher-order function to tell me whether a particular function is acceptable.

I can imagine a solution where we add parameter names to the FunctionTest syntax, and function coercion changes the names of the parameters in the supplied function to the names used in the FunctionTest. But it's feels pretty complex to specify, and leads to a lot of questions for example whether the subtype relation for functions has to take parameter names into account.

@dnovatchev
Copy link
Contributor

If you write a higher-order function that requires a function to be supplied as an argument, you can't constrain what the parameter names of the supplied function should be, and you therefore have no way of knowing what keywords should be used when calling the function.

Can't we introduce (in addition to using the specific argument names of a function) a "Standard Arguments Naming", let's say: "$arg1", "$arg2", ..., "$argN"?

This can then be used for referring to the arguments of a function passed as a parameter to us, that we don't know anything about its specific argument names.

Here the $ is the first character in the standard keyword name, that is in the actual function call we will have (for example):

$f(3, $$arg2 := 5, $$arg4 := 7)

In this example, the first argument's value is passed positionally, the 3rd argument's value is the default one, so it is not passed, and we specify values to the 2nd and 4th arguments by using their standard keyword names, because $f is passed to us as a parameter and we cannot know its actual/specific argument names.

@michaelhkay
Copy link
Contributor

I'm coming round to Dimitre's approach here. The only alternatives I can come up with are (a) allowing parameter names to be defined in the type signature, so that parameters are renamed during function coercion, or (b) not allowing keywords in dynamic function calls at all. The first option is a lot of syntactic machinery for a facility that will be very rarely used, and the second creates an unwelcome asymmetry in the capabilities of dynamic function calls versus static function calls.

So my proposal is that whenever function coercion is used to convert a supplied function item to a required type, the parameters should be renamed. Since we aren't using $ signs in argument keywords, I propose we rename them simply to arg1, arg2 etc.

The exact circumstances under which this renaming takes place will need careful consideration: does it happen, for example, if the required type is function(*)? (I think the answer is no.) But we're dealing there with edge cases.

@ChristianGruen ChristianGruen changed the title Proposal to support named arguments on dynamic function calls. Support named arguments on dynamic function calls Apr 27, 2023
@michaelhkay
Copy link
Contributor

Revisiting this after six months, I find my Nov 16th argument unconvincing. I fail to see what benefit someone calling a dynamically supplied function item would gain by writing $f(arg1:='a', arg2:='b') in preference to writing $f('a', 'b'). Keyword arguments are only useful if (a) the argument names have semantic value making the code more readable, or (b) some arguments are optional. Neither of these conditions applies here. Why make a language change with no benefits?

@michaelhkay michaelhkay added the Propose Closing with No Action The WG should consider closing this issue with no action label Sep 5, 2023
@michaelhkay
Copy link
Contributor

I'm tagging this as "propose to close with no action"; I've come to the conclusion that the usability problems caused by keyword arguments on dynamic function calls outweigh any benefits.

Keywords on parameters/arguments are a useful way for the person defining a function to give hints about the purpose of each parameter, and on a function call, they are useful as a hint to anyone reading the code to see what is going on. This only has value if the keywords used on the function call relate to the keywords used in the function definition, but when function items are supplied across a function call boundary, it's not feasible to require the caller to know the keywords defined by the function supplier. Redefining the keywords on the boundary just seems to invite complexity and confusion. To what purpose?

@ndw
Copy link
Contributor

ndw commented Sep 12, 2023

The CG agreed to close this issue without action at meeting 045.

@ndw ndw closed this as completed Sep 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature A change that introduces a new feature Propose Closing with No Action The WG should consider closing this issue with no action XPath An issue related to XPath
Projects
None yet
Development

No branches or pull requests

4 participants