1
votes

I'm playing with Racket-Stamps, which is a mix of typed and regular Racket.

I'm writing a new feature and the code below attempts to call a function with a list of Reals, however because this list comes from untyped racket, it is actually a list of Any:

(define bounding (make-parameter '()))

;; snip

(when (not (empty? (bounding)))
  (let-values ([(x1 y1 x2 y2) (apply values (bounding))])
    (send pr set-bounding x1 y1 x2 y2)))

And in another file that calls the code above:

(bounding '(-20 -100 100 2))

Here's the error:

Type Checker: Bad arguments to function in `apply': Domains: a b ... b #f * Arguments: (Listof Any) * in: (apply values (bounding))

So how do I convert the Listof Any to a Listof Real?

1
I've discovered that I can make an empty list of Real with the ann function, so: (define bounding (make-parameter (ann '() (Listof Real)))) -- however I still get the error about apply, maybe it's something to do with make-parameter and values?Eric Clack
apply isn't very smart for most cases in Typed Racket. Especially since you have an arbitrary-length list as input, and a fixed number of values (4 of them) expected as output.Alex Knauth
Also, if you look at the type of values, it allows either zero things or one-or-more things, but not zero-or-more things. I would try to design this without using (apply values ...).Alex Knauth
If you use (match-let ([(list x1 y1 x2 y2) (bounding)]) ...) instead, does it work?Alex Knauth
match-let works, many thanks Alex.Eric Clack

1 Answers

1
votes

The apply function here is given an arbitrary-length list as input, but the context expects exactly 4 values. If the list had any length other than 4, it would fail.

It seems like you meant for bounding to contain either the empty list or a list of exactly 4 real numbers.

(: bounding : (Parameterof (U Null (List Real Real Real Real))))
(define bounding (make-parameter '()))

Then every time your program tests whether the contents of (bounding) are empty and then relies on it being a list of 4 numbers, you need to put the value in a local variable first, so that Typed Racket sees the connection between the (not (empty? ...)) test and the use below it.

In other words, transform the pattern

(if (not (empty? (bounding)))
    (.... (bounding) ....)
    ....)

Into

(let ([bounding-v (bounding)])
  (if (not (empty? bounding-v))
      (.... bounding-v ....)
      ....))

In your example, that transformation gives:

(: bounding : (Parameterof (U Null (List Real Real Real Real))))
(define bounding (make-parameter '()))

....

(let ([bounding-v (bounding)])
   (when (not (empty? bounding-v))
     (let-values ([(x1 y1 x2 y2) (apply values bounding-v)])
       (send pr set-bounding x1 y1 x2 y2))))