Skip to content

Instantly share code, notes, and snippets.

@fiddlerwoaroof
Last active August 15, 2017 00:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fiddlerwoaroof/4e34a8c62d3a7426c3711960b8e30698 to your computer and use it in GitHub Desktop.
Save fiddlerwoaroof/4e34a8c62d3a7426c3711960b8e30698 to your computer and use it in GitHub Desktop.
(defpackage :anonymous-generic-function
(:use :cl :alexandria)
(:export :lambda-generic))
(in-package :anonymous-generic-function)
(defmacro defun-ct (name (&rest args) &body body)
`(eval-when (:load-toplevel :compile-toplevel :execute)
(defun ,name ,args
,@body)))
(defun-ct make-anonymous-generic-function (lambda-list methods)
(declare (optimize (debug 3)))
(let* ((gf (make-instance 'standard-generic-function
:lambda-list lambda-list))
(mc (closer-mop:generic-function-method-class gf)))
(prog1 gf
(format t "~&~s~%" methods)
(loop for (specializers qualifiers body) in methods
for (method-lambda initargs) = (multiple-value-list (closer-mop:make-method-lambda gf (closer-mop:class-prototype mc)
`(lambda ,lambda-list
,@body)
nil))
do
(format t "~&~s~%" method-lambda)
(add-method gf
(apply #'make-instance mc
:function (compile nil method-lambda)
:specializers specializers
:qualifiers qualifiers
:lambda-list lambda-list
initargs))))))
(defun-ct take-until (pred list)
(loop for (item . rest) on list
until (funcall pred item)
collect item into items
finally
(return (values items
(cons item rest)))))
(defun-ct get-specializers (specialized-lambda-list)
(flet ((get-specializer (specializer)
(etypecase specializer
(symbol (find-class specializer))
(cons (ecase (car specializer)
('eql (closer-mop:intern-eql-specializer (cdr specializer))))))))
(mapcar (lambda (specialized-arg)
(if (listp specialized-arg)
(get-specializer (cadr specialized-arg))
(find-class t)))
specialized-lambda-list)))
(defun-ct get-methods (method-definition-list)
(loop for (keyword . rest) in method-definition-list
unless (eq keyword :method) do
(error "method definitions must begin with the :METHOD keyword")
collect
(multiple-value-bind (qualifiers rest) (take-until #'listp rest)
(list (get-specializers (car rest))
qualifiers
(cdr rest)))))
(defmacro lambda-generic ((&rest lambda-list) &body methods)
(let ((methods (get-methods methods)))
`(load-time-value
(make-anonymous-generic-function ',lambda-list ',methods))))
#+null
(lambda-generic (a b)
(:method ((a integer) (b integer)) (+ a b))
(:method (a b) 2)
(:method :after (a b) (format t "~&~d ~d~%" a b)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment