Enforcing Function Contracts in Racket with define/contract
Learn how to attach and enforce Racket contracts on functions, test valid and invalid calls, and recover from contract violations using handlers.
14 Aug 2026, 19:01 UTC

Desired outcome
Create a Racket function that automatically validates its arguments and return value against a contract, raising a clear contract‑violation exception when the contract is broken and allowing the caller to handle the exception safely.
Prerequisites
- Racket installation version 6.12 or later (any platform).
- Basic familiarity with
defineand function definition. - Either DrRacket IDE or the command‑line
racketexecutable for running code.
Focused procedure
- Write a contract. Use the
->combinator to describe the domain (input contracts) and range (output contracts). For a function that computes the square root of a non‑negative real number, the contract is(-> real? real?)with an additional precondition that the input is ≥ 0. - Attach the contract to a function. Use
define/contract(or the#:contractkeyword) to bind the contract to the function definition. - Test with valid arguments. Call the function in the REPL or a script with inputs that satisfy the contract; it should return the expected result without raising an exception.
- Test with invalid arguments. Call the function with arguments that violate the contract (e.g., a negative number). The call should raise an exception of type
exn:fail:contractwhose message contains the contract name and the offending value. - Add recovery handling (optional). Wrap the call in
with-handlersto catchexn:fail:contract. Inside the handler you can log the error, substitute a default value, or take alternative action, allowing the program to continue.
Expected checks and recovery options
Checks
- Valid call:
(sqrt-safe 9)returns3and produces no error output. - Invalid call:
(sqrt-safe -1)raises an exception; inspecting the exception yields a message similar to...contract violation: expected: real? given: -1. - Handler test: Wrapping the invalid call in
(with-handlers ([exn:fail:contract? (lambda (e) (display "bad input\n") 0)]) (sqrt-safe -1))prints "bad input" and returns0, letting execution proceed.
Recovery options
- Default value. Return a sentinel (e.g.,
#for0) when a contract fails. - Logging. Use
printfor a logging library inside the handler to record the contract violation for later debugging. - Alternative computation. In the handler, invoke a fallback algorithm that is less efficient but safe for the out‑of‑domain input.
Concrete example
#lang racket
;; Contract: input must be a real number ≥ 0, output is a real number ≥ 0
(define/contract (sqrt-safe x)
(-> real? real?)
(if (negative? x)
(error "sqrt-safe: negative input" x) ;; this line will never be reached if contract works
(sqrt x)))
;; Valid call
(sqrt-safe 16) ; => 4
;; Invalid call – triggers contract violation
(sqrt-safe -4) ; raises exn:fail:contract
;; Using a handler to recover
(with-handlers ([exn:fail:contract?
(lambda (e)
(printf "Contract violation: ~a\n" (exn-message e))
-1)])
(sqrt-safe -4)) ; prints message and returns -1
Note: Contracts add runtime overhead; avoid placing them inside tight loops unless the safety benefit justifies the cost. Keep contracts simple and document which party (provider or consumer) is responsible for each clause to reduce confusion with higher‑order functions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.