Is there a shorthand way to update a specific struct field in racket?

前端 未结 3 1821
执念已碎
执念已碎 2021-02-12 23:40

Suppose I have a struct with many fields:

(struct my-struct (f1 f2 f3 f4))

If I am to return a new struct with f2 updated, I have

3条回答
  •  南方客
    南方客 (楼主)
    2021-02-13 00:02

    I like Alexis' macro! It has more of the "lens" flavor you wanted.

    I also want to point out struct-copy. Given:

    #lang racket
    (struct my-struct (f1 f2 f3 f4) #:transparent)
    (define s (my-struct 1 2 3 4))
    

    You can use struct-copy to set a value:

    (struct-copy my-struct s [f2 200])
    ;;=> (my-struct 1 200 3 4)
    

    Or to update a value:

    (struct-copy my-struct s [f2 (* 100 (my-struct-f2 s))])
    ;;=> (my-struct 1 200 3 4)
    

    Update: Thinking about this more, here are a few more ideas.

    You could also update using match's struct* pattern:

    (match s
      [(struct* my-struct ([f2 f2]))
       (struct-copy my-struct s [f2 (* 100 f2)])])
    

    Of course, that's very verbose. On the other hand the struct* pattern makes it easy to define a macro using the simpler define-syntax-rule:

    ;; Given a structure type and an instance of it, a field-id, and a
    ;; function, return a new structure instance where the field is the
    ;; value of applying the function to the original value.
    (define-syntax-rule (struct-update struct-type st field-id fn)
      (match st
        [(struct* struct-type ([field-id v]))
         (struct-copy struct-type st [field-id (fn v)])]))
    
    (struct-update my-struct s f2 (curry * 100))
    ;;=> (my-struct 1 200 3 4)
    

    Of course, setting is the special case where you give update a const function:

    (struct-update my-struct s f2 (const 42))
    ;;=> (my-struct 1 42 3 4)
    

    Finally, this is like struct-update, but returns an updater function, in the spirit of Alexis' macro:

    (define-syntax-rule (struct-updater struct-type field-id)
      (λ (st fn)
        (struct-update struct-type st field-id fn)))
    
    (define update-f2 (struct-updater my-struct f2))
    
    (update-f2 s (curry * 100))
    ;;=> (my-struct 1 200 3 4)
    

    I'm not saying that any of this is idiomatic or efficient. But it's possible. :)

提交回复
热议问题