Racket 球拍合同依赖性评估两次?

Racket 球拍合同依赖性评估两次?,racket,contract,Racket,Contract,输出是 #lang racket (module inside racket (provide (contract-out [dummy (->i ([x (lambda (x) (begin (displayln 0) #t))] [y (x) (lambda (y) (begin (displayln 1) #t))] [z (x y) (lambda (z) (beg

输出是

#lang racket

(module inside racket
  (provide
    (contract-out
      [dummy (->i ([x       (lambda (x) (begin (displayln 0) #t))]
                   [y (x)   (lambda (y) (begin (displayln 1) #t))]
                   [z (x y) (lambda (z) (begin (displayln 2) #t))]
                   )
                  any
                  )]
      )
    )

  (define (dummy x y z) #t)
  )

(require 'inside)

(dummy 1 2 3)
我不清楚为什么将
x
y
作为依赖项会要求相应的守卫再次开火

->i
的文档似乎没有提到这种行为


任何人都可以解释一下这一点吗?

这对我来说就像对你一样让人困惑,所以我抓住了这个机会。下面是对我的发现的总结

->i
combinator生成一个依赖契约,该契约使用本文中介绍的indy-burn语义。本文提出的关键思想是,对于从属合同,实际上可能有三方因违反合同而受到指责

对于正常功能合同,有两个潜在的犯罪方。第一个是最明显的一个,即调用方。例如:

>(定义/合同(foo x)
(整数?->.string?)
(数字->字符串x))
>(福“你好”)
foo:违反合同
期望值:整数?
给:“你好”
in:的第一个参数
(->整数?字符串?)
合同来自:(职能部门foo)
责备:匿名模块
(假设合同是正确的)
第二个潜在犯罪方是功能本身;也就是说,实现可能与合同不匹配:

>(定义/合同(条形图x)
(整数?->.string?)
十)
>(第1栏)
酒吧:违反了自己的合同
承诺:字符串?
制作:1
在:范围
(->整数?字符串?)
合同来源:(功能栏)
责备:(功能条)
(假设合同是正确的)
这两种情况都很明显。然而,
->i
合同引入了第三个潜在的犯罪方:合同本身

由于
->i
契约可以在契约附件时执行任意表达式,因此它们可能会违反自身。考虑以下合同:

(>i([mk-ctc(整数?->.contract?))
[val(mk ctc)(mk ctc“你好”)]
[结果编号/c])
这是一份有点愚蠢的合同,但很容易看出这是一份顽皮的合同。它承诺只使用整数调用
mk-ctc
,但依赖表达式
(mk-ctc“hello”)
使用字符串调用它!责备调用函数显然是错误的,因为它无法控制无效的契约,但是责备契约函数也可能是错误的,因为契约可以完全独立于它所附加的函数来定义

为了说明这一点,考虑一个多模块的例子:

#朗球拍
(模块m1)球拍
(提供反恐委员会)
(定义反恐委员会)
(->i([f(整数?->.integer?)]
[v(f)(λ(v)(>(f v)0))]
[结果(如有/c]))
(模块m2)球拍
(需要(子模块“.”m1))
(提供(外包[foo ctc]))
(定义(foo f v)
(f#f))
(需要平方米)
在本例中,
ctc
契约在
m1
子模块中定义,但使用契约的函数在单独的子模块
m2
中定义。这里有两种可能的责任情景:

  • foo
    函数显然无效,因为它将
    f
    应用于
    #f
    ,尽管契约为该参数指定了
    (integer?->.integer?
    )。您可以通过调用
    foo
    在实践中看到这一点:

    0
    0
    1
    1
    2
    #t
    
    合同错误的顶部是相同的(Racket为这类违反合同的行为提供相同的“违反自己的合同”消息),但责任信息是不同的!现在它将责任归咎于m1,这是合同的实际来源。这是印第谴责党

  • 这种区别意味着合同必须适用两次。它将它们应用于每个不同的责任方的信息:首先,它将它们应用于合同责任,然后它将它们应用于功能责任

    从技术上讲,扁平合同可以避免这种情况,因为在初始合同附件过程之后,扁平合同永远不会发出违反合同的信号。然而,
    ->i
    combinator目前没有实现任何此类优化,因为它可能不会对性能产生重大影响,而且契约的实现已经相当复杂(尽管如果有人想要实现它,它可能会被接受)

    但是,一般来说,契约应该是无状态且幂等的(平面契约应该是简单的谓词),因此不能保证不会发生这种情况,
    ->i
    只是使用它来实现其细粒度的信息


    一,。事实证明,
    ->d
    合同组合器根本没有抓住这个问题,因此
    add1
    最终在这里引发了合同违约。这就是为什么创建了
    ->i
    ,这也是为什么
    ->i
    ->d
    更受青睐的原因

    > (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) > (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)