如何覆盖结构构造函数,同时仍提供结构元数据以在Racket中进行编译?

时间:2019-03-20 14:12:23

标签: struct racket

这似乎类似于Overloading a struct constructor?Overloading a struct constructor之类的问题。但是这些问题都没有解决将重载标识符传递到模块边界之外(通过提供它)的问题。

例如,假设我有一个要重载构造函数的结构:

(struct fish (weight scales))
(define (make-fish [weight 5] [scales 'blue])
  (fish weight scales))

现在,我想提供新的构造函数,使其具有该结构的名称,以使其用法完全透明:

(provide
  (except-out (struct-out fish) fish)
  (rename-out (make-fish fish)))

这在大多数情况下都会起作用。但是可能会出现一些细微的错误。

不能再继承结构,也不能使用match

(require animals/fish)

(struct shark fish (teeth)) ;; ERROR: parent struct type not defined

(define (describe-animal animal)
  (match animal
    [(fish weight scales) ;; ERROR: syntax error in pattern
     (format "A ~a pounds fish with ~a scales" weight scales)]
    [_ "Not a fish"]))

失败:使用匹配扩展器

创建匹配扩展器(链接的问题中可接受的解决方案)。
这将无法正常工作,因为您无法将Match-Expander导出为结构。

#lang racket/base

(require
  (for-syntax
    racket/base
    syntax/transformer)
  racket/match)

(provide
  (except-out (struct-out fish) fish)
  (rename-out (make-fish fish)))

(struct fish (weight scales)
  #:name private-fish
  #:constructor-name private-fish)

(define (make-fish [weight 5] [scales 'blue])
  (private-fish weight scales))

(define-match-expander fish
  (lambda (stx)
    (syntax-case stx ()
      [(_ field ...) #'(private-fish field ...)]))
  (make-variable-like-transformer #'private-fish))

您收到错误:

  

struct-out:标识符未绑定到结构类型信息
  于:鱼
  输入:(禁止输出的鱼)

问题

那么我们如何更改一个结构的构造函数,但仍然允许它在其他结构中提供并用作父结构?

1 个答案:

答案 0 :(得分:3)

使用元数据结构(它只是在编译时定义的结构),您可以在编译时将结构定义封装到一个可用于match和继承的值中。

#lang racket/base

(require
  (for-syntax
    racket/base
    racket/struct-info
    syntax/transformer)
  racket/match)

(provide
  (struct-out fish))

(struct fish (weight scales)
  #:name private-fish
  #:constructor-name private-fish)

(define (make-fish [weight 5] [scales 'blue])
  (private-fish weight scales))

(begin-for-syntax
  ;; we define a struct that will only exist at compile time
  ;; and can encapsulate an identifier
  (struct metadata (ctor struct-info)
    #:property prop:procedure (struct-field-index ctor)
    #:property prop:struct-info (lambda (self) (metadata-struct-info self))))

(define-syntax fish ;; this variable can be used like the initial struct when compiling
  (metadata
    (set!-transformer-procedure
      (make-variable-like-transformer #'make-fish))
    (extract-struct-info (syntax-local-value #'private-fish))))

此结构必须具有特定的属性:prop:procedure(以便它仍然可以用作构造函数)和prop:struct-info(以便matchstruct)可以在以下位置获取结构信息编译时间。

注意

请注意,在下一版的Racket中,由于Alex Knauth提供了PR,因此不再需要set!-transformer-procedure,而只需致电make-variable-like-transformer