I don’t think you want to use a macro to do this.

You could detect, at compile-time, whether or not the provided argument
is a string within the macro. The best way to do this would probably be
to use the “str” syntax class, which detects strings within a
syntax-parse pattern. However, this has a significant drawback: users
cannot provide expressions that evaluate to strings, only string
literals. For example, this will not work:

  (let ([str "some string"])
    (throws (boom) str "should be treated like a string"))

Of course, this makes sense, given that macros operate entirely at
compile-time. What you really want here is a function, not a macro. You
can write a simple function that will accept a procedure or a string and
will produce a procedure:

  (define/contract (value->exn-predicate x)
    (-> (or/c string? (-> any/c any/c))
        (-> any/c any/c))
    (if (procedure? x) x
        (λ (exn)
          (and (exn? exn)
               (equal? (exn-message exn) x)))))

In fact, throws can be implemented as a function as well, if you just
pass the function to be invoked instead of an expression that throws an
exception itself:

  (define (throws exn-generator exn-value msg)
    (with-handlers ([(value->exn-predicate exn-value)
                     (λ (exn) (println "caught"))])
      (exn-generator)))

Generally, prefer functions over macros when functions will do.
Functions are both more flexible (they can be used as values, and
therefore can be used higher-order) and more predictable (function
invocations have very predictable evaluation semantics, while macros
might not).

> On Jul 16, 2016, at 11:16, David Storrs <david.sto...@gmail.com> wrote:
> 
> I'm trying to write a macro to test expected exceptions.  I'd like it
> to have the following forms:
> 
> (throws exception-generator-function proc msg)
> (throws exception-generator-function string msg)
> 
> Where 'proc' would be something like exn:fail? and 'string' would be
> matched against the message inside the exception.
> 
> I got all the pieces of this working but not playing nice together.
> There's three elements in the pattern for both versions, so the only
> way to distinguish which to use is by the types of the arguments.  I
> looked at syntax classes but made no headway on grokking that.  I saw
> the #:when keyword for patterns and thought that would do what I
> needed.  The following code seems like it should work, but it doesn't.
> What am I missing?
> 
> 
> (define-syntax (throws stx)
>  (syntax-parse stx
>                [(_ boom pred msg)
>                 #:when (lambda () (procedure? #'pred))
>                 #'(with-handlers
>                    ([(lambda (x) #t) ;; already checked it's a proc
>                      (lambda (xcpt)
>                        (println "procedure form"))])
>                    boom)]
> 
>                [(_ boom pred msg)
>                 #:when (lambda () (string? #'pred))
>                 #'(with-handlers
>                    ([(lambda (x) #t) ;; already checked it's a string
>                      (println "string form")])
>                    boom)]))
> 
> (define (boom) (raise-argument-error 'boom "PEBKAC" 18)) ;; random choices
> (throws (boom) exn:fail? "should trigger the proc form")
> (throws (boom) "PEBKAC"  "should trigger the string form")

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