94 lines
3.1 KiB
Racket
94 lines
3.1 KiB
Racket
#lang scribble/manual
|
|
|
|
@(require (for-label racket/base
|
|
"../main.rkt"))
|
|
|
|
@title{define/return}
|
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
|
|
|
@defmodule[define-return]
|
|
|
|
The @racketmodname[define-return] library provides @racket[define/return],
|
|
a small definition form with an explicit early return. It is useful in
|
|
compact defensive functions, and especially around FFI bindings, where a
|
|
null pointer, an error code, an unsupported state, or a failed precondition
|
|
should leave the function immediately.
|
|
|
|
The return mechanism is based on @racket[call/cc]. A @racket[define/return]
|
|
form captures the current continuation of the function body and binds it to
|
|
the return identifier supplied by the programmer. Calling that identifier
|
|
leaves the body and returns the supplied value as the result of the function.
|
|
No exception is raised and no exception handler is installed.
|
|
|
|
@section{Definitions with an early return}
|
|
|
|
@defform[(define/return def return body ...)]{
|
|
|
|
Like @racket[define], but @racket[body] may call @racket[return] to leave
|
|
the definition early.
|
|
|
|
The @racket[return-id] identifier is part of the surface syntax. It is not a
|
|
predefined binding exported by this module. This keeps the form hygienic: the
|
|
programmer chooses the name of the local escape continuation, and the body
|
|
uses that same name explicitly.
|
|
|
|
@racketblock[
|
|
(define/return (status->symbol code) return
|
|
(unless (number? code)
|
|
(return 'not-a-number))
|
|
(when (= code 0) (return 'ok))
|
|
(when (< code 0) (return 'failed))
|
|
(when (> code 5) (return 'out-of-range))
|
|
(cond
|
|
((= code 1) 'normal)
|
|
((>= code 2) (string->symbol (format "code-~a" (* code code))))))
|
|
]
|
|
|
|
The final expression is used when no early return is taken.
|
|
|
|
@codeblock|{
|
|
(status->symbol 0) ; => 'ok
|
|
(status->symbol -1) ; => 'failed
|
|
(status->symbol 10) ; => 'out-of-range
|
|
(status->symbol "Hi") ; => 'not-a-number
|
|
(status->symbol 1) ; => 'normal
|
|
(status->symbol 3) ; => 'code-9
|
|
}|
|
|
|
|
}
|
|
|
|
@section{FFI-style checks}
|
|
|
|
The form is deliberately small, but it fits the style of many C libraries.
|
|
For example, a wrapper can leave immediately when an FFI call returns a null
|
|
pointer:
|
|
|
|
@racketblock[
|
|
(define/return (make-codec-context codec-id) return
|
|
(define codec (avcodec-find-decoder codec-id))
|
|
(unless codec
|
|
(return 'decoder-not-found))
|
|
|
|
(define ctx (avcodec-alloc-context3 codec))
|
|
(unless ctx
|
|
(return 'alloc-codec-context-failed))
|
|
|
|
ctx)
|
|
]
|
|
|
|
The second call is evaluated only when the first one succeeded. If either
|
|
call returns @racket[#f], the function returns the corresponding symbol.
|
|
Otherwise the final expression returns the allocated context.
|
|
|
|
@section{Notes}
|
|
|
|
The mechanism is intentionally just a local escape. It uses @racket[call/cc]
|
|
to capture the continuation around the function body. The captured
|
|
continuation is bound to @racket[return] and can be called as a normal
|
|
procedure with one value.
|
|
|
|
Since @racket[return] is a continuation, not a syntax form, it can also be
|
|
passed to local helper procedures when that is useful. It should normally be
|
|
used only as an escape from the dynamic extent of the function body.
|
|
|