define/  return/  contract
1 Contracted definitions
define/  contract/  return
9.1

define/return/contract🔗ℹ

Hans Dijkema <hans@dijkewijk.nl>

 (require define-return/contract) package: define-return

The define-return library provides definition forms with an explicit early return. This is useful in small defensive functions, and especially around FFI bindings, where null pointers, error codes, unsupported states, or failed preconditions should leave the function immediately.

The early return is implemented with an internal exception. A return raises that exception, and the definition forms catch it around the function body. The contracted form additionally checks early-returned values against the result contract.

This module provides the contracted version of "define/return". The module re-exports racket/contract, so contracts such as ->, ->*, any/c, or/c, and/c, and listof are available from the same require.

Note. This can lead to clashes of symbol -> with module ffi/unsafe.

1 Contracted definitions🔗ℹ

syntax

(define/contract/return (name . formals)
  (contract-part ... result-contract)
  body ...)
(define/contract/return name
  result-contract
  value)
Like define/contract, but the body may use return.

The first form defines a contracted function. Ordinary results are checked by define/contract. Early-returned values are checked separately against result-contract. The implementation does this by defining a small local contracted returner with the same result contract, so the result contract is passed through Racket’s contract machinery.

The contract must be written inline as a parenthesized contract form. The last element is used as the early-return result contract.

(define/contract/return (h x)
  (-> number? (or/c symbol? number?))
  (when (< x 0) (return 'x-not-positive))
  (let ((y (* x x)))
    (when (> y 100) (return 'maxed-out))
    (when (= y 9) (return "Wrong answer!"))
    y))

Here the result contract is (or/c symbol? number?). The symbol returns are accepted, ordinary numeric results are accepted, and the string "Wrong answer!" is rejected.

(h -1)  ; => 'x-not-positive
(h 2)   ; => 4
(h 11)  ; => 'maxed-out
(h 3)   ; contract violation: string result

A zero-argument function works in the same way:

(define/contract/return (z)
  (-> symbol?)
  (let ((cs (current-seconds)))
    (when (= (remainder cs 3) 0) (return "deelbaar door 3"))
    (when (= (remainder cs 2) 0) (return 'dividable-by-2))
    'yes))

The result contract is symbol?. Returning 'dividable-by-2 or 'yes is accepted. Returning the string "deelbaar door 3" is rejected.

Rest arguments can be used when the corresponding contract form is accepted by define/contract:

(define/contract/return (sum . xs)
  (->* () #:rest (listof number?) number?)
  (when (null? xs) (return 0))
  (apply + xs))
(sum)       ; => 0
(sum 1 2)   ; => 3
(sum 1 'x)  ; contract violation

The second form defines a contracted value. The value expression may use return, and the returned value is checked against result-contract.

(define/contract/return v
  number?
  (return 'ss))

This definition raises a contract violation, because 'ss does not satisfy number?.