[racket] Multiple return values
Does Racket blindly inherit from Scheme because of history?
No, Racketeers choose what to take over and what to leave behind.
Why are multiple values useful?
Multiple values enable a smooth functional style where pedestrian programmers may have to use clumsy package-unpackage or, worse, imperative style. This improvement shows up especially in loops. Here is a concrete example from a recent program I wrote:
> ;; Board ->* Natural [non-empty-Listof Player]
> ;; determines the number of winning territories and the players(s) who have that many territories
> ;; > (winners (list (territory 0 0 1 9 0) (territory 0 0 1 9 1)))
> ;; (values 2 '(0))
> ;; > (winners (list (territory 0 1 1 9 0) (territory 0 0 1 9 1)))
> ;; (values 1 '(0 1))
>
> (define (winners board) ;; <--- winners returns multiple values
> (for/fold ([best 0][winners '()]) ([p PLAYER#]) ;; <-- for/fold uses multiple values
> (define p-score (sum-territory board p))
> (cond [(> p-score best) (values p-score (list p))]
> [(< p-score best) (values best winners)]
> [(= p-score best) (values best (cons p winners))])))
Before someone had written it with lists and that had injected a mistake in the AI evaluation function of the game:
> ;; GameTree Natural -> Number
> ;; Returns a number that is the best move for the given player.
> (define (rate-position tree depth)
> (cond [(or (= depth 0) (no-more-moves? tree))
> (define-values (best w) (winners (game-board tree))) ;; <--- receive multiple values
> (if (member AI w) (/ 1 (length w)) 0)]
> [else
> (define ratings (rate-moves tree depth))
> (apply (if (= (game-player tree) AI) max min)
> (map second ratings))]))
You see, the code cleanly separates the winning score from the list of winners now. In turn, the AI function can easily compute the probability of winning at the end of this branch of the code. [[ This is a naive evaluation function. ]]
When the previous programmer had just used list, he forgot to extract the winning score from the list of winners -- and that meant the scoring was all wrong. Of course this is extremely difficult to notice during the game and the programmer conveniently also adjusted the tests to match the actually computed result.