Use fold-left instead of map for an example of a nonuniform vararg function.

Fixes PR12014.

original commit: 4f761ddb2f44f4cd4961984c8c2cb65fa39f71ae
This commit is contained in:
Stevie Strickland 2011-07-01 11:59:14 -04:00
parent 8a0e9249ff
commit 3d15ead6b5

View File

@ -1,6 +1,6 @@
#lang scribble/manual
@begin[(require "../utils.rkt" (for-label typed/racket/base))]
@begin[(require "../utils.rkt" (for-label typed/racket/base) (for-label (only-in rnrs/lists-6 fold-left)))]
@title[#:tag "varargs"]{Variable-Arity Functions: Programming with Rest Arguments}
@ -43,63 +43,74 @@ of the rest parameter is used at the same type.
@section{Non-Uniform Variable-Arity Functions}
However, the rest argument may be used as a heterogeneous list.
Take this (simplified) definition of the Racket function @racket[map]:
Take this (simplified) definition of the R6RS function @racket[fold-left]:
@racketmod[
racket
(define (map f as . bss)
(define (fold-left f i as . bss)
(if (or (null? as)
(ormap null? bss))
null
(cons (apply f (car as) (map car bss))
(apply map f (cdr as) (map cdr bss)))))
i
(apply fold-left
f
(apply f i (car as) (map car bss))
(cdr as)
(map cdr bss))))
(map add1 (list 1 2 3 4))
(map cons (list 1 2 3) (list (list 4) (list 5) (list 6)))
(map + (list 1 2 3) (list 2 3 4) (list 3 4 5) (list 4 5 6))]
(fold-left + 0 (list 1 2 3 4) (list 5 6 7 8))
(fold-left + 0 (list 1 2 3) (list 2 3 4) (list 3 4 5) (list 4 5 6))
(fold-left (λ (i v n s) (string-append i (vector-ref v n) s))
""
(list (vector "A cat" "A dog" "A mouse")
(vector "tuna" "steak" "cheese"))
(list 0 2)
(list " does not eat " "."))]
Here the different lists that make up the rest argument @racket[bss]
can be of different types, but the type of each list in @racket[bss]
corresponds to the type of the corresponding argument of @racket[f].
We also know that, in order to avoid arity errors, the length of
@racket[bss] must be one less than the arity of @racket[f] (as
@racket[as] corresponds to the first argument of @racket[f]).
@racket[bss] must be two less than the arity of @racket[f].
The first argument to @racket[f] is the accumulator,
and @racket[as] corresponds to the second argument of @racket[f].
The example uses of @racket[map] evaluate to @racketresult[(list 2 3 4 5)],
@racketresult[(list (list 1 4) (list 2 5) (list 3 6))], and
@racketresult[(list 10 14 18)].
The example uses of @racket[fold-left] evaluate to @racketresult[36],
@racketresult[42], and @racketresult["A cat does not eat cheese."].
In Typed Racket, we can define @racket[map] as follows:
In Typed Racket, we can define @racket[fold-left] as follows:
@racketmod[
typed/racket
(: map
(: fold-left
(All (C A B ...)
((A B ... B -> C) (Listof A) (Listof B) ... B
((C A B ... B -> C) C (Listof A) (Listof B) ... B
->
(Listof C))))
(define (map f as . bss)
C)))
(define (fold-left f i as . bss)
(if (or (null? as)
(ormap null? bss))
null
(cons (apply f (car as) (map car bss))
(apply map f (cdr as) (map cdr bss)))))]
i
(apply fold-left
f
(apply f i (car as) (map car bss))
(cdr as)
(map cdr bss))))]
Note that the type variable @racket[B] is followed by an
ellipsis. This denotes that B is a dotted type variable
which corresponds to a list of types, much as a rest
argument corresponds to a list of values. When the type
of @racket[map] is instantiated at a list of types, then
of @racket[fold-left] is instantiated at a list of types, then
each type @racket[t] which is bound by @racket[B] (notated by
the dotted pre-type @racket[t ... B]) is expanded to a number
of copies of @racket[t] equal to the length of the sequence
assigned to @racket[B]. Then @racket[B] in each copy is
replaced with the corresponding type from the sequence.
So the type of @racket[(inst map Integer Boolean String Number)]
So the type of @racket[(inst fold-left Integer Boolean String Number)]
is
@racket[((Boolean String Number -> Integer)
(Listof Boolean) (Listof String) (Listof Number)
@racket[((Integer Boolean String Number -> Integer)
Integer (Listof Boolean) (Listof String) (Listof Number)
->
(Listof Integer))].
Integer)].