# msgid "" msgstr "" msgid "" "title: Common Lisp argument precedence order parameterization of a generic " "function" msgstr "" msgid "date: 2021-04-24" msgstr "" msgid "layout: post" msgstr "" msgid "lang: en" msgstr "" msgid "" "ref: common-lisp-argument-precedence-order-parameterization-of-a-generic-" "function" msgstr "" msgid "" "When CLOS dispatches a method, it picks the most specific method definition " "to the argument list:" msgstr "" msgid "" "\n" "* (defgeneric a-fn (x))\n" "#\n" "\n" "* (defmethod a-fn (x) :default-method)\n" "#\n" "\n" "* (defmethod a-fn ((x number)) :a-number)\n" "#\n" "\n" "* (defmethod a-fn ((x (eql 1))) :number-1)\n" "#\n" "\n" "* (a-fn nil)\n" ":DEFAULT-METHOD\n" "\n" "* (a-fn \"1\")\n" ":DEFAULT-METHOD\n" "\n" "* (a-fn 0)\n" ":A-NUMBER\n" "\n" "* (a-fn 1)\n" ":NUMBER-1\n" msgstr "" msgid "" "CLOS uses a similar logic when choosing the method from parent classes, when" " multiple ones are available:" msgstr "" msgid "" "* (defclass class-a () ())\n" "\n" "#\n" "* (defclass class-b () ())\n" "\n" "#\n" "* (defgeneric another-fn (obj))\n" "\n" "#\n" "* (defmethod another-fn ((obj class-a)) :class-a)\n" "; Compiling LAMBDA (.PV-CELL. .NEXT-METHOD-CALL. OBJ):\n" "; Compiling Top-Level Form:\n" "\n" "#\n" "* (defmethod another-fn ((obj class-b)) :class-b)\n" "; Compiling LAMBDA (.PV-CELL. .NEXT-METHOD-CALL. OBJ):\n" "; Compiling Top-Level Form:\n" "\n" "#\n" msgstr "" msgid "" "Given the above definitions, when inheriting from `class-a` and `class-b`, " "the order of inheritance matters:" msgstr "" msgid "" "* (defclass class-a-coming-first (class-a class-b) ())\n" "#\n" "\n" "* (defclass class-b-coming-first (class-b class-a) ())\n" "#\n" "\n" "* (another-fn (make-instance 'class-a-coming-first))\n" ":CLASS-A\n" "\n" "* (another-fn (make-instance 'class-b-coming-first))\n" ":CLASS-B\n" msgstr "" msgid "" "Combining the order of inheritance with generic functions with multiple " "arguments, CLOS has to make a choice of how to pick a method given two " "competing definitions, and its default strategy is prioritizing from left to" " right:" msgstr "" msgid "" "* (defgeneric yet-another-fn (obj1 obj2))\n" "#\n" "\n" "* (defmethod yet-another-fn ((obj1 class-a) obj2) :first-arg-specialized)\n" "#\n" "\n" "* (defmethod yet-another-fn (obj1 (obj2 class-b)) :second-arg-specialized)\n" "#\n" "\n" "* (yet-another-fn (make-instance 'class-a) (make-instance 'class-b))\n" ":FIRST-ARG-SPECIALIZED\n" msgstr "" msgid "" "For that, we use the `:argument-precedence-order` option when declaring a " "generic function:" msgstr "" msgid "" "* (defgeneric yet-another-fn (obj1 obj2) (:argument-precedence-order obj2 obj1))\n" "#\n" "\n" "* (yet-another-fn (make-instance 'class-a) (make-instance 'class-b))\n" ":SECOND-ARG-SPECIALIZED\n" msgstr "" msgid "" "I liked that the `:argument-precedence-order` option exists. We shouldn't " "have to change the arguments from `(obj1 obj2)` to `(obj2 obj1)` just to " "make CLOS pick the method that we want. We can configure its default " "behaviour if desired, and keep the order of arguments however it best fits " "the generic function." msgstr "" msgid "Comparison with Clojure" msgstr "" msgid "Clojure has an equivalent, when using `defmulti`." msgstr "" msgid "" "Since when declaring a multi-method with `defmulti` we must define the " "dispatch function, Clojure uses it to pick the method definition. Since the " "dispatch function is required, there is no need for a default behaviour, " "such as left-to-right." msgstr "" msgid "Conclusion" msgstr "" msgid "" "Making the argument precedence order configurable for generic functions but " "not for class definitions makes a lot of sense." msgstr "" msgid "" "One shouldn't change the order of arguments of a generic function for the " "sake of tailoring it to the CLOS priority ranking algorithm, but doing it " "for a class definition is just fine." msgstr "" msgid "TIL." msgstr "" msgid "" "CLOS has to make a choice between the first and the second definition of " "`yet-another-fn`, but its choice is just a heuristic. What if we want the " "choice to be based on the second argument, instead of the first?" msgstr "" msgid "" "When declaring a class, we can choose the precedence order, and that is " "about it. But when defining a generic function, the order of arguments is " "more important to the function semantics, and the argument precedence being " "left-to-right is just the default behaviour." msgstr "" msgid "References" msgstr "" msgid "" "[Object-Oriented Programming in Common Lisp: A Programmer's Guide to " "CLOS](https://en.wikipedia.org/wiki/Object-" "Oriented_Programming_in_Common_Lisp), by Sonja E. Keene" msgstr "" #~ msgid "" #~ "CLOS has to make a choice between the first and the second definition of " #~ "`yet-another-fn`, but its choice is just a heuristic. What if we want to the" #~ " choice to be based on the second argument first?" #~ msgstr "" #~ msgid "" #~ "When declaring a class, we can choose the precedence order, and that is " #~ "about it. But when defining a generic function, the order of argumentws is " #~ "more important to the function semantics, and the argument precedence being " #~ "left-to-right is just the default behaviour." #~ msgstr ""