racket - 不强制执行带有外判的合同
问题描述
我需要解释为什么这种“外包”不起作用。它应该只返回正值,但它仍然接受负值。谁能解释一下?谢谢你。
#lang racket
(struct account (balance))
(provide (contract-out
[balance (-> account? number?)]
[deposit (-> account? positive-num? account?)]))
(define new-account (account 0))
(define (positive-num? n)
(cond [(not (number? n)) #f]
[(> n 0) #t]))
(define (balance acc)
(account-balance acc))
(define (deposit acc amt)
(account (+ (account-balance acc) amt)))
(displayln (balance (deposit new-account -10)))
解决方案
你写的程序有两个问题。
首先,你的positive-num?
谓词是错误的。用负数试试——你不会回来#f
的。您的实现将(positive-num? -10)
产生#<void>
(因为没有cond
子句匹配),这不是#f
,因此是真实的。您可以将主体重写positive-num?
为简单(and (number? n) (> n 0))
,这既更清晰也更正确,但您也可以positive-num?
完全废弃自定义谓词而只使用合同(and/c real? positive?)
。
您的代码的第二个问题更加微妙。当一个合同被赋予一个价值时,合同被附加在一个合同边界上。每当值在合同边界之外使用时,合同就会被强制执行,但不会在合同边界内强制执行。这是因为,在合同边界内,您直接使用价值,而不是附加合同的价值。
当您使用contract-out
时,合约边界是提供标识符的模块。在模块之外,合同是强制执行的,但在模块内部,它不是。因此,由于您的整个程序都在一个模块中,因此合同永远不会相关。
要查看实际情况,请尝试由多个模块组成的程序:
#lang racket
(module bank racket
(provide (contract-out
[balance (-> account? number?)]
[deposit (-> account? (and/c real? positive?) account?)]
[new-account account?]))
(struct account (balance))
(define new-account (account 0))
(define (balance acc)
(account-balance acc))
(define (deposit acc amt)
(account (+ (account-balance acc) amt))))
(require 'bank)
(displayln (balance (deposit new-account -10)))
由于合同定义已移至单独的子模块中,并且由于调用deposit
在该子模块之外,因此上述代码引发了合同违规:
deposit: contract violation
expected: a number strictly greater than 0
given: -10
in: the 2nd argument of
(->
account?
(and/c real? positive?)
account?)
contract from: (anonymous-module bank)
blaming: anonymous-module
(assuming the contract is correct)
at: unsaved-editor:6.13
如果您希望在同一个模块中执行合同,您需要使用一种创建比contract-out
. 一种这样的形式是define/contract
,它在定义本身和定义主体之外的所有内容之间创建了一个边界:
#lang racket
(struct account (balance))
(define new-account (account 0))
(define/contract (balance acc)
(-> account? number?)
(account-balance acc))
(define/contract (deposit acc amt)
(-> account? (and/c real? positive?) account?)
(account (+ (account-balance acc) amt)))
(displayln (balance (deposit new-account -10)))
现在即使使用和定义在同一个模块中,也会发出违反合同的信号。
有关合同边界的更多详细信息以及为什么您可能希望选择一种形式而不是另一种形式,请参阅The Racket Guide 中的合同和边界。
推荐阅读
- php - 使用 Docker 执行内置 PHP 服务器文件
- azure - 在没有 rbac 问题的情况下在 aks 中部署 nginx-ingress
- angular - 如何在angular5中列出ng2-select中的客户?
- python - 从 Telegram 的私人频道转发帖子
- java - 无法为访问令牌交换 OAuth 身份验证代码
- python - .csv 后仅在一个单元格中导出项目
- c - OpenGL 仅在 fb0 中进行第一次纹理渲染
- sql - SQL SELECT 从列值不等于某值的两列之一
- javafx - ComboBoxTableCell,回调
- c# - 如何在linq列表中分组并在c#linq中连接一些字符串