对球拍合同感到困惑

时间:2013-02-05 22:35:18

标签: racket

我定义了一个函数true?,用于在球拍/列表中使用count

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

我注意到我可以提供数字参数,我的函数很乐意返回#f

> (true? 6)
#f

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

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

然而,在添加合约后,我仍然在球拍REPL中获得与上述相同的行为。我不明白这是怎么回事。我错过了什么?

2 个答案:

答案 0 :(得分:21)

合同通常在模块之间强制执行。所以你必须从外部角度尝试它。但是,REPL适用于您正在使用的模块内部。

从外部测试的一种简单方法是使用test submodule。例如:

#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.

答案 1 :(得分: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是默认建议,因为在模块边界检查合同通常是一个不错的选择。