The Racket Guide阅读笔记,chapter 7.
7 Contracts
7.1 Contracts and Boundaries
合约(contract)是一个模块为其自身提供的使用声明。
#lang racket
(provide (contract-out [amount positive?]))
上述合约声明确保amount永远为正。
对于#lang racket/base
,需要显示(require racket/contract)
,如同:
#lang racket/base
(require racket/contract) ; now we can write contracts
(provide (contract-out [amount positive?]))
7.1.1 Contract Violations
前面例子中的positive?
只适用于数字,如果amount不是数字,那么positive?
就会出错。一个解决办法是使用and/c
确保它既是数字又为正值:
(provide (contract-out [amount (and/c number? positive?)]))
7.1.2 Experimenting with Contracts and Modules
racket支持submodule,例如:
#lang racket
(module+ server
(provide (contract-out [amount (and/c number? positive?)]))
(define amount 150))
(module+ main
(require (submod ".." server))
(+ amount 10))
7.1.3 Experimenting with Nested Contract Boundaries
可以在更细颗粒度上使用合约,这涉及到define/contract
:
#lang racket
(define/contract amount
(and/c number? positive?)
150)
(+ amount 10)
7.2 Simple Contracts on Functions
合约也可以应用于函数:
(provide (contract-out
[deposit (-> number? any)]
[balance (-> number?)]))
->
后面首先指定输入值,然后是返回值约束。
7.2.1 Styles of ->
(-> number? any)
也可以写成(number? . -> . any)
。
7.2.2 Using define/contract and ->
你也可以在函数上使用define/contract
:
(define/contract (deposit amount)
(-> number? any)
; implementation goes here
....)
但是这会导致每次函数调用的时候都进行合约检查,可能会影响性能。然后合约检查对于此函数在模块内部的调用也会进行。
7.2.3 any and any/c
上面deposit函数中使用的any合约表示deposit可以返回任意值,一个或者多个值。any只可以用于约束返回值,上面的例子中,也可以将any替换成具体的void?
。
除了any之外,还可以使用any/c
,表示只会返回单个任意值。
7.2.4 Rolling Your Own Contracts
可以自定义合约:
(define (amount? a)
(and (number? a) (integer? a) (exact? a) (>= a 0)))
也可以通过and/c
以及or/c
来定义:
(define amount/c
(and/c number? integer? exact? (or/c positive? zero?)))
另一个例子:
(provide (contract-out
; convert a random number to a string
[format-number (-> number? string?)]
; convert an amount into a string with a decimal
; point, as in an amount of US currency
[format-nat (-> natural-number/c
(and/c string? has-decimal?))]))
也可以使用regexp来作为合约
#lang racket
(provide
(contract-out
....
; convert an amount (natural number) of cents
; into a dollar-based string
[format-nat (-> natural-number/c
(and/c string? #rx"[0-9]*\\.[0-9][0-9]"))]))
7.2.5 Contracts on Higher-order Functions
如何描述函数输入以及返回值中的函数类型:
(-> integer? (-> integer? integer?))
(-> (-> integer? integer?) integer?)
7.2.6 Contract Messages with “???”
可以用flat-named-contract给复杂的合约取一个名字:
(module improved-bank-server racket
(provide
(contract-out
[deposit (-> (flat-named-contract
'amount
(λ (x)
(and (number? x) (integer? x) (>= x 0))))
any)]))
7.2.7 Dissecting a contract error message
对合约错误信息进行解读。
7.3 Contracts on Functions in General
->
只能应用于参数个数固定的函数,对于其他类型的参数,需要使用->*
以及->i
。
7.3.1 Optional Arguments
关于可选参数的一个例子:
(provide
(contract-out
; pad the given str left and right with
; the (optional) char so that it is centered
[string-pad-center (->* (string? natural-number/c)
(char?)
string?)]))
->*
后面先通过一个连对执行必选参数,然后通过额外的连对指定可选参数,最后指定返回值类型。
7.3.2 Rest Arguments
在合约中指定可变参数需要通过关键字的辅助:
(provide
(contract-out
[max-abs (->* (real?) () #:rest (listof real?) real?)]))
上述:
- 必选参数
(real?)
- 可选擦书
()
- 可变参数
#:rest (listof real?)
- 返回值
real?
7.3.3 Keyword Arguments
对于关键字参数,直接按照关键字指定即可:
(provide (contract-out
[ask-yes-or-no-question
(-> string?
#:default boolean?
#:title string?
#:width exact-integer?
#:height exact-integer?
boolean?)]))
7.3.4 Optional Keyword Arguments
可选关键字参数:
(provide (contract-out
[ask-yes-or-no-question
(->* (string?
#:default boolean?)
(#:title string?
#:width exact-integer?
#:height exact-integer?)
boolean?)]))
7.3.5 Contracts for case-lambda
对于case-lambda的contract定义可能比较特别。对于
(provide (contract-out
[report-cost
(case->
(integer? integer? . -> . string?)
(string? . -> . string?))]))
其对应的case-lambda是:
(define report-cost
(case-lambda
[(lo hi) (format "between $~a and $~a" lo hi)]
[(desc) (format "~a of dollars" desc)]))
7.3.6 ~ 7.3.9
略
7.4 Contracts: A Thorough Example
对argmax定义contracts。
#lang racket
(define (argmax f lov) ...)
(provide
(contract-out
[argmax (-> (-> any/c real?) (and/c pair? list?) any/c)]))
剩余略
7.5 Contracts on Structures
7.5.1 Guarantees for a Specific Value
可以通过struct/c
来指明导出的是一个struct。
7.5.2 Guarantees for All Values
略
7.5.3 Checking Properties of Data Structures
略
7.6 Abstract Contracts using #:exists and #:∃
可以通过#:exists亦或#:∃创建抽象的合约类型,以抹掉原有类型的痕迹。
7.7 Additional Examples
略
7.8 Building New Contracts
合约的内部表示其实是一个接受特定参数并返回projection的。
具体内容略。
7.9 Gotchas
略。
(本篇完)