Overloading a struct constructor?

后端 未结 2 1324
死守一世寂寞
死守一世寂寞 2021-01-19 19:36

My question is the same as the one asked here, but the answer given there doesn\'t actually work.

I have a bunch of structs inheriting from a parent struct A

相关标签:
2条回答
  • 2021-01-19 19:54

    Looking further into the new struct keywords that soegaard mentioned, I think I've come up with a solution that's a bit cleaner and, more importantly, much easier to abstract into a macro (those modules and requires were giving me a really hard time). I thought I'd share it for future reference. Again, this requires one of the nightly Racket builds. I'm using 6.5.0.7.

    (Note: the use of case-lambda over defining keyword arguments here is just my preference.)

    #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))
    

    Sample usage:

    > (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"
    

    In short: instantiation, matching, and accessing fields seem to work as you would expect if you'd just used struct. The struct id can also be used as an identifier without any syntax issues. I don't really think that part has much practical use, but I thought it was nice.

    0 讨论(0)
  • 2021-01-19 20:09

    The struct construct will define two entities with the name of the struct. A constructor and a transformer binding that has information on the struct.

    In order to avoid the "duplicate identifier" error you can use #:omit-define-syntaxes.

    An alternative is to define the struct in a submodule and export only the things you need (and possibly renaming some of the identifiers).

    #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")
    

    Output:

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

    EDIT

    A solution that works with match is possible with the help of a match expander. Note: You need at least version 6.5 for this to work due the the use of #:extra-name.

    #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])
    

    Output:

    (horse "black")
    (horse "red")
    "blue"
    
    0 讨论(0)
提交回复
热议问题