类型化球拍中的自定义哈希集导致语法对象违反合同

Custom hash set in typed racket leads to contract violation with syntax objects

我有一个custom set,我想用在打字球拍上。我 要求它使用 require/typed#:opaque custom-set? 操作说明。它有效,除了我在 运行 时代码失败 使用语法对象调用 custom-set?

我有如下内容:

#lang typed/racket/base

(module UNTYPED racket/base
  (require racket/set)
  (provide custom-set?
           make-immutable-custom-set)

  (define-custom-set-types custom-set
    #:elem? identifier?
    (λ (id1 id2) (eq? (syntax-e id1) (syntax-e id2)))))

(require/typed 'UNTYPED
  [#:opaque MySet custom-set?]
  [make-immutable-custom-set ((Listof Identifier) -> MySet)])

(custom-set? (make-immutable-custom-set (list #'foo #'bar)))  ;; #t
(custom-set? '())  ;; #f

哪种类型检查 returns #t#f 符合预期。

现在,如果我尝试使用语法对象调用相同的 custom-set? 谓词:

(custom-set? #'(foo bar))

然后,我得到以下合同违规而不是 #f

custom-set?: broke its own contract
  Attempted to use a higher-order value passed as `Any` in untyped code: #<syntax:stdin:: a>
  in: the 1st argument of
      a part of the or/c of
      (or/c
       struct-predicate-procedure?/c
       (-> Any boolean?))
  contract from: (interface for custom-set?)
  blaming: (interface for custom-set?)

UNTYPED 模块的相同调用有效,并且 returns #f 符合预期。你能告诉我为什么语法对象会在这里破坏契约吗?我可以解决这个问题吗?

这是一个由语法对象契约不够好引起的错误。具体来说,syntax/c 合约适用于平面合约,而 Typed Racket 希望在涉及 Any 类型时使用伴侣合约。

这里涉及的Any是由#:opaque引入的隐式Any需要谓词custom-set?。它出现在输入中,保护以类型化代码开始并以非类型化代码结束的值。由于未类型化的代码可能会尝试与类型化的高阶值混淆,因此它必须用伴侣契约包装 any-wrap/c(Typed Racket 内部)。

any-wrap/c 用于保护潜在的高阶值,以及可能包含高阶值的容器值。如果某些部分是可变的,或者如果某些部分中有函数,则不允许未类型化代码改变该数据或调用该函数。

语法对象是容器。它们可以在 "syntax-e" 和语法属性中包含任意值。理想情况下,Typed Racket 的 any-wrap/c 合约应该将语法对象包装在保护这些地方的伴侣合约中。不幸的是,合同系统中的 syntax/c 合同还不够好。由于这种语法对象被认为是 "unsafe" 容器,如果 any-wrap/c 不能安全地包装它们,它必须引发合同错误。

我认为这个问题在未来可能得到解决的唯一方法是改进 syntax/c 以与伴侣合同一起使用。之后 any-wrap/c 可以像列表一样考虑语法对象安全容器。