Allowing a default predicate for methods.

Fixes issue #191.
This commit is contained in:
Alex Shinn 2013-07-29 21:16:18 +09:00
parent a62d82e048
commit 19e5398b92
2 changed files with 52 additions and 5 deletions

View file

@ -17,22 +17,34 @@
. body))))))
;;> \subsubsubsection{(define-method (name (param type) ...) body ...)}
;;> \subsubsubsection{\scheme{(define-method (name params ...) body ...)}}
;;> Extends the generic function \var{name} with a new method that
;;> applies when the given param types all match.
;;> Each parameter should be either a single identifier or a list of the form
;;> \scheme{(param type)} where \var{param} is the parameter name and
;;> \var{type} is a predicate which returns true if it's argument is of the
;;> correct type.
;;> Parameters without a predicate will always match.
;;> If multiple methods satisfy the arguments, the most recent method
;;> will be used. The special form \scheme{(call-next-method)} can be
;;> invoked to call the next most recent method with the same arguments.
(define-syntax define-method
(er-macro-transformer
(lambda (e r c)
(let ((name (car (cadr e)))
(params (cdr (cadr e)))
(params (map (lambda (param)
(if (identifier? param)
`(,param (lambda _ #t))
param))
(cdr (cadr e))))
(body (cddr e)))
`(,(r 'generic-add!) ,name
(,(r 'list) ,@(map cadr params))
(,(r 'lambda) (next ,@(map car params))
(,(r 'lambda) (,(r 'next) ,@(map car params))
(,(r 'let-syntax) ((call-next-method
(,(r 'syntax-rules) ()
((_) (next)))))
((_) (,(r 'next))))))
,@body)))))))
(define (no-applicable-method-error name args)

35
tests/generic-tests.scm Normal file
View file

@ -0,0 +1,35 @@
(import (chibi) (chibi generic) (chibi test))
(test-begin "generics")
(let ()
(define-generic add)
(define-method (add (x number?) (y number?))
(+ x y))
(define-method (add (x string?) (y string?))
(string-append x y))
(define-method (add x (y list?))
(append x y))
(test 4 (add 2 2))
(test "22" (add "2" "2"))
(test '(2 2) (add '() '(2 2)))
(test '(2 2) (add '(2) '(2)))
(test '(2 2) (add '(2 2) '()))
(test '(2) (add #f '(2)))
(test-error (add #(2) #(2))))
(let ()
(define-generic mul)
(define-method (mul (x number?) (y number?))
(* x y))
(define-method (mul (x inexact?) (y inexact?))
(+ (* x y) 0.1))
(define-method (mul (x exact?) (y exact?))
(inexact->exact (call-next-method)))
(test 21 (mul 3 7))
(test 21.0 (mul 3.0 7))
(test 21.0 (mul 3 7.0))
(test 21.1 (mul 3.0 7.0)))
(test-end)