Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate contracts for keyword functions #19

Open
dstorrs opened this issue Aug 26, 2021 · 0 comments
Open

Generate contracts for keyword functions #19

dstorrs opened this issue Aug 26, 2021 · 0 comments

Comments

@dstorrs
Copy link

dstorrs commented Aug 26, 2021

The following is a snippet from the struct-plus-plus module, available here: https://docs.racket-lang.org/struct-plus-plus/index.html

It is a template metafunction, meaning a function that can be used inside a macro.

Example

(struct++ person
               ([name]
                [age number?]
                [(vegan? #f) boolean?])
              #:transparent)

(person++ #:name "alice" #:age 18)

When run, the above code produces:

> (person "alice" 18 #f)

The contract on the person++ function is:

(->* (#:name any/c #:age number?) (#:vegan? boolean?) person?)

Code

  (require syntax/parse/experimental/template)

  ; Fields have an identifier, optionally followed by a contract, optionally followed by 
  ; a wrapper function:
  ;
  ;   [name]           ; a field to store a person's name
  ;   [name string?]   ; accept only strings
  ;   [name any/c ~a]  ; accept any value, convert it to a string
  ;
  ; A default value may be supplied with any of the forms by replacing `name` with
  ;  `(name val)`, e.g.:   [(name "bob") string?]

  (define-syntax-class field                                                                   
    (pattern (~or id:id
                         [id:id (~optional (~seq cont:expr (~optional wrap:expr)))])
             #:with required? #'#t
             #:with field-contract (template (?? cont any/c))
             #:with wrapper (template (?? wrap identity))
             #:with ctor-arg #`(#,(syntax->keyword #'id) id)
             #:with def #''no-default-given)

    (pattern [(id:id default-value:expr)
                    (~optional (~seq cont:expr (~optional wrap:expr)))]
             #:with required? #'#f
             #:with field-contract (template (?? cont any/c))
             #:with wrapper (template (?? wrap identity))
             #:with ctor-arg #`(#,(syntax->keyword #'id) [id default-value])
             #:with def (template  default-value)))

   ;; Generate the contract for the constructor function.  Must handle optional arguments.  
   ;; This would be called something like the following:
   ;;   (make-ctor-contract  ((field.required? (field.id field.field-contract)) ... predicate))
   ;; NB:   `predicate` would be something like `person?` but its definition is not shown.
   ;;
   (define-template-metafunction (make-ctor-contract stx)
    (define-syntax-class contract-spec
      (pattern (required?:boolean  (name:id contr:expr))))
    ;;
    (syntax-parse stx
      #:datum-literals (make-ctor-contract)
      [(make-ctor-contract (item:contract-spec ...+ predicate))
       (let-values
           ([(mandatory optional)
             (partition (syntax-parser [(flag _) (syntax-e #'flag)])
                        (map (syntax-parser [(flag (name contr))
                                             (quasitemplate (flag (#,(syntax->keyword #'name)
                                                                   contr)))])
                             (syntax->list #'(item ...))))])
         (with-syntax ((((_ (mand-kw mand-contract)) ...) mandatory)
                       (((_ (opt-kw  opt-contract)) ...)  optional))
           (template (->* ((?@ mand-kw mand-contract) ...)
                          ((?@ opt-kw opt-contract) ...)
                          predicate))))]))
bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Oct 11, 2021
from syntax-objects/Summer2021#19
cc @dstorrs

I included a pointer to struct-plus-plus. I'm not planning to include
it as another entry because it does a lot at once ... but maybe
it'd go nicely as a big "capstone" kind of example?
bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Oct 27, 2021
from syntax-objects/Summer2021#19
cc @dstorrs

I included a pointer to struct-plus-plus. I'm not planning to include
it as another entry because it does a lot at once ... but maybe
it'd go nicely as a big "capstone" kind of example?
bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Oct 27, 2021
from syntax-objects/Summer2021#19
cc @dstorrs

I included a pointer to struct-plus-plus. I'm not planning to include
it as another entry because it does a lot at once ... but maybe
it'd go nicely as a big "capstone" kind of example?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant