[racket] sharing an expensive computation required in procedure and its contract?

From: Jon Zeppieri (zeppieri at gmail.com)
Date: Sun Jul 20 21:07:17 EDT 2014

On Sun, Jul 20, 2014 at 8:18 PM, Robby Findler
<robby at eecs.northwestern.edu> wrote:
> FWIW, I think raise-argument-error is a fine way to go here to avoid
> that expense (I assuming you're thinking of not checking this aspect
> of the contract using the racket/contract library but instead
> computing 'expensive-result' once in the body and then checking it?).
> And one day perhaps the research will catch up with this use of
> contracts.
>
> Can you supply a little more detail on what the expensive computation
> is doing, tho, to help spur our imagination?
>
> Robby

Sure. (It turns out that I don't actually I need to raise an exception
where I thought I did, so this is no longer a problem for me. However,
I could see it coming up again, so here goes...)

I was translating the DTF2D function from the SOFA date and time
library. This function takes a time scale, along with date and time
components (year, month, day, hour, minutes, seconds) and returns a
Julian date. For the most part, validating the inputs is
straightforward (e.g., month needs to be between 1 and 12), but the
`seconds` parameter is a pain. If the time scale is UTC and if the
entire date-time falls on a leap second, then 60 is a valid value. The
function body needs to know the same thing in order to produce the
correct Julian date. (Actually, it needs to know a bit more, and it
would have been possible to make the contract use a cheaper check than
the function, come to think of it.)

I realized at some point that the SOFA library doesn't return an error
if seconds is too high; it returns a warning instead. And considering
that it does return errors for other out-of-bounds conditions, I
assume there's a good reason for this behavior. So I ended up just
defining seconds/c to be (integer-in 0 60).

Another option I thought of later: change the function to take a
struct containing the date-time value, and put the dependent contract
burden on the struct instead of the function.

Posted on the users mailing list.