重载结构构造函数?

Overloading a struct constructor?

我的问题与 here 的问题相同,但那里给出的答案实际上不起作用。

我有一堆继承自父结构 A 的结构,它有两个字段,我想为其所有后代提供可选字段。以前我使用的是 #:auto,但事实证明这并不是我想要的,因为它破坏了 struct-copy 之类的方法,而且我确实希望能够在结构创建时有选择地为这些字段提供值.

我发现了一些关于结构的可选参数的其他问题,但答案都建议定义自定义构造函数并改用它。不幸的是,我已经有很多使用常规构造函数的代码,所以我真正想要的是让这个自定义构造函数与结构本身同名。我链接的问题的答案正是我要找的,但不幸的是它只在 REPL 中有效,因为允许重复定义。在 REPL 之外,我收到类似 module: duplicate definition for identifier in: exn:my-app 的错误(当 运行 链接问题的答案时)。

编辑:我知道重复定义问题是因为结构 id 也绑定到转换器。我不想阻止这个定义的发生;我想要一个名称绑定到该结构的构造函数和转换器,其中构造函数不是默认构造函数。

有没有一种方法可以很好地与现有代码配合使用?

struct 构造将定义两个具有结构名称的实体。具有结构信息的构造函数和转换器绑定。

为了避免 "duplicate identifier" 错误,您可以使用 #:omit-define-syntaxes.

另一种方法是在子模块中定义结构并仅导出您需要的内容(并可能重命名一些标识符)。

#lang racket
(struct horse (color)
  #:constructor-name make-horse
  #:omit-define-syntaxes
  #:transparent)

(define (horse #:color [color "black"])
  (make-horse color))

(horse)
(horse #:color "red")

输出:

(horse "black")
(horse "red")

编辑

在匹配扩展器的帮助下,可以使用匹配解决方案。 注意:由于使用 #:extra-name.

,您至少需要 6.5 版才能正常工作
#lang racket

(module horse racket
  (provide (struct-out Horse)
           make-horse)
  (struct horse (color)
    #:extra-name Horse
    #:extra-constructor-name make-horse
    #:transparent))

(require 'horse)

; the custom horse constructor
(define (my-make-horse #:color [color "black"])
  (make-horse color))

(define-match-expander horse
  ; match expander
  (λ (pat)
    (syntax-case pat ()
      [(_horse more ...)
       #'(Horse more ...)]))
  ; constructor
  (λ (stx)
    (syntax-case stx ()
      [(_horse arg ...)
       (syntax/loc stx
         (my-make-horse arg ...))])))

(horse)
(horse #:color "red")

(match (horse #:color "blue")
  [(horse color) color])

输出:

(horse "black")
(horse "red")
"blue"

进一步研究 soegaard 提到的新结构关键字,我想我想出了一个更简洁的解决方案,更重要的是,更容易抽象成一个宏(那些模块和要求给了我一个非常艰难的时期)。我想我会分享它以供将来参考。同样,这需要每晚 Racket 构建之一。我正在使用 6.5.0.7.

(注意:在这里使用 case-lambda 来定义关键字参数只是我的偏好。)

#lang racket

(require (for-syntax syntax/transformer))

(struct horse (color)
  #:name Horse
  #:constructor-name Horse
  #:transparent)

(define make-horse
  (case-lambda
    [() (Horse "black")]
    [(color) (Horse color)]))

(define-match-expander horse
  ; match expander
  (λ (pat)
    (syntax-case pat ()
      [(_ more ...) #'(Horse more ...)]))

  ; constructor
  ; edit: changing this to use make-variable-like-transformer,
  ;  as suggested in the comments.
  #;(syntax-id-rules ()
      [(_ args ...) (make-horse args ...)]
      [horse Horse])
  (make-variable-like-transformer #'make-horse))

示例用法:

> (define black-beauty (horse))
> black-beauty
(horse "black")

> (define ginger (horse "red"))
> ginger
(horse "red")

> (match black-beauty
    [(horse color) color])
"black"

> (match ginger
    [(horse color) color])
"red"

> (horse-color black-beauty)
"black"

> (horse-color ginger)
"red"

简而言之:如果您刚刚使用 struct,实例化、匹配和访问字段似乎可以像您期望的那样工作。 struct id 也可以用作标识符,没有任何语法问题。我真的不认为那部分有多大实际用处,但我觉得它很好。