這和我一樣對你感到困惑,所以我藉此機會ask this question on the Racket mailing list。接下來是試圖總結我發現的內容。
->i
組合子使用文獻Correct Blame for Contracts中提供的indy blame語義生成依賴合約。文件中提出的關鍵思想是,在有依賴合同的情況下,實際上可能需要責備三方,因爲違反合同。
正常的功能合同有兩個潛在的罪犯。第一個是最明顯的一個,這是來電者。例如:
> (define/contract (foo x)
(integer? . -> . string?)
(number->string x))
> (foo "hello")
foo: contract violation
expected: integer?
given: "hello"
in: the 1st argument of
(-> integer? string?)
contract from: (function foo)
blaming: anonymous-module
(assuming the contract is correct)
第二個潛在的有罪方是函數本身;
> (define/contract (bar x)
(integer? . -> . string?)
x)
> (bar 1)
bar: broke its own contract
promised: string?
produced: 1
in: the range of
(-> integer? string?)
contract from: (function bar)
blaming: (function bar)
(assuming the contract is correct)
這兩種情況是非常明顯的:那就是,執行可能不會在合同相匹配。然而,->i
合同引入了一個第三潛在的有罪方:合同本身。
由於->i
合同可以在合同附件時間執行任意表達式,因此他們可能自己違反。考慮以下合約:
(->i ([mk-ctc (integer? . -> . contract?)])
[val (mk-ctc) (mk-ctc "hello")])
[result any/c])
這是一個有點傻的合同,但它很容易地看到,這是一個頑皮的一個。它承諾只用整數調用mk-ctc
,但相關表達式(mk-ctc "hello")
用字符串調用它!指責調用函數顯然是錯誤的,因爲它無法控制無效合同,但它也可能是也是是錯誤的,因爲可以將合同定義爲完全與其所附函數分離至。
有關此方法的說明,考慮一個多模塊例如:
#lang racket
(module m1 racket
(provide ctc)
(define ctc
(->i ([f (integer? . -> . integer?)]
[v (f) (λ (v) (> (f v) 0))])
[result any/c])))
(module m2 racket
(require (submod ".." m1))
(provide (contract-out [foo ctc]))
(define (foo f v)
(f #f)))
(require 'm2)
在這個例子中,ctc
合同在m1
子模塊定義的,但是,使用合同的功能被在單獨的定義子模塊,。有兩種可能的情景怪在這裏:
的foo
功能顯然是無效的,因爲它適用於f
#f
,儘管合同這樣的說法指定(integer? . -> . integer?)
。你可以通過調用foo
在實踐中看到這一點:
> (foo add1 0)
foo: broke its own contract
promised: integer?
produced: #f
in: the 1st argument of
the f argument of
(->i
((f (-> integer? integer?))
(v (f) (λ (v) (> (f v) 0))))
(result any/c))
contract from: (anonymous-module m2)
blaming: (anonymous-module m2)
(assuming the contract is correct)
我已經強調了,包括怪信息的合同錯誤的地方,你可以看到它指責m2
,這是有道理的。這不是一個有趣的案例,因爲這是非依賴案件中提到的第二個潛在的指責方。
但是,ctc
合約實際上有點不對!請注意0上的合同適用f
到v
,但它從不檢查v
是一個整數。因此,如果v
是別的,f
將以無效的方式被調用。 您可以通過給非整數值爲v
看到此行爲:
> (foo add1 "hello")
foo: broke its own contract
promised: integer?
produced: "hello"
in: the 1st argument of
the f argument of
(->i
((f (-> integer? integer?))
(v (f) (λ (v) (> (f v) 0))))
(result any/c))
contract from: (anonymous-module m1)
blaming: (anonymous-module m1)
(assuming the contract is correct)
合同錯誤的頂部是相同的(球拍提供相同的「打破了自己的合同」的消息對這類合同違規),但是這些責任信息是不同的!現在責備m1
,這是合同的實際來源。這是指責派對的indy。
這個區別是什麼意思是合同必須應用兩次。它將它們應用於每個明確責任方的信息:首先它將它們應用於合同責任,然後將它們應用於功能責任。
從技術上講,對於平面合同,這是可以避免的,因爲在初始合同附件處理後,平面合同永遠不會發出違反合同的信號。然而,目前的組合器並沒有實現任何這樣的優化,因爲它可能不會對性能產生重大影響,並且合同實現已經相當複雜(儘管如果有人想實施它,它可能會被接受)。
一般來說,雖然,合同預計是無狀態的和冪(平合約預計將簡單謂詞),所以沒有任何真正的保證,這不會發生,->i
只是使用它來實現它的細粒度的責備信息。
1.事實證明,在->d
合同組合子不抓住這個問題好,所以add1
結束了在這裏養違反合同。這就是爲什麼->i
創建的原因,這就是爲什麼->i
比->d
更受青睞。