> On Jul 16, 2016, at 2:16 PM, 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")

A few problems with this.

One, you shouldn't put extra (lambda () ...)s around the #:when conditions. A 
lambda is a truthy value, since it's not false, so those #:when checks weren't 
checking anything at all.

Two, once you remove those lambdas, (procedure? #'pred) will always fail 
because #'pred isn't the actual predicate, it's an expression, which might 
eventually evaluate to predicate, or it might evaluate something else. At 
compile-time, you just don't know yet. What you could do is check that it's an 
identifier, with (identifier? #'pred).

Three, (string? #'pred) will also always fail, because #'pred is an expression. 
Now for this you can actually check that it is a string, by using (string? 
(syntax-e #'pred)).

Four, in the second clause you have (println "string form") as the handler 
function. You probably meant to put a lambda around it like you did for the 
first clause: (lambda (xcpt) (println "string form")).

Five, did you mean to actually use the pred argument in the exception 
predicates of the with-handlers forms? 

Six, did you mean to use the msg argument for anything?

With the first five of these addressed, the code looks like this:

#lang racket
(require (for-syntax racket/base syntax/parse))
(define-syntax (throws stx)
  (syntax-parse stx
    [(_ boom pred msg)
     #:when (identifier? #'pred)
     #'(with-handlers
           ([pred
             (lambda (xcpt)
               (println "procedure form"))])
         boom)]
    [(_ boom pred msg)
     #:when (string? (syntax-e #'pred))
     #'(with-handlers
           ([(lambda (x) (string-contains? (exn-message x) pred))
             (lambda (xcpt)
               (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")

Although there is an easier, better way to check whether an argument is an 
identifier or a string, since you're using syntax-parse. You can use the id 
syntax class in the first clause, and the str syntax class in the second 
clause. Then the code looks like this:

#lang racket
(require (for-syntax racket/base syntax/parse))
(define-syntax (throws stx)
  (syntax-parse stx
    [(_ boom pred:id msg)
     #'(with-handlers
           ([pred
             (lambda (xcpt)
               (println "procedure form"))])
         boom)]
    [(_ boom pred:str msg)
     #'(with-handlers
           ([(lambda (x) (string-contains? (exn-message x) pred))
             (lambda (xcpt)
               (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