Skip to content

Instantly share code, notes, and snippets.

@WetHat
Last active February 20, 2020 17:16
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 WetHat/ac9706a4c3099bd1447b829495a2c714 to your computer and use it in GitHub Desktop.
Save WetHat/ac9706a4c3099bd1447b829495a2c714 to your computer and use it in GitHub Desktop.
Call sequence of Methods in Common Lisp
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"toc-hr-collapsed": true
},
"source": [
"# Call Sequence of Methods in Common Lisp\n",
"\n",
"As explained in [The Standard Method Combination](http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html#the-standard-method-combination) Common Lisp (primary) methods can be augmented\n",
"with three kinds of auxiliary methods:\n",
"* `:before` methods run before the most specific primary method and are run in **most-specific-first** order.\n",
"* `:after` methods run after the most specific primary method and are run in **most-specific-last** order.\n",
"* `:around` methods are combined much like primary methods except they're run \"around\" all the other methods.\n",
"\n",
"This Jupyter Notebook demonstrates the call sequence of primary and auxilliary methods with as small class \n",
"hierarchy. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Coding Style\n",
"\n",
"For the most part the [Google Common Lisp Style Guide](https://google.github.io/styleguide/lispguide.xml) is used."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## About this Jupyter Notebook\n",
"\n",
"This _Gist_ was created using:\n",
"* the [Jupyter Lab](https://jupyter.org/) computational notebook.\n",
"* the [common-lisp-jupyter](https://yitzchak.github.io/common-lisp-jupyter/) kernel by Frederic Peschanski.\n",
"* [Steel Bank Common Lisp](http://www.sbcl.org/) (SBCL)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Exploring the Method Call Sequence\n",
"\n",
"In this section we are going to set-up a simple class hierarchy and methods to demonstrate the call sequence"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Class Hierarchy"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"#<STANDARD-CLASS COMMON-LISP-USER::X>"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-CLASS COMMON-LISP-USER::XX>"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-CLASS COMMON-LISP-USER::XXX>"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"; A small hierarchy of test classes\n",
"(defclass X () ())\n",
"\n",
"(defclass XX (X) ())\n",
"\n",
"(defclass XXX (XX)())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Methods\n",
"\n",
"The methods defined below cover all the possible qualifiers: `:around`, `:before`, `:primary`, `:after`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT (X) {1004BE11A3}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT (XX) {1004D14F93}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT (XXX) {1004E2ADA3}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT :AROUND (X) {1004F508C3}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT :AROUND (XX) {10050652D3}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT :AROUND (XXX) {1005189A63}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT :BEFORE (X) {10051CA813}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT :BEFORE (XX) {100520BB13}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT :BEFORE (XXX) {100524BB63}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT :AFTER (X) {100528CC13}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT :AFTER (XX) {10052CCAE3}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"#<STANDARD-METHOD COMMON-LISP-USER::DOIT :AFTER (XXX) {100530CC13}>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"; :primary methods\n",
"\n",
"(defmethod doit ((instance X))\n",
" (format t \"X doit :primary~%\")\n",
")\n",
"\n",
"(defmethod doit ((instance XX))\n",
" (format t \"XX doit :primary before call-next-method~%\")\n",
" (call-next-method)\n",
" (format t \"XX doit :primary after call-next-method~%\")\n",
")\n",
"\n",
"(defmethod doit ((instance XXX))\n",
" (format t \"XXX doit :primary before call-next-method~%\")\n",
" (call-next-method)\n",
" (format t \"XXX doit :primary after call-next-method~%~%\")\n",
")\n",
"\n",
"; :around methods\n",
"\n",
"(defmethod doit :around ((instance X))\n",
" (format t \"X doit :around before call-next-method~%~%\")\n",
" (call-next-method)\n",
" (format t \"X doit :around after call-next-method~%\")\n",
")\n",
"\n",
"(defmethod doit :around ((instance XX))\n",
" (format t \"XX doit :around before call-next-method~%\")\n",
" (call-next-method)\n",
" (format t \"XX doit :around after call-next-method~%\")\n",
")\n",
"\n",
"(defmethod doit :around ((instance XXX))\n",
" (format t \"XXX doit :around before call-next-method~%\")\n",
" (call-next-method)\n",
" (format t \"XXX doit :around after call-next-method~%~%\")\n",
")\n",
"\n",
"; :before methods\n",
"(defmethod doit :before ((instance X))\n",
" (format t \"X doit :before~%~%\")\n",
")\n",
"\n",
"(defmethod doit :before ((instance XX))\n",
" (format t \"XX doit :before~%\")\n",
")\n",
"\n",
"(defmethod doit :before ((instance XXX))\n",
" (format t \"XXX doit :before~%\")\n",
")\n",
"\n",
"; :after methods\n",
"(defmethod doit :after ((instance X))\n",
" (format t \"X doit :after~%\")\n",
")\n",
"\n",
"(defmethod doit :after ((instance XX))\n",
" (format t \"XX doit :after~%\")\n",
")\n",
"\n",
"(defmethod doit :after ((instance XXX))\n",
" (format t \"XXX doit :after~%~%\")\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"NIL"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"XXX doit :around before call-next-method\n",
"XX doit :around before call-next-method\n",
"X doit :around before call-next-method\n",
"\n",
"XXX doit :before\n",
"XX doit :before\n",
"X doit :before\n",
"\n",
"XXX doit :primary before call-next-method\n",
"XX doit :primary before call-next-method\n",
"X doit :primary\n",
"XX doit :primary after call-next-method\n",
"XXX doit :primary after call-next-method\n",
"\n",
"X doit :after\n",
"XX doit :after\n",
"XXX doit :after\n",
"\n",
"X doit :around after call-next-method\n",
"XX doit :around after call-next-method\n",
"XXX doit :around after call-next-method\n",
"\n"
]
}
],
"source": [
"; Show the call sequence\n",
"(doit (make-instance 'XXX))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Summary\n",
"\n",
"* `:before` methods are called **most-specific-first** or (`Specific -> Generic`).\n",
"* `:after` methods are called **most-specific-last** or (`Generic -> Specific`)\n",
"* Execution of code in `:around` methods depends on the position relative\n",
" to `(call-next-method)`. Code **before** `(call-next-method)` is called on the same order\n",
" as `:before` methods. Code **after** `(call-next-method)` is called on the same order\n",
" as `:after` methods.\n",
"* The call sequence of `:primary` methods is similar to the sequence of `:around` methods except they're run \"around\" all the other methods."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Reference\n",
"* [Google Common Lisp Style Guide](https://google.github.io/styleguide/lispguide.xml)\n",
"* [16. Object Reorientation: Generic Functions](www.gigamonkeys.com/book/object-reorientation-generic-functions.html) from [Practical Common Lisp](http://www.gigamonkeys.com/book/)\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Common Lisp",
"language": "common-lisp",
"name": "common-lisp"
},
"language_info": {
"codemirror_mode": "text/x-common-lisp",
"file_extension": ".lisp",
"mimetype": "text/x-common-lisp",
"name": "common-lisp",
"pygments_lexer": "common-lisp",
"version": "1.4.14"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": true,
"toc_position": {
"height": "calc(100% - 180px)",
"left": "10px",
"top": "150px",
"width": "256px"
},
"toc_section_display": true,
"toc_window_display": true
},
"toc-autonumbering": true,
"toc-showtags": true
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment