首页 > 解决方案 > 类型化球拍中的自定义哈希集导致语法对象违反合同

问题描述

我有一套定制的套装,我想在打字球拍中使用它。我要求它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

哪种类型检查并返回#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工作并返回。#f你能告诉我为什么语法对象在这里违反合同吗?我可以解决这个问题吗?

标签: rackettyped-racket

解决方案


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

这里Any涉及的是通过要求 predicateAny引入的隐式。它出现在输入中,保护以类型代码开始并以非类型代码结束的值。由于无类型代码可能会尝试与有类型的高阶值混淆,因此必须用伴侣合同(Typed Racket 内部)包装它。#:opaquecustom-set?any-wrap/c

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

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

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


推荐阅读