Re: define-typed: checking values on proc entry and exit
"Dr. Arne Babenhauserheide" <[email protected]>
| Newsgroups | gmane.lisp.guile.devel,gmane.lisp.guile.user |
|---|---|
| Message-ID | <[email protected]> |
"Dr. Arne Babenhauserheide" <[email protected]> writes: > Zelphir Kaltstahl <[email protected]> writes: >> https://codeberg.org/ZelphirKaltstahl/guile-examples/src/commit/0e231c289596cb4c445efb30168105914a8539a5/macros/contracts > And the *-versions are ominous: optional and keyword arguments may be > the next frontier. > > I’m not sure how to keep those simple. I now have a solution: https://www.draketo.de/software/guile-snippets#define-typed ┌──── │ (import (srfi :11 let-values)) │ (define-syntax-rule (define-typed (procname args ...) (ret? types ...) body ...) │ (begin │ (define* (procname args ...) │ ;; create a sub-procedure to run after typecheck │ (define (helper) │ body ...) │ ;; use a typecheck prefix for the arguments │ (map (λ (type? argument) │ (let ((is-keyword? (and (keyword? type?) │ (keyword? argument)))) │ (when (and is-keyword? (not (equal? type? argument))) │ (error "Keywords in arguments and types are not equal ~a ~a" │ type? argument)) │ (unless (or is-keyword? (type? argument)) │ (error "type error ~a ~a" type? argument)))) │ (list types ...) (list args ...)) │ ;; get the result │ (let-values ((res (helper))) │ ;; typecheck the result │ (unless (apply ret? res) │ (error "type error: return value ~a does not match ~a" │ res ret?)) │ ;; return the result │ (apply values res))) │ (unless (equal? (length (quote (args ...))) (length (quote (types ...)))) │ (error "argument error: argument list ~a and type list ~a have different size" │ (quote (args ...)) (quote (types ...)))) │ ;; add procedure properties via an inner procedure │ (let ((helper (lambda* (args ...) body ...))) │ (set-procedure-properties! procname (procedure-properties helper)) │ ;; preserve the name │ (set-procedure-property! procname 'name 'procname)))) └──── This supports most features of regular define like docstrings, procedure properties, multiple values (thanks to Vivien!), keyword-arguments (thanks to Zelphir Kaltstahl’s [contracts]), and so forth. Basic usage: ┌──── │ (define-typed (hello typed-world) (string? string?) │ typed-world) │ (hello "typed") │ ;; => "typed" │ (hello 1337) │ ;; => type error ~a ~a #<procedure string? (_)> 1337 │ (define-typed (hello typed-world) (string? string?) │ "typed" ;; docstring │ #((props)) ;; more properties │ 1337) ;; wrong return type │ (procedure-documentation hello) │ ;; => "typed" │ (procedure-properties hello) │ ;; => ((name . hello) (documentation . "typed") (props)) │ (hello "typed") │ ;; type error: return value ~a does not match ~a (1337) #<procedure string? (_)> └──── Multiple Values and optional and required keyword arguments: ┌──── │ (define-typed (multiple-values num) ((λ(a b) (> a b)) number?) │ (values (* 2 (abs num)) num)) │ (multiple-values -3) │ ;; => 6 │ ;; => -3 │ (define-typed (hello #:key typed-world) (string? #:key string?) "typed" #((props)) typed-world) │ (hello #:typed-world "foo") │ ;; => "foo" │ ;; unused keyword arguments are always boolean #f as input │ (hello) │ ;; => type error ~a ~a #<procedure string? (_)> #f │ ;; typing optional keyword arguments │ (define (optional-string? x) (or (not x) (string? x))) │ (define-typed (hello #:key typed-world) (string? #:key optional-string?) │ (or typed-world "world")) │ (hello) │ ;; => "world" │ (hello #:typed-world "typed") │ ;; => "typed" │ (hello #:typed-world #t) │ ;; => type error ~a ~a #<procedure optional-string? (x)> #t │ ;; optional arguments │ (define-typed (hello #:optional typed-world) (string? #:optional optional-string?) │ (or typed-world "world")) │ (hello) │ ;; => "world" │ (hello "typed") │ ;; => "typed" │ (hello #t) │ ;; => type error ~a ~a #<procedure optional-string? (x)> #t └──── Best wishes, Arne
signature.asc
(application/pgp-signature, 1.1 KB)
-----BEGIN PGP SIGNATURE----- iQJEBAEBCAAuFiEE801qEjXQSQPNItXAE++NRSQDw+sFAmZD/WMQHGFybmVfYmFi QHdlYi5kZQAKCRAT741FJAPD6084D/9KQEBK3XxggLF87WNGUXzN8lW42+0W+z4/ BqZkdRwxSAiVpdvOJKjO8uxlDu6hHPSz4V0lsbqKgjGCewjPWdIK7mGWne3G49DI Mpin7E6fHz/MzMsPmOxp1F8jVtOrfkVyJrKeMMSm51oDqO5jLlGLRm8IljrmgD0T gNDv+OBOV1iczYqCRk2VL2qgrhqIkm6tsm0VApO7Yo1xw4YEdglx/d5baOk09txp r6YOymxjDAgE4jlqhEMIVsDEh62OHJasNEWA0Tk8Zgbreuk9u3+EL6AHWSJQIWeB XiLiYavJdo/2DCQAFf2MOp5LxV6+qUFsjfvbjKQafZaPqpm8RtaYHim3lcIqxgiK IlYHXas1O0Eh0y/tedWKpWoXtBj44uGQ0NugmR/sHw28V/Doil8vNEHPSELf5jL1 LXqbZ+jxTlwl+vPU/w2bUDJ6oZ8u9DOC8v7Hg/I0Gvfw8WqcGZzxmYs6JOjRvP8V 7ySGkIeBMG7R02BdaliG09Rmq/7CXxxroECxXcQVm1iiRmyAA1+11nGCxg6LPOh9 8saf/946lcOnQuhdwsC+63IIGU2qZp12ymSQsJvTfp2KxVUYHfw5JvgZGt/bVsQm Lfk8bZvvR3xgmCfcpCYtcjWU4gVe8u+oVkg+gOcwbZPsoM1h1xhi6zngG1L3txu5 6j0y1WJJ2ojEBAEBCAAuFiEE3Si95tmHXKvOSosd3M8NswvBBUgFAmZD/WMQHGFy bmVfYmFiQHdlYi5kZQAKCRDczw2zC8EFSOfpA/9SSZ9FfNC8d45Vj6h8U86SA0oO BNmETOnEo4HaP0ELNb3uaz29HmgGewiv7mxky0180tao6JV1G/aTlRHVnzMYByAR En1aZB63a/km6gKKizzq/oTdrDE1xPxFOE79SEwnhiomAmTTIROy2Tsum4sWRjes P0optjNwD/4lCChzMA== =GvR0 -----END PGP SIGNATURE-----