3

I'm writing small pet project in Racket and using Gregor lib to handle dates.

I have function that accepts two dates (from Gregor, not standard library) and I would like to add contract for it. Contract should say that date from first argument has to be less/earlier than date from second argument.

In Gregor we can achieve it by using (date<=? x y) or similar predicate, but I cannot understand how to combine it with contracts.

 (contract-out
          [process-dates (->i ([x date?]
                               [y (x) (and/c date?
                                             (date>=? x))])])

will not work, and there is no out-of-box date>=?/c predicate.

So I guess that I will need to write such predicates by my own, therefore I would like to know how to do it. I've looked through Racket sources and found that standard features are quite complicated to reproduce.

Is there simpler way to achieve what I want?

Bohdan Ivanov
  • 806
  • 9
  • 28

2 Answers2

5

The simplest way is to use lambda:

(->i ([x date?]
      [y (x) (and/c date? (lambda (y) (date>=? y x)))])
     [_ any/c])

One disadvantage is that if the contract is violated, the error message will include a ??? in place of the lambda expression. If you want it to print something more meaningful there, you can do something like the following:

(define (date>=/c x)
  (flat-named-contract
   `(date>=/c ,x)
   (lambda (y) (date>=? y x))))
....
(->i ([x date?]
      [y (x) (and/c date? (date>=/c x))])
     [_ any/c])

If you want even finer control over the error message, you could try using flat-contract-with-explanation.

Ryan Culpepper
  • 10,495
  • 4
  • 31
  • 30
3

While Ryan's answer is great, I've found that one may solve this problem in following manner, using precondition:

(->i ([x date?]
      [y date?])
    #:pre (x y) (date<=? x y)
    ;; ...
 )
Bohdan Ivanov
  • 806
  • 9
  • 28