Use fold-left instead of map for an example of a nonuniform vararg function.
Fixes PR12014. original commit: 4f761ddb2f44f4cd4961984c8c2cb65fa39f71ae
This commit is contained in:
parent
8a0e9249ff
commit
3d15ead6b5
|
@ -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)].
|
||||
|
|
Loading…
Reference in New Issue
Block a user