使用合约定义 Typed Racket 结构

Defining a Typed Racket structure with a contract

有没有一种方法可以在 Typed Racket 中为整个结构定义一个(类型化的)结构?在我的特殊情况下,我有一个将两个列表作为字段的结构,我希望这两个列表的长度相同。

我看过:

理想情况下,我想立即将合同绑定到结构(如 define/contract),但我愿意在 provide 类型的过程时添加合同。但是,我目前单独提供识别器和访问器过程,而不是使用 struct-out(这样我可以排除或重命名单个过程),我想保持这种灵活性。

现在我有这样的东西:

(provide
    (rename-out
        [MyStruct my-struct]
        [MyStruct? my-struct?]
        [MyStruct-foo my-struct-foo]
        [MyStruct-bar my-struct-bar]
    )
)

(struct MyStruct (
    [foo : (Listof Symbol)]
    [bar : (Listof Any)]
))

哇。我很惊讶在 Typed Racket 中做到这一点有多么困难。在普通(无类型)Racket 中,它就像添加 #:guard 一样简单,当你在 Typed Racket 中制作 struct. Unfortunately, the struct 形式时不支持它。

因此,为了解决这个问题,我会生成一个具有私有(对于模块)构造函数名称的结构类型,然后创建您自己的构造函数,该函数实际上执行您希望它检查的契约。

最终看起来像这样:

(struct env ([keys : (Listof Symbol)]
             [values : (Listof Any)])
  #:constructor-name internal-env)

(: make-env (-> (Listof Symbol) (Listof Any) env))
(define (make-env k v)
  (unless (= (length k) (length v))
    (raise-arguments-error 'env
                           "env key and value counts don't match"
                           "keys" k
                           "values" v))
  (internal-env k v))

现在,当您提供结构时,只需不提供 internal-env,而是提供 make-env 函数:

(provide (except-out (struct-out env)
                     internal-env)
         make-env)

现在,当我构造一个环境时,我得到一个(动态)检查以确保列表长度匹配:

> (make-env '() '())
#<env>
> (make-env '(a) '(1))
#<env>
> (make-env '(a) '())
env: env key and value counts don't match
  keys: '(a)
  values: '()

您可能会发现使用 Racket 中的保护函数定义和提供结构定义,然后 require/typed-ing 并在 Typed Racket 中对结构进行类型注释比尝试在 Typed Racket 中实现相同的效果更简单。