{"id":27016729,"url":"https://github.com/marcoheisig/fast-generic-functions","last_synced_at":"2026-01-28T10:33:22.613Z","repository":{"id":77342891,"uuid":"248213136","full_name":"marcoheisig/fast-generic-functions","owner":"marcoheisig","description":"Seal your generic functions for an extra boost in performance.","archived":false,"fork":false,"pushed_at":"2025-03-13T05:41:08.000Z","size":161,"stargazers_count":98,"open_issues_count":7,"forks_count":5,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-04T15:52:59.531Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcoheisig.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-18T11:28:26.000Z","updated_at":"2025-03-20T02:51:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"ee44ba96-4adb-46ee-859c-761ac13d5621","html_url":"https://github.com/marcoheisig/fast-generic-functions","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/marcoheisig/fast-generic-functions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcoheisig%2Ffast-generic-functions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcoheisig%2Ffast-generic-functions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcoheisig%2Ffast-generic-functions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcoheisig%2Ffast-generic-functions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcoheisig","download_url":"https://codeload.github.com/marcoheisig/fast-generic-functions/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcoheisig%2Ffast-generic-functions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28844014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T07:39:25.367Z","status":"ssl_error","status_checked_at":"2026-01-28T07:39:24.487Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-04T15:45:28.131Z","updated_at":"2026-01-28T10:33:17.599Z","avatar_url":"https://github.com/marcoheisig.png","language":"Common Lisp","readme":"#+TITLE: Fast Generic Functions\n\nThis library introduces /fast generic functions/, i.e., functions that\nbehave just like regular generic functions, except that the can be sealed\non certain domains.  If the compiler can then statically detect that the\narguments to a fast generic function fall within such a domain, it will\nperform a variety of optimizations.\n\n* Example 1 - Generic Find\n\nThis example illustrates how one can define a (hopefully) fast method\nfor finding items in a sequence.\n\nThe first step is to define a generic function whose generic function class\nis =fast-generic-function=.\n\n#+BEGIN_SRC lisp\n(defgeneric generic-find (item sequence \u0026key test)\n  (:generic-function-class fast-generic-functions:fast-generic-function))\n#+END_SRC\n\nOnce this definition is loaded (and only then, so you shouldn't put the\nnext snippets in the same file as the defgeneric form), it is possible to\nadd methods to it in the usual way.\n\n#+BEGIN_SRC lisp\n(defmethod generic-find (item (list list) \u0026key (test #'eql))\n  (and (member item list :test test)\n       t))\n\n(defmethod generic-find (item (vector vector) \u0026key (test #'eql))\n  (cl:find item vector :test test))\n\n(seal-domain #'generic-find '(t list))\n(seal-domain #'generic-find '(t vector))\n#+END_SRC\n\nThe novelty are the two calls to =seal-domain=.  These calls seal the\nspecified part of the function domain, and at the same time install\ncompiler optimizations for calls to that generic function.\n\nWhenever the compiler can detect that the arguments of a call to a fast\ngeneric function fall within such a sealed domain, the entire call can be\noptimized in a variety of ways.  By default, the call to the fast generic\nfunction's discriminating function will be replaced by a direct call to a\ncustom effective method function.  This means that there will be zero\noverhead for determining the generic function's behavior.  The following\nexample illustrates this:\n\n#+BEGIN_SRC lisp\n(defun small-prime-p (x)\n  (generic-find x '(2 3 5 7 11)))\n\n;; The call to GENERIC-FIND should have been replaced by a direct call to\n;; the appropriate effective method function.\n(disassemble #'small-prime-p)\n#+END_SRC\n\nIt is even possible to inline the entire effective method into the call\nsite.  However, to avoid code bloat, this feature is disabled by default.\nTo enable it, each method withing the sealed domain must contain an\nappropriate declaration, as shown in the next example.\n\n* Example 2 - Extensible Number Functions\n\n#+BEGIN_SRC lisp\n(defgeneric binary-+ (x y)\n  (:generic-function-class fast-generic-function))\n\n(defmethod binary-+ ((x number) (y number))\n  (declare (method-properties inlineable))\n  (+ x y))\n\n(seal-domain #'binary-+ '(number number))\n#+END_SRC\n\nIt is easy to generalize such a binary function to a function that accepts\nany number of arguments:\n\n#+BEGIN_SRC lisp\n(defun generic-+ (\u0026rest things)\n  (cond ((null things) 0)\n        ((null (rest things)) (first things))\n        (t (reduce #'binary-+ things))))\n\n(define-compiler-macro generic-+ (\u0026rest things)\n  (cond ((null things) 0)\n        ((null (rest things)) (first things))\n        (t (reduce (lambda (a b) `(binary-+ ,a ,b)) things))))\n#+END_SRC\n\nWith all this in place, we can use our =generic-+= function much like\nCommon Lisp's built-in =+= without worrying about performance.  The next\ncode snippet shows that in fact, each call to =generic-+= is inlined and\nturned into a single =addss= instruction.\n\n#+BEGIN_SRC lisp\n(disassemble\n (compile nil\n   '(lambda (x y z)\n     (declare (single-float x y z))\n     (generic-+ x y z))))\n\n;; disassembly for (lambda (x y z))\n;; Size: 38 bytes. Origin: #x52FD9354\n;; 54:       498B4510         mov RAX, [R13+16]\n;; 58:       488945F8         mov [RBP-8], RAX\n;; 5C:       0F28CC           movaps XMM1, XMM4\n;; 5F:       F30F58CB         addss XMM1, XMM3\n;; 63:       F30F58CA         addss XMM1, XMM2\n;; 67:       660F7ECA         movd EDX, XMM1\n;; 6B:       48C1E220         shl RDX, 32\n;; 6F:       80CA19           or DL, 25\n;; 72:       488BE5           mov RSP, RBP\n;; 75:       F8               clc\n;; 76:       5D               pop RBP\n;; 77:       C3               ret\n;; 78:       CC10             int3 16\n#+END_SRC\n\nOnce a fast generic function has been sealed, it is not possible to add,\nremove, or redefine methods within the sealed domain.  However, outside of\nthe sealed domain, it behaves just like a standard generic function.  That\nmeans we can extend its behavior, e.g., to allow addition of strings:\n\n#+BEGIN_SRC lisp\n(defmethod binary-+ ((x string) (y string))\n  (concatenate 'string x y))\n\n(generic-+ \"foo\" \"bar\" \"baz\")\n;; =\u003e \"foobarbaz\"\n#+END_SRC\n\n* Specializing on a User-Defined Class\n\nBy default, only built-in classes and structure classes can appear as\nspecializers of a method within a sealed domain of a fast generic function.\nHowever, it is also possible to define custom sealable classes.  This\nexample illustrates how.\n\nSince this example has plenty of dependencies (metaobject definition and\nuse, generic function definition and method defintion, sealing and use of a\nsealed function), each of the following snippets of code should be put into\nits own file.\n\nIn the first snippet, we define sealable standard class, that is both a\nsealable class and a standard class.\n\n#+BEGIN_SRC lisp\n(defclass sealable-standard-class\n    (sealable-metaobjects:sealable-class standard-class)\n  ())\n\n(defmethod validate-superclass\n    ((class sealable-standard-class)\n     (superclass standard-class))\n  t)\n#+END_SRC\n\nIn the next snippet, we define a class =foo= whose metaclass is our newly\nintroduced =sealable-standard-class=.  Because the implementation of fast\ngeneric functions uses literal instances to find an optimized effective\nmethod function at load time, each sealable class must also have a suitable\nmethod on =make-load-form=.\n\n#+BEGIN_SRC lisp\n(defclass foo ()\n  ((x :reader x :initarg :x))\n  (:metaclass sealable-standard-class))\n\n(defmethod make-load-form ((foo foo) \u0026optional env)\n  (make-load-form-saving-slots foo :slot-names '(x) :environment env))\n#+END_SRC\n\nIn the next snippet, we define a fast generic function =op=.\n\n#+BEGIN_SRC lisp\n(defgeneric op (foo)\n  (:generic-function-class fast-generic-functions:fast-generic-function))\n#+END_SRC\n\nOnce we have loaded the definition of =op=, we can add individual methods\nand seal some of them.  In particular, we can add a method that specializes\non the =foo= class.\n\nWe could also have defined this method without =foo= being a sealable\nclass, but then the call to =seal-domain= would have signaled an error.\n\n#+BEGIN_SRC lisp\n(defmethod op ((foo foo))\n  (* 2 (x foo)))\n\n(sealable-metaobjects:seal-domain #'op '(foo))\n#+END_SRC\n\nFinally, we have everything in place for having optimized calls to =op= in\nthe case where its argument is of type =foo=.\n\n#+BEGIN_SRC lisp\n(defun bar ()\n  (let ((foo (make-instance 'foo :x 42)))\n    (declare (foo foo))\n    (op foo)))\n#+END_SRC\n\nIf this example is too intimidating for you, please remember that you can\nalways specialize fast methods on built-in classes (like integer and\nsimple-vector) or structure classes (everything defined via =defstruct=).\n","funding_links":[],"categories":["Miscellaneous ##"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcoheisig%2Ffast-generic-functions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcoheisig%2Ffast-generic-functions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcoheisig%2Ffast-generic-functions/lists"}