Racket: настраиваемый предикат для использования в контракте

Я пишу небольшой проект для домашних животных в Racket и использую Gregor lib для обработки дат .

У меня есть функция, которая принимает две даты (от Грегора, а не из стандартной библиотеки), и я хотел бы добавить для нее контракт. В контракте должно быть указано, что дата из первого аргумента должна быть меньше / раньше даты из второго аргумента.

В Грегоре мы можем добиться этого, используя (date‹ =? Xy) или подобный предикат, но я не могу понять, как объединить его с контрактами.

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

не будет работать, и нет предиката date>=?/c "из коробки".

Итак, я полагаю, что мне нужно будет написать такие предикаты самостоятельно, поэтому я хотел бы знать, как это сделать. Я просмотрел источники Racket и обнаружил, что стандартные функции: довольно сложно воспроизвести.

Есть ли более простой способ добиться того, чего я хочу?


person Bohdan Ivanov    schedule 25.06.2019    source источник


Ответы (2)


Самый простой способ - использовать lambda:

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

Одним из недостатков является то, что при нарушении контракта сообщение об ошибке будет включать ??? вместо лямбда-выражения. Если вы хотите, чтобы он напечатал там что-то более значимое, вы можете сделать что-то вроде следующего:

(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 < / а>.

person Ryan Culpepper    schedule 25.06.2019
comment
Идеально! Сообщение об ошибке действительно не очень полезно, но что касается Racket7.3, по крайней мере, я могу видеть лямбда-выражение в трассировке. - person Bohdan Ivanov; 25.06.2019

Хотя ответ Райана великолепен, я обнаружил, что эту проблему можно решить следующим образом, используя предварительное условие:

(->i ([x date?]
      [y date?])
    #:pre (x y) (date<=? x y)
    ;; ...
 )
person Bohdan Ivanov    schedule 25.06.2019