{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# common-lisp-jupyter\n", "\n", "A Common Lisp kernel for Jupyter." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All stream output is captured and displayed in the notebook interface." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "NIL" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "NIL" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Hello, World" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Goodbye, cruel World." ] } ], "source": [ "(format t \"Hello, World\")\n", "(format *error-output* \"Goodbye, cruel World.\")" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "NIL" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/markdown": [ "# Title\n", "This is *markdown*!" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(format j:*markdown-output* \"# Title\n", "This is *markdown*!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Evaluation results are displayed directory in the notebook." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "14" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(+ 2 3 4 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All Lisp code is value, including calls to quicklisp." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "To load \"shasht\":\n", " Load 1 ASDF system:\n", " shasht\n" ] }, { "data": { "text/plain": [ "(:SHASHT)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "; Loading \"shasht\"\n", "\n" ] } ], "source": [ "(ql:quickload :shasht)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The serialized JSON will represented as a Lisp string." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(:OBJECT-PLIST \"foo\" \"bar\" \"quux\" 1.23)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"foo\": \"bar\",\n", " \"quux\": 1.23\n", "}" ] } ], "source": [ "(shasht:write-json `(:object-plist \"foo\" \"bar\" \"quux\" 1.23) t)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "JSON can also be displayed with open/close expanders using `json` or `json-file`" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "application/json": { "foo": "bar", "quux": { "a": 1, "b": 2, "c": 3 } } }, "metadata": { "application/json": { "expanded": true } }, "output_type": "display_data" } ], "source": [ "(jupyter:json `(:object-plist \"foo\" \"bar\" \"quux\" (:object-plist \"a\" 1 \"b\" 2)) :expanded t :display t :id \"a\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you use `display_data` and assign an id then you can update the result later on." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "(jupyter:json `(:object-plist \"foo\" \"bar\" \"quux\" (:object-plist \"a\" 1 \"b\" 2 \"c\" 3)) :expanded t :display t :update t :id \"a\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Error conditions will be captured and a backtrace will be sent to `*error-output*`" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "ename": "DIVISION-BY-ZERO", "evalue": "arithmetic error DIVISION-BY-ZERO signalled\nOperation was (/ 1 0).", "output_type": "error", "traceback": [ "3: ((FLET \"H1\" :IN COMMON-LISP-JUPYTER::MY-EVAL) arithmetic error DIVISION-BY-ZERO signalled\nOperation was (/ 1 0).)", "4: (SB-KERNEL::%SIGNAL arithmetic error DIVISION-BY-ZERO signalled\nOperation was (/ 1 0).)", "5: (ERROR DIVISION-BY-ZERO OPERATION / OPERANDS (1 0))", "6: (\"DIVISION-BY-ZERO-ERROR\" 1 0)", "7: (SB-KERNEL:INTERNAL-ERROR #.(SB-SYS:INT-SAP #X7F7B8362DC80) #)", "8: (\"foreign function: call_into_lisp\")", "9: (\"foreign function: funcall2\")", "10: (\"foreign function: interrupt_internal_error\")", "11: (\"foreign function: #x558902F62993\")", "12: (SB-KERNEL::INTEGER-/-INTEGER 1 0)", "13: (/ 1 0)", "14: (SB-INT:SIMPLE-EVAL-IN-LEXENV (/ 1 0) #)", "15: (EVAL (/ 1 0))", "16: (COMMON-LISP-JUPYTER::MY-EVAL (/ 1 0))", "17: ((:METHOD JUPYTER:EVALUATE-CODE (COMMON-LISP-JUPYTER:KERNEL T)) # (/ 1 0))", "18: (JUPYTER::HANDLE-EXECUTE-REQUEST)", "19: (JUPYTER::RUN-SHELL #)", "20: ((LAMBDA NIL :IN BORDEAUX-THREADS::BINDING-DEFAULT-SPECIALS))", "21: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))", "22: ((FLET \"WITHOUT-INTERRUPTS-BODY-11\" :IN SB-THREAD::RUN))", "23: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))", "24: ((FLET \"WITHOUT-INTERRUPTS-BODY-4\" :IN SB-THREAD::RUN))", "25: (SB-THREAD::RUN)", "26: (\"foreign function: call_into_lisp\")", "27: (\"foreign function: funcall1\")" ] }, { "name": "stderr", "output_type": "stream", "text": [ "#\n", " [Environment of thread #]\n", "\n", "arithmetic error DIVISION-BY-ZERO signalled\n", "Operation was (/ 1 0).\n", " [Condition of type DIVISION-BY-ZERO]\n", "\n", "\n", "Backtrace:\n", " 3: ((FLET \"H1\" :IN COMMON-LISP-JUPYTER::MY-EVAL) arithmetic error DIVISION-BY-ZERO signalled\n", "Operation was (/ 1 0).)\n", " 4: (SB-KERNEL::%SIGNAL arithmetic error DIVISION-BY-ZERO signalled\n", "Operation was (/ 1 0).)\n", " 5: (ERROR DIVISION-BY-ZERO OPERATION / OPERANDS (1 0))\n", " 6: (\"DIVISION-BY-ZERO-ERROR\" 1 0)\n", " 7: (SB-KERNEL:INTERNAL-ERROR #.(SB-SYS:INT-SAP #X7F7B8362DC80) #)\n", " 8: (\"foreign function: call_into_lisp\")\n", " 9: (\"foreign function: funcall2\")\n", " 10: (\"foreign function: interrupt_internal_error\")\n", " 11: (\"foreign function: #x558902F62993\")\n", " 12: (SB-KERNEL::INTEGER-/-INTEGER 1 0)\n", " 13: (/ 1 0)\n", " 14: (SB-INT:SIMPLE-EVAL-IN-LEXENV (/ 1 0) #)\n", " 15: (EVAL (/ 1 0))\n", " 16: (COMMON-LISP-JUPYTER::MY-EVAL (/ 1 0))\n", " 17: ((:METHOD JUPYTER:EVALUATE-CODE (COMMON-LISP-JUPYTER:KERNEL T)) # (/ 1 0))\n", " 18: (JUPYTER::HANDLE-EXECUTE-REQUEST)\n", " 19: (JUPYTER::RUN-SHELL #)\n", " 20: ((LAMBDA NIL :IN BORDEAUX-THREADS::BINDING-DEFAULT-SPECIALS))\n", " 21: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))\n", " 22: ((FLET \"WITHOUT-INTERRUPTS-BODY-11\" :IN SB-THREAD::RUN))\n", " 23: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))\n", " 24: ((FLET \"WITHOUT-INTERRUPTS-BODY-4\" :IN SB-THREAD::RUN))\n", " 25: (SB-THREAD::RUN)\n", " 26: (\"foreign function: call_into_lisp\")\n", " 27: (\"foreign function: funcall1\")" ] } ], "source": [ "(/ 1 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "New functions can be defined. The default namespace is `COMMON-LISP-USER`." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "FIBONACCI" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(defun fibonacci (n)\n", " (if (<= n 1)\n", " 1\n", " (+ (fibonacci (- n 2)) (fibonacci (- n 1)))))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The seventh element of everybody's favorite sequence." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "21" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(fibonacci 7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "S-Expressions will be displayed using `pprint`." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(LAMBDA (N)\n", " (BLOCK FIBONACCI\n", " (IF (<= N 1)\n", " 1\n", " (+ (FIBONACCI (- N 2)) (FIBONACCI (- N 1))))))" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "T" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "FIBONACCI" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(function-lambda-expression #'fibonacci)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Rich text and images can be displayed using inline values using the `inline-result`, `html`, `jpeg`, `latex`, `markdown`, `png`, `svg` or `text` functions. " ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "## wibble\n", "foo `quux`" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(jupyter:markdown \"## wibble\n", "foo `quux`\")" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$R_{\\mu \\nu} - \\tfrac{1}{2}R \\, g_{\\mu \\nu} + \\Lambda g_{\\mu \\nu} =\n", " 8 \\pi G c^{-4} T_{\\mu \\nu}$$" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(jupyter:latex \"$$R_{\\\\mu \\\\nu} - \\\\tfrac{1}{2}R \\\\, g_{\\\\mu \\\\nu} + \\\\Lambda g_{\\\\mu \\\\nu} =\n", " 8 \\\\pi G c^{-4} T_{\\\\mu \\\\nu}$$\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "External files can be rendered using the `file`, `gif-file`, `jpeg-file`, `png-file`, `ps-file`, `svg-file` functions. \n", "\n", "The MIME type will be automatically determined in the case of a call to `file`." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " image/svg+xml\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "lisplogo_alien.svg" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(jupyter:file \"lisplogo_alien.svg\" :display t)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Calls to `yes-or-no-p` will result in a `input_request` to the user." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdin", "output_type": "stream", "text": [ "\n", "LISP rocks? (yes or no) yes\n" ] }, { "data": { "text/plain": [ "LISP-ROCKS" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(defparameter lisp-rocks (yes-or-no-p \"LISP rocks?\"))" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "For the record Lisp **rocks**!" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(jupyter:markdown (format nil \"For the record Lisp ~A\" (if lisp-rocks \"**rocks**!\" \"**does not** rock.\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Output send to `*query-io*` will result also result in an input_request to the user." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ASK" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(defun ask (prompt)\n", " (format *query-io* prompt)\n", " (finish-output *query-io*)\n", " (read-line *query-io*))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdin", "output_type": "stream", "text": [ "What is your quest? wibble\n" ] }, { "data": { "text/plain": [ "QUEST" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(defvar quest (ask \"What is your quest? \"))" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "NIL" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Your quest is: wibble" ] } ], "source": [ "(format t \"Your quest is: ~A\" quest)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`jupyter:clear` will clear the output of the current." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "10 " ] } ], "source": [ "(loop\n", " for i from 1 to 10\n", " do (sleep 0.25)\n", " do (jupyter:clear t)\n", " do (print i)\n", " do (finish-output *standard-output*)\n", " finally (return (values)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`(values)` can be used to suppress the output. Defining a reader macro can make this easier." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "NO-OUTPUT-READER" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "T" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(defun no-output-reader (stream char)\n", " (declare (ignore char))\n", " (list (quote progn) (read stream t nil t) (values)))\n", "\n", "(set-macro-character #\\~ #'no-output-reader)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "NIL" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "No output returned!" ] } ], "source": [ "~(format t \"No output returned!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Multiple value returns function correctly and previous result/form are set. " ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "A1" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "A2" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "B" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "C1" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "C2" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "C3" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "((C1 C2 C3) (B) (A1 A2))" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(values 'a1 'a2) \n", "'b \n", "(values 'c1 'c2 'c3) \n", "(list / // ///)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "A1" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "A2" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "B" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "C1" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "C2" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "C3" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "(C1 B A1)" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(values 'a1 'a2) \n", "'b \n", "(values 'c1 'c2 'c3) \n", "(list * ** ***)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "2" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "3" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "((/ 9 3) (- 4 2) (+ 0 1))" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(+ 0 1) \n", "(- 4 2) \n", "(/ 9 3) \n", "(list + ++ +++)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Common Lisp (SBCL)", "language": "common-lisp", "name": "common-lisp_sbcl" }, "language_info": { "codemirror_mode": "text/x-common-lisp", "file_extension": ".lisp", "mimetype": "text/x-common-lisp", "name": "common-lisp", "pygments_lexer": "common-lisp", "version": "2.4.0" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }