rnso
rnso

Reputation: 24545

Reversing the function usage in compose function

The compose function applies last function first, i.e. it applies sent functions in reverse order. For example:

((compose sqrt add1) 8)

Above will add1 to 8 and then find sqrt of 9.

I want to create a mycompose function which applies first sent function first, and then second, third etc. Hence, in above example, sqrt needs to be applied first and then add1. I could manage following:

(define (mycompose L arg)
  (let loop ((L L)
             (res arg))
    (cond
      [(empty? L) res]
      [else (loop (rest L) ((car L) res))])))

(mycompose (list sqrt add1) 8)

I am sure there is a better way. Especially, can above be achieved using macros and also permit multiple arguments to be sent to each function turn by turn?

Upvotes: 0

Views: 107

Answers (1)

Alexis King
Alexis King

Reputation: 43852

This “reverse composition” that you describe is often called thrush, and the point-free package provides a thrush function that does precisely what you want. However, let’s say you wanted to implement it yourself. Well, the easiest way would be to reuse the existing implementation of compose, since thrush is just compose with the arguments reversed:

(define (thrush . fs)
  (apply compose (reverse fs))

This is, in fact, how the point-free package implements thrush. Still, this might be unsatisfying to you, since you don’t know how compose is implemented. Well, fortunately, we can implement thrush from scratch with a simple fold:

(define (thrush . fs)
  (for/fold ([f values])
            ([g (in-list fs)])
    (λ args (call-with-values (thunk (apply f args)) g))))

The real trick here is the use of call-with-values, which properly handles functions that return multiple values. Just like Racket’s compose, this will accept multiple values and pass them along to functions later in the pipeline as multiple arguments, creating a nice symmetry between function inputs and function outputs.

Upvotes: 4

Related Questions