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

On Thu, 10 Dec 2020 at 02:24, Dimitre Novatchev <dnovatchev@gmail.com>
wrote:

> Here is a summary feedback for the recent submission by Dr. Kay that, in
> his words, is “an attempt to consolidate a proposal for variadic
> functions that combines all the ideas and requirements that have been
> expressed”.
>
>
> *The not so good*
>
> The obvious things that are not good in the proposal:
>
> ·         The arity of a variadic function is undefined and it cannot
> have overloads – only a single function with that name can exist. There is
> no obvious, specific reason for such design decision, the practice of other
> programming languages shows that functions allowing calls with keyword
> arguments, can have additional overloads. See for example Jon Skeet’s “C#
> in Depth” / Overloading (section: Optional parameters (
> https://csharpindepth.com/articles/Overloading)). Here one can see at
> least two ways of disambiguating seemingly ambiguous method-calls –
> disambiguation by explicitly specifying the optional parameters, and
> disambiguation by argument names.
>

I think it would make sense to make it such that as long as the minimum and
maximum bounds of a function do not overlap (where the bounds are equal for
non-variadic functions), then they can be overloaded, otherwise an XQST0034
error is raised (like currently when trying to define a function with the
same arity). It gets tricky for map-variadic functions,but I think that can
be solved by treating them like array-variadic functions -- that is, the
maximum bound is infinite/unbounded (within architectural limits).


> ·         The function reference of a variadic function. Obviously
> someFunName#*  tells us nothing about the possible number of arguments,
> and the value of the arity is passed to other functions as nothing (the
> empty sequence ()   )  , even though in all three types of proposed
> variadic functions, the exact number of positional argument is known, and
> in the case of a “bounded variadic function " the maximum number of
> optional arguments in a function-call is also exactly known
>

If you want to bind to a specific arity you would use name#arity as you
currently do, and that would bind to the variadic function at that specific
arity.

The question is whether it makes sense to bind to the actual function
definition. I think it does for an array-variadic or map-variadic function
-- where you may want access to a function that takes an array or map as
its last argument, but not for the bound-variadic function unless you are
referencing the version of the bound-variadic function where all parameters
are required. Using name#arity with arity set to the number of parameters
will work for the bound-variadic function, but for the array- and
map-variadic functions the last parameter would be a different type.

So how do you then reference the non-variadic versions of the array- or
map-bound functions? Using #* in this context would make sense provided
there can only be a single function of the given QName when defining
variadic functions. The question then is whether the resulting reference is
itself variadic (like is implied by #*) or if it is non-variadic.

---

An alternative would be to make it so that when using a name#arity
reference with the same arity of the function, that
a) for bound-variadic functions it is no longer variadic (as all arguments
are now required due to specifying the arity);
b) for array--variadic functions it binds to the version accepting an array
or an item type matching the array's item type (i.e. a single array value)
-- i.e. a union-of(array(T), T) parameter, which is needed as both types
are valid at the given arity;
c) for map-variadic functions it binds to the version accepting a map or
record type -- it does not make sense to have the last parameter support a
named argument as the function is no longer variadic.

In this case the resulting function reference would not be variadic, so
function-arity would not need to support an empty-sequence return type in
the case where the function is variadic. This would make function call
dispatch trickier, as you would need to know if the function is variadic or
not, instead of just checking the last parameter is an array or map and
then applying the variadic rules there. However, I suspect that this would
be similar to a situation where a user uses argument placeholders -- that
is, if a function is array- or map-variadic and has 2 parameters, then f#2
and f(?, ?) would be equivalent, and f(2, ?) would result in a similar
situation.


> ·         “References to virtual fixed-arity functions that map directly
> to the variadic function”. The author had to do this in order to overcome
> the two bad things above – thus doing a 3rd bad thing…
>

I don't think this is a bad thing (as you can bind to a function at a given
fixed arity currently), I just think that it is loosely specified using
informal terminology (as per my attempt at providing a more formal
definition in terms of equivalence to creating an inline function
expression in the other thread). Not being able to do this would break
backwards compatibility, and would prevent a user being able to get a fixed
arity reference to a variadic function (so would not be able to do things
like passing concat#2 to a function).


> ·         In “map-variadic” functions there is no possibility for static
> type checking. This provides for unlimited space of runtime
> “confusion-errors” where the free-text English language description is
> understood differently by the author of the function and by its users.
> Natural language ambiguity is best avoided when doing serious function
> design work.
>
I think it makes sense to support record types for map-variadic functions.
That way, if you want the map-based parameters to be clearly defined, you
can through that mechanism (while opting in to allowing other parameters if
you make the record extensible/open ended). If you don't want that, you can
specify a map type.

A map type would be useful if you wanted to do things like defining json
object constructor functions (with the parameters being the object keys),
or xml/html contructor functions (with the additional parameters being the
attributes). If you wanted more verification (e.g. in a JSON-LD library),
you could use record types.

> *The unacceptable*
>
>
>
> Some of the bad things are so bad that they are unacceptable:
>
> ·         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.
>

A better return type for variadic functions would be a record with a min
value for the minimum bound and max for a maximum bound. If the function is
array- or map-variadic, maximum bound would be an empty sequence to denote
it being unbounded/infinite.

If using my alternative proposal this problem would not exist as it would
not be possible to create a reference to the variadic version of a function.

It may be useful to have a new `fn:function-arities($function-name as
xs:QName) as union-of(record(min: xs:integer, max: xs:integer?),
xs:integer)*` function that returns all the arities of functions in the
static context. For variadic functions it will return a map with a min
value for the minimum value, and a max value if an upper bound is specified
(for bounded-variadic functions).


> ·         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?
>

If using Michael Kay's proposal of using #* to bind to the underlying
function (to access the array/map last parameter) it is not possible. Using
my alternative proposal, it would be possible. I've elaborated further
above.


> ·         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.
>

I think this is because the language/wording hasn't been formalised, and
that was a short-hand for a more formal definition that would come later
(like the one I wrote in the other thread). The main thing is assessing the
overall approach.


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

I agree that records should be allowed in map-variadic functions. That
wouldn't be a record-variadic function though, as records are supposed to
be usable wherever maps are.


> ·         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 think it makes sense to keep the (arity, function name) mechanism as the
way to disambiguate functions. In the context of variadic functions, the
arity here is now a range defined by the bounds of the function.

Kind regards,
Reece


> Thanks,
>
> Dimitre
>
>
> Attachment: this text as a pdf file, in case it is not well readable on
> the w3 web server
>
>
>

Received on Thursday, 10 December 2020 09:28:19 UTC