Previous: WHILE*, Up: Reference


3.7 compose

— Function: compose &rest functions ⇒ composed-function

Compose functions right-associatively, returning composed-function.

If compose was called with zero arguments, then composed-function acts as the identity function 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.

If compose was called with one argument, function, then composed-function is function.

If compose was called with two or more arguments, then let rfunctions be the result of evaluating (reverse functions). 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:

Design notes: There are a few different sets of semantics one might consider for compose with 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 (compose my-function) should simply return my-function. Regardless of the fact you'd rarely want to call compose with 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 compose is 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-call aught 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.