Search code examples
exceptionerror-handlingrackettyped-racket

Unexpected behaviour of `with-handlers` in typed Racket


I am trying to handle certain kinds of errors in typed Racket. The following code works as expected in both typed and untyped Racket

(with-handlers
    ([exn:fail:contract:divide-by-zero?
      (lambda (e)
        (raise e))])
    (let ([x 0]
          [y 1])
      (display (/ y x))))

resulting in the error message

/: division by zero

A slight elaboration works in untyped Racket:

(with-handlers
    ([exn:fail:contract:divide-by-zero?
      (lambda (e)
        (displayln "oops!")
        (raise e))])
    (let ([x 0]
          [y 1])
      (display (/ y x))))
oops!
/: division by zero

But gives the following error message in typed Racket:

Type Checker: No function domains matched in function application:
Domains: (U (Rec flat (U (Immutable-HashTable flat flat) (Immutable-Vectorof flat) (Pairof flat flat) Boolean Bytes Char Complex Keyword Null String Symbol)) exn) Any 
         (U (Rec flat (U (Immutable-HashTable flat flat) (Immutable-Vectorof flat) (Pairof flat flat) Boolean Bytes Char Complex Keyword Null String Symbol)) exn) 
Arguments: Any
 in: (raise e)

Can someone explain what is going on here, or suggest a workaround?


Solution

  • While in normal Racket, the first argument to raise can be any value, for some reason in Typed Racket it's limited to a list of specific types, including the exn hierarchy of structures.

    I've found you almost always have to explicitly specify the types of arguments to functions; Typed Racket typically doesn't infer them and gives them the generic Any type. Thus, to satisfy raise's type:

    #lang typed/racket/base
    
    (with-handlers
        ([exn:fail:contract:divide-by-zero?
          (lambda ([e : exn]) ; The type of a struct is usually the name of the struct
            (displayln "oops!")
            (raise e))])
        (let ([x 0]
              [y 1])
          (display (/ y x))))