common-lisp-jupyter

A Common Lisp kernel for Jupyter.

All stream output is captured and displayed in the notebook interface.

In [1]:
(format t "Hello, World")
(format *error-output* "Goodbye, cruel World.")
Out[1]:
NIL
Out[1]:
NIL
Hello, World
Goodbye, cruel World.

Evaluation results are displayed directory in the notebook.

In [2]:
(+ 2 3 4 5)
Out[2]:
14

All Lisp code is value, including calls to quicklisp.

In [3]:
(ql:quickload :jsown)
To load "jsown":
  Load 1 ASDF system:
    jsown
Out[3]:
(:JSOWN)
; Loading "jsown"

The serialized JSON will represented as a Lisp string.

In [4]:
(jsown:to-json
    (jsown:new-js ("foo" "bar")))
Out[4]:
"{\"foo\":\"bar\"}"

Error conditions will be captured and a backtrace will be sent to *error-output*

In [ ]:
(/ 1 0)

arithmetic error DIVISION-BY-ZERO signalled
Operation was (/ 1 0).
   [Condition of type DIVISION-BY-ZERO]

#<ENVIRONMENT {100386F333}>
   [Environment of thread #<THREAD "SHELL Thread" RUNNING {1001DFA6B3}>]


Backtrace:
 5: (SB-KERNEL::%SIGNAL arithmetic error DIVISION-BY-ZERO signalled
Operation was (/ 1 0).)
 6: (ERROR DIVISION-BY-ZERO OPERATION / OPERANDS (1 0))
 7: ("DIVISION-BY-ZERO-ERROR" 1 0)
 8: (SB-KERNEL:INTERNAL-ERROR #.(SB-SYS:INT-SAP #X7F56EDD0DC40) #<unused argument>)
 9: ("foreign function: call_into_lisp")
 10: ("foreign function: funcall2")
 11: ("foreign function: interrupt_internal_error")
 12: ("foreign function: #x5611E9A9E4F3")
 13: (SB-KERNEL::INTEGER-/-INTEGER 1 0)
 14: (/ 1 0)
 15: (SB-INT:SIMPLE-EVAL-IN-LEXENV (/ 1 0) #<NULL-LEXENV>)
 16: (EVAL (/ 1 0))
 17: (COMMON-LISP-JUPYTER::MY-EVAL (/ 1 0))
 18: ((:METHOD JUPYTER:EVALUATE-CODE (COMMON-LISP-JUPYTER:KERNEL T)) #<unused argument> (/ 1 0))
 19: (JUPYTER::HANDLE-EXECUTE-REQUEST #<KERNEL {10042A5B83}> #<MESSAGE {10035FF883}>)
 20: (JUPYTER::HANDLE-SHELL-MESSAGE #<KERNEL {10042A5B83}> #<MESSAGE {10035FF883}>)
 21: (JUPYTER::RUN-SHELL #<KERNEL {10042A5B83}>)
 22: ((LAMBDA NIL :IN BORDEAUX-THREADS::BINDING-DEFAULT-SPECIALS))
 23: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))
 24: ((FLET "WITHOUT-INTERRUPTS-BODY-14" :IN SB-THREAD::RUN))
 25: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))
 26: ((FLET "WITHOUT-INTERRUPTS-BODY-4" :IN SB-THREAD::RUN))
 27: (SB-THREAD::RUN)
 28: ("foreign function: call_into_lisp")
 29: ("foreign function: funcall1")

New functions can be defined. The default namespace is COMMON-LISP-USER.

In [6]:
(defun fibonacci (n)
    (if (<= n 1)
        1
        (+ (fibonacci (- n 2)) (fibonacci (- n 1)))))
Out[6]:
FIBONACCI

The seventh element of everybody's favorite sequence.

In [7]:
(fibonacci 7)
Out[7]:
21

S-Expressions will be displayed using pprint.

In [8]:
(function-lambda-expression #'fibonacci)
Out[8]:
(LAMBDA (N)
  (BLOCK FIBONACCI
    (IF (<= N 1)
        1
        (+ (FIBONACCI (- N 2)) (FIBONACCI (- N 1))))))
Out[8]:
T
Out[8]:
FIBONACCI

Rich text and images can be displayed using inline values using the inline-result, html, jpeg, latex, markdown, png, svg or text functions.

In [9]:
(jupyter:markdown "## wibble
foo `quux`")
Out[9]:

wibble

foo quux

In [10]:
(jupyter:latex "$$R_{\\mu \\nu} - \\tfrac{1}{2}R \\, g_{\\mu \\nu} + \\Lambda g_{\\mu \\nu} =
               8 \\pi G c^{-4} T_{\\mu \\nu}$$")
Out[10]:
$$R_{\mu \nu} - \tfrac{1}{2}R \, g_{\mu \nu} + \Lambda g_{\mu \nu} = 8 \pi G c^{-4} T_{\mu \nu}$$

External files can be rendered using the file, gif-file, jpeg-file, png-file, ps-file, svg-file functions.

The MIME type will be automatically determined in the case of a call to file.

In [11]:
(jupyter:file "lisplogo_alien.svg")
Out[11]:
image/svg+xml

Calls to yes-or-no-p will result in a input_request to the user.

In [12]:
(defparameter lisp-rocks (yes-or-no-p "LISP rocks?"))
Out[12]:
LISP-ROCKS
In [13]:
(jupyter:markdown (format nil "For the record Lisp ~A" (if lisp-rocks "**rocks**!" "**does not** rock.")))
Out[13]:

For the record Lisp rocks!

Output send to *query-io* will result also result in an input_request to the user.

In [14]:
(defun ask (prompt)
    (format *query-io* prompt)
    (finish-output *query-io*)
    (read-line *query-io*))
Out[14]:
ASK
In [16]:
(defvar quest (ask "What is your quest? "))
Out[16]:
QUEST
In [17]:
(format t "Your quest is: ~A" quest)
Out[17]:
NIL
Your quest is: Search for the holy grail.

jupyter:clear will clear the output of the current.

In [18]:
(loop
    for i from 1 to 10
    do (sleep 0.25)
    do (jupyter:clear t)
    do (print i)
    do (finish-output *standard-output*)
    finally (return :no-output))
10 

:no-output can be used to suppress the output. Defining a reader macro can make this easier.

In [19]:
(defun no-output-reader (stream char)
   (declare (ignore char))
   (list (quote progn) (read stream t nil t) :no-output))

(set-macro-character #\~ #'no-output-reader)
Out[19]:
NO-OUTPUT-READER
Out[19]:
T
In [20]:
~(format t "No output returned!")
No output returned!

Multiple value returns function correctly and previous result/form are set.

In [21]:
(values 'a1 'a2) 
'b 
(values 'c1 'c2 'c3) 
(list / // ///)
Out[21]:
A1
Out[21]:
A2
Out[21]:
B
Out[21]:
C1
Out[21]:
C2
Out[21]:
C3
Out[21]:
((C1 C2 C3) (B) (A1 A2))
In [22]:
(values 'a1 'a2) 
'b 
(values 'c1 'c2 'c3) 
(list * ** ***)
Out[22]:
A1
Out[22]:
A2
Out[22]:
B
Out[22]:
C1
Out[22]:
C2
Out[22]:
C3
Out[22]:
(C1 B A1)
In [23]:
(+ 0 1) 
(- 4 2) 
(/ 9 3) 
(list + ++ +++)
Out[23]:
1
Out[23]:
2
Out[23]:
3
Out[23]:
((/ 9 3) (- 4 2) (+ 0 1))
In [ ]: