9

我在球拍/列表中定义了一个true?计数一起使用的函数。

(define (true? expr)
  (and (boolean? expr) expr #t))

我注意到我可以为它提供数字参数,并且我的函数会很高兴地返回#f.

> (true? 6)
#f

所以,我想我会探索使用球拍合约来使非布尔参数返回违反合约的错误。所以我把这段代码放在我文件的顶部:

(provide (contract-out
          [true?         (-> boolean? boolean?)]))

但是,在添加合同后,我仍然在球拍 REPL 中得到与上述相同的行为。我不明白这怎么可能。我错过了什么?

4

2 回答 2

21

合同通常在模块之间强制执行。因此,您必须从外部角度进行尝试。但是,REPL 从您正在使用的模块内部应用。

从外部进行测试的一种简单方法是使用测试子模块。例如:

#lang racket

(define (true? expr)
  (and (boolean? expr) expr #t))

(provide (contract-out
          [true?         (-> boolean? boolean?)]))   

(module* test racket/base
  (require (submod "..")
           rackunit)
  (check-true (true? #t))
  (check-false (true? #f))
  (check-exn exn:fail:contract? (lambda () (true? 3))))

更改合同并在 DrRacket 中重新运行,您应该在此处看到您的合同生效,因为test此处的模块被视为合同的外部客户。


或者,制作另一个文件作为require第一个文件,然后您也可以在那里看到合同的效果。如果第一个文件被调用true-test.rkt,那么你可以制作另一个模块,然后:

 #lang racket
 (require "true-test.rkt")
 (true? 42)  ;; And _this_ should also raise a contract error.
于 2013-02-05T23:02:53.320 回答
14

Danny Yoo 给出了一个很好的答案。我只是想扩展它并注意 Racket 确实为您提供了更多的灵活性,让您可以在哪里执行合同(即,在哪里放置合同边界)。例如,您可以使用以下define/contract形式:

-> (define/contract (true? expr)
     (-> boolean? boolean?)
     (and (boolean? expr) expr #t))

这将在定义true?和所有其他代码之间建立合同检查:

-> (true? "foo")
; true?: contract violation
;  expected: boolean?
;  given: "foo"
;  in: the 1st argument of
;       (-> boolean? boolean?)
;  contract from: (function true?)
;  blaming: top-level
;  at: readline-input:1.18
; [,bt for context]

如果我想在 REPL 测试与合同相关的东西,我发现define/contract它特别有用,因为我并不总是有模块。但是,contract-out这是默认建议,因为在模块边界检查合约通常是一个不错的选择。

于 2013-02-06T06:53:32.747 回答