Compose functions right-associatively, returning composed-function.
composewas called with zero arguments, then composed-function acts as the
identityfunction except it accepts and ignores any number of arguments beyond the first. That is, it accepts any number of arguments except 0 and always returns the first one.
composewas called with one argument, function, then composed-function is function.
composewas called with two or more arguments, then let rfunctions be the result of evaluating
). The arguments passed to composed-function are passed as-is to the first function in rfunctions, returning result1 (only the first value is retained). Then result1 is passed to the second function in rfunctions, returning result2, and so on until there are no more functions to call. composed-function returns the multiple values returned by the last function.
To recapitulate the effects of multiple arguments and values:
- The last function in functions (the first to be called) can accept or require many arguments. The other functions must be able to accept a single argument.
- The first function in functions (the last to be called) can return multiple values. The other functions can return multiple values but only the first value will be retained and passed to the next function.
Design notes: There are a few different sets of semantics one might consider for
composewith respect to multiple arguments and return values. I thought I'd document why I chose the particular set explained above.
A central issue is that I strongly feel that calling
)should simply return my-function. Regardless of the fact you'd rarely want to call
composewith one argument, I feel this is a semantically meaningful base case. I see no good reason to impose restrictions on the number of arguments my-function should take or how many values it should return. Anyway, trying to enforce these restrictions would carry a performance penalty.
Presuming we want to stay consistent with this base case, it follows that no matter how many functions
composeis called with, the first function that will be called should be able accept any number of arguments it wants, and the last one should be able to return as many values as it wants.
As for what happens with the functions in the middle, it would be possible to pass any multiple values into the next function, such that returning 3 values, for example, would call the next function with 3 arguments. But I really don't think that feature would be used often (at least I never personally wished to use it), and I think dealing with potential multiple values like this with
multiple-value-callaught to have at least a small performance penalty. Paying a performance cost for a very rarely used feature is not my idea of fun.
There's also the possibility of forgetting that one of the functions you're using as middle returns multiple values, which might happen if you usually only deal with its first value, which is typically the most important and the most often used. You'd get a sometimes-cryptic “Wrong number of arguments.” error.