schemeracketfunctormultiple-return-values

Generalizing map-values in Racket


Suppose I have the following function:

(define foo
  (lambda (n)
    (values n (* 2 n))))

I want to call map foo over the list '(1 2 3 4). Hence, I create a map-values function:

(define map-values
  (lambda (f xs)
    (match xs
      ['() (values '() '())]
      [(cons x xs)
        (let-values ([(a  b)  (f x)]
                     [(as bs) (map-values f xs)])
          (values (cons a as) (cons b bs)))]
      )))

Now, I can do the following:

(map-values foo '(1 2 3 4)) => (values '(1 2 3 4) '(2 4 6 8))

However, what if foo returns three values, or four, ... etc.? I'll need to create a new map-values function for each case. Is there a way to generalize map-values so that is can work with any foo that returns multiple values?


Solution

  • This does what you want:

    (define (map-values proc lst)
      (define (wrap e)
        (call-with-values (lambda () (proc e)) list))      
      (apply values
             (apply map list (map wrap lst))))
    
    (define (foo n)
      (values n (* 2 n) (/ 1 n)))
    
    (map-values foo '(1 2 3 4))
    ; ==> (1 2 3 4)
    ; ==> (2 4 6 8)
    ; ==> (1 1/2 1/3 1/4)
    

    This is compatible with the standard schemes as well.