Re: Re-Proposal for variadic functions - the good the bad and the unacceptable

I've been doing some reading about how this works in C#, where my knowledge has always been a bit superficial.

As I understand it, method calls in C# correspond to our static function calls, and delegate calls correspond to our dynamic calls. 

Delegates (function objects) have simple signatures with a fixed arity and are always called supplying all the arguments positionally. Is that right?

Method calls can be overloaded, and a call on a method is resolved using a complex algorithm based on the method name, the number of arguments, their types, and any keywords used in the call. In contrast, function calls and function references in XPath have traditionally been resolved using the function name and arity alone.

If we want to allow more complex overloading of functions, then the mechanism needs to work both for function calls and for function references. 

What I've been proposing is that if you want a function that does different things depending on what arguments are supplied, you define one function, and the logic of the function itself is responsible for sorting out what to do based on the actual arguments. That is, you have a choice of defining multiple functions, one for each arity, or a single function that handles all combinations of arguments. On the whole that's what I see users wanting: it's a pain having to define an arity-one function that calls an arity-two function which calls an arity-three function; users would like to combine all of these into a single function declaration that handles all combinations.

It would certainly be possible to continue resolution based on arity but with arity ranges rather than fixed arity, as Reece is suggesting: so you have one function declaration that handles arities in the range 2-5 and another that handles arities of 6 and higher. It's not actually difficult to formulate the rules, it just seems to me that few users would want to do this.

Resolution by type is also possible in principle, but in a dynamically typed language the rules would be rather complicated, and they might be computationally expensive. I think it could lead to subtle and hard-to-understand bugs when users don't understand the rules.

Resolution by keyword is also possible in principle, but it also complicates the rules, because it means that using keywords has more than one purpose, it doesn't just identify which parameter you are binding, it also affects which function you are binding. In particular it complicates function references, because we then need to use keywords somehow in functon references as well as function calls. I'd prefer not to do this.

Looking at Dimitre's objections, I'll try hard to avoid the instinctive defensiveness that gets triggered by use of confrontational words like "unacceptable", and treat them as if they had been phrased in more constructive, consensus-seeking language. [*]

> ·         It is unacceptable to define the arity of a function as nothing (the empty sequence () or *) when the reader clearly sees that the function has M positional parameters and N optional parameters. People who believe what they see will be confused and upset.

"*" doesn't generally have the connotation of "nothing", it has the connotation of "everything". Similarly () can be read here as saying "I'm not stating any constraints, therefore everything matches.".

> ·         It is unacceptable to forbid a variadic function to have other overloads, contrary to the practice in other programming languages. If this can be done in C#, why shouldn’t it be done here?

C# is strongly typed, so overloading by type works fairly easily; overloading by type in a dynamically typed language is much more difficult. Overloading by keyword just feels very complicated and hard to understand.

> ·         It is unacceptable to introduce vague, confusing terms such as “virtual fixed-arity functions“ just in attempt to fix the holes left by the document in its current form.

Reece has suggested a better way of presenting the concept, thanks Reece for your constructive suggestions.

> ·         It is unacceptable to prevent any static type-checking in the case of “map-variadic” functions. An obvious improvement would be “record-variadic functions”.

Map-variadic functions in conjunction with record types certainly provide the static type-checking needed.

> ·         It is unacceptable not to provide any disambiguation mechanisms when there are overloads that may be ambiguous if allowed. Based on existing programming languages, there are at least two obvious ways of disambiguation: by arity and by argument name.
I can just about see how to make disambiguation by keyword work for static function calls, but the detailed rules seem very difficult. For example if I have two functions f(x, [y]) and f(x, [z]) where parameters in square brackets are optional, then how do I distinguish which of them the call f(42) should invoke? And how do function references work? The extra complexity doesn't seem to give enough benefit to be worth the trouble.

Michael Kay

[*] A little anecdote here. I once attended a standards meeting presenting a proposal which was accepted with no dissent. In the break the chairman took me aside and told me that essentially the same proposal had been tabled by someone else a year earlier, and had been thrown out with almost no-one in favour. The chairman told me that the only reason I got it through and the other guy didn't was that I used consensus-seeking language whereas the other guy was confrontational.

Received on Thursday, 10 December 2020 12:28:26 UTC