You may wish to study Alexis’s struct updater package: 
https://github.com/lexi-lambda/struct-update 
<https://github.com/lexi-lambda/struct-update> 


> On May 26, 2018, at 11:46 AM, David Storrs <david.sto...@gmail.com> wrote:
> 
> Hi folks,
> 
> I am becoming more converted to the way of structs vs hashes as time goes on 
> and so I sat down to write a macro that would generate functional setters as 
> a convenience.  Code is at bottom of email; I'd appreciate suggestions on how 
> to improve it, in particular better ways of handling macros that have 
> multiple optional parts, e.g.:
>    
>    (struct foo (bar baz jaz))
>    (make-functional-setter foo bar)
>    (make-functional-setter foo baz string?)
>    (make-functional-setter foo jaz path-string? path-string->string)
> 
> I've seen this handled in two ways:
> 
> 1) define-syntax-class with multiple (pattern ...) clauses, which renders the 
> parsing cleaner and easier to understand.  Handling missing parts is still an 
> issue, since using an attribute that wasn't matched is a syntax error, so my 
> questions:
> 
> A) Is there a way to test if a syntax class has a particular attribute before 
> trying to use it?
> 
> B) Alternatively, is there a way to create a null syntax object that expands 
> to nothing?  Not (void), not "", literally nothing.   Then I could have each 
> pattern bind all the attributes via #:with and just have some of them be 
> blank.
> 
> 
> 2) Alternatively, macros with optional parts can be handled by a series of 
> test cases in a syntax-parse, one for each option.  That ends up with a lot 
> of copy/pasted code between the segments.  I'm getting around this via 
> re-parsing but that feels clumsy.  What is the better way?
> 
> 
> 3) There's something I'm not understanding about providing this thing.  I 
> have it in a file called make_functional_setter.rkt:
> 
> #lang racket
> (require (for-syntax racket/syntax syntax/parse))
> (provide make-functional-setter)
> ; code and comments for make-functional-setter, see bottom of email
> 
> When I (require "../make_functional_setter.rkt") on the CLI racket shell, 
> everything works great.  As soon as I try to require it into a file, it fails:
> 
> Contents of file test.rkt:
> 
> #lang racket
> (struct bar (a b))
> (require "../make_functional_setter.rkt") 
> (make-functional-setter bar a)
> (make-functional-setter bar b path-string? ~a)
> 
> When I do "racket test.rkt" on the command line, I get the following 
> exception:
> 
> bar?: unbound identifier in module
>   context...:
>    #(2082 module test 0) #(3901 module) #(3902 module struct 0) #(25633 macro)
>    #(25639 use-site) #(25818 local) #(25820 intdef)
>   other binding...:
>    #f
>    #(2081 module) #(2082 module test 0)
>   in: bar?
>   context...:
>    standard-module-name-resolver
> 
> I've been through this in the DrRacket macro stepper but that hasn't helped 
> me any.  (It rarely does.)
> 
> I'm assuming it's a phase issue, so I've played around with for-syntax, 
> for-template, and for-meta but apparently I do not understand phases well 
> enough.
> 
> What could be causing this?
> 
> 
> 
> I've been going through all the macro- and syntax-related docs that I can 
> find, but there's a lot of them and they take a few re-reads to sink in, so 
> there's probably something exactly for this that I haven't seen.
> 
> Code is below and at 
> https://gist.github.com/dstorrs/a53ec8736551eb5745d1d5fd265b5062 
> <https://gist.github.com/dstorrs/a53ec8736551eb5745d1d5fd265b5062> in case 
> Gmail mangled the formatting.
> 
> 
> ;;     make-functional-setter: macro for generating non-mutating field
> ;;     setter functions for a struct
> ;;
> ;; Define a struct:  (struct book (title current-page filepath) #:transparent)
> ;;
> ;; Generate 'set-book-title', 'set-book-current-page', and 
> 'set-book-filepath'.
> ;; All of these take two to four arguments: the 'book' struct to update, the 
> ;; new value, an optional contract, and an optional wrapper function.
> ;;
> ;;    (make-functional-setter book title)
> ;;    (make-functional-setter book current-page  exact-positive-integer?)
> ;;    (make-functional-setter book filepath      path-string?            
> path-string->string)
> ;;
> ;; Details:
> ;;    set-book-title           accepts any value, regardless of sensibility
> ;;    set-book-current-page    accepts only exact-positive-integer?s, else 
> contract violation
> ;;    set-book-filepath        accepts only path-string?s, converts to string 
> before storing
> ;;
> ;; Examples:
> ;;    (define b (book "Foundation" 297 (build-path "/foo/bar")))
> ;;    b                                                ; (book "Foundation" 
> 297 "/foo/bar")
> ;;    (set-book-title b (hash))                        ; (book (hash) 297 
> "/foo/bar")
> ;;    (set-book-current-page b 99)                     ; (book "Foundation" 
> 99 "/foo/bar")
> ;;    (set-book-current-page b 'x)                     ; ERROR!  Contract 
> violation
> ;;    (set-book-filepath b (build-path "/foo"))        ; (book "Foundation" 
> 297 "/foo")
> ;;
> (define-syntax (make-functional-setter stx)
>   (syntax-parse stx
>     ; First, grab the name of the struct and the field we're making
>     ; this for.  We'll build some stuff here then re-parse instead of
>     ; copy/pasting for every pattern match
>     [(_ type-name field-name ignored ...)
>      (with-syntax* ([func-name   (format-id #'type-name "set-~a-~a" 
> #'type-name #'field-name)]
>                     [func-header #'(func-name the-struct val)]
>                     [definer     #'define]
>                     [type-pred   (format-id #'type-pred "~a?" #'type-name)]
>                     [func-body   #'(struct-copy type-name the-struct 
> [field-name val])]
>                     )
>        (syntax-parse stx
>          [(_ _ _) #'(definer func-header func-body)]
>          [(_ _ _ field-contract:expr ignored ...)
>           (with-syntax ([definer #'define/contract]
>                         [func-contract #'(-> type-pred field-contract 
> type-pred)])
>             (syntax-parse stx
>               [(_ _ _ _) #'(definer func-header func-contract func-body)]
>               [(_ _ _ _ wrapper:expr)
>                #'(definer func-header
>                    func-contract
>                    (struct-copy type-name the-struct [field-name (wrapper 
> val)]))]))]))]))
> 
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to racket-users+unsubscr...@googlegroups.com 
> <mailto:racket-users+unsubscr...@googlegroups.com>.
> For more options, visit https://groups.google.com/d/optout 
> <https://groups.google.com/d/optout>.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to