Racket:在合同中使用的自定义谓词

Racket: Custom predicate to use within contract

我正在用 Racket 编写小型宠物项目并使用 Gregor 库来处理日期。

我有接受两个日期的函数(来自 Gregor,不是标准库),我想为其添加合同。合同应规定第一个参数的日期必须比第二个参数的日期 less/earlier。

在 Gregor 中我们可以通过使用 (date<=? x y) 或类似的谓词来实现它,但我不明白如何将它与契约结合起来。

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

将不起作用,并且没有开箱即用的 date>=?/c 谓词。

所以我想我需要自己写这样的谓词,所以我想知道如何去做。我查看了 Racket 资源,发现标准功能 quite complicated 可以重现。

有没有更简单的方法来实现我想要的?

最简单的方法是使用lambda:

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

一个缺点是,如果违反约定,错误消息将包含一个 ??? 来代替 lambda 表达式。如果你想让它在那里打印更有意义的东西,你可以这样做:

(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])

如果您想更好地控制错误消息,可以尝试使用 flat-contract-with-explanation

虽然 Ryan 的回答很好,但我发现可以使用前提条件以下列方式解决此问题:

(->i ([x date?]
      [y date?])
    #:pre (x y) (date<=? x y)
    ;; ...
 )