7.2简单契约和函数

数学函数有一个领域和范围。领域代表能够接受的参数,范围代表产生的值。传统表示方法如下

f:A->B

程序语言的函数也有领域和范围,契约能保证函数值接受领域的值和只产生范围的值。->创建一个函数契约。

  #lang racket
  (provide (contract-out
                [deposit (-> number? any)]
                [balance (-> number?)]))
  (define amout 0)
  (define (deposit a) (set! amount (+ amount a)))
  (define (balance) amout)

这个模块导出了两个函数

  • deposit 接受一个数字返回某个值
  • balance 返回一个数字
    但一个模块导出一个函数,它建立了两个交流通道。它自己作为服务器,导入这个函数的模块则作为客户端。如果客户端调用一个函数,它发送一个值到服务器。相反的,如果这个函数调用结束函数返回一个值,服务器发送一个值给客户端。客户端和服务器的区别很重要,因为当出现错误,会把错误归咎到某个部分。
    ->本身并不是契约,它是一个契约联接符。

7.2.1->样式

如果你使用数学函数,你可能更愿意让契约箭头出现在领域和范围之间,而不是在开头。事实上,你确实可以这么做

  (provide (contract-out
                 [deposit (number? . -> . any)]))

在racket里面一个s表达式如果含有两个点的符号在中间,读取器则会重排s表达式让符号在前面。所以它等价于

  (-> number? any)

7.2.2使用 define/contract 和 ->

define/contract形式也能使用契约来定义。

  (define/contract (deposit amount)
    (->number? any)
    ; implementation goes here
    ...)

这种使用方式有两个影响
1 因为调用的时候总是检查契约,即使在模块内部。所以,它会造成性能下降。特别是在循环和递归的时候。
2 在某些情况下,同模块调用时,可能有一些更加宽松的输入限制。在这种情况下,define/contract建立的限制太严格了。
7.2.3any和any/c
any契约匹配任何一种结果,它只能使用在范围的位置。除此之外,我们能使用void?契约,表示函数会返回(void)值。void?契约会被契约检测系统检查,当函数返回值的时候。相反的,any契约告诉契约检测系统不用检查返回值,它告诉客户端模块服务器不会对函数返回值作出任何承诺,可以单个值也可以多值。
any/c和any类似,但是,any/c代表一个单值。

  (define (f x) (values (+ x 1) (- x 1)))

上面这个函数匹配(-> integer? any),但是不匹配(-> integer? any/c)。当承诺返回单个结果很重要的时候使用any/c,当竟可能少的承诺返回结果时使用any。

7.2.4定制自己的契约

  #lang racket
   (define (amount? a)
    (and (number? a) (integer? a) (exact? a) (>= a 0)))

    (provide (contract-out
      ; an amount is a natural number of cents
      ; is the given number an amount?
      [deposit (-> amount? any)]
      [amount? (-> any/c boolean?)]
      [balance (-> amount?)]))

      (define amount 0)
      (define (deposit a) (set! amount (+ amount a)))
      (define (balance) amount)

上面这个模块自定义了amount?函数作为契约。如果客户端无法理解amount?,那就没有任何意义。所以模块也导出了amount?谓词本身。我们可使用natural-number/c替代amount?,因为它们的约束恰好相同。

  (provide (contract-out
                [depsoit (-> natural-number/c any)]
                [balance (-> natural-number/c)]))

每个接受一个参数的函数都能被当做谓词在契约里使用。所以契约组合器变的很有用。比如and/c和or/c.
(define amount/c
(and/c number? integer? exact? (or/c positive? zero?)))
(provide (contract-out
[deposit (-> amount/c any)]
[balance (-> amount/c)]))

7.2.5 高阶函数契约

函数契约不是只能使用简单的谓词在他们的领域和范围。任何这里讨论的组合器,包括函数契约本身,都能被当做契约来使用在函数的参数和结果上。

  (->integer? (-> integer? integer?))

它表示接受一个参数并返回一个函数,这个函数接受一个参数并最终返回一个结果。
类似的还有

  (-> (->ineger? integer?) integer?)

7.2.6 契约消息tempN

如果在契约里传入一个lambda表达式(匿名函数),则调用的时候错误信息会显示tempN,如果想改进这种错误提示,可以先申明一个有函数名的谓词判断函数,那么提示就会变得清晰。

7.2.7解释契约错误信息

一般来说,契约的错误信息包含下面留个方面

  • 一个和约束相关的方法或函数,并有短语contract violation或者broken its contract。这个短语取决于客户端还是服务器违反了契约。
    deposit: contract violation
  • 一个描述希望值和实际值
    expected: amount
    given:-10
  • 完整的契约加上那个被违反了
    in: the ist argument of
    (-> amount any)
  • 契约所属的模块
    contract from: improved-bank-server
  • 归咎于谁
    blaming:top-level
    (assuming the contract is correct)
  • 源代码出现的位置
    at: eval:5.0
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容