đź“ťCommon Lisp

tags

Lisp #programming-language

Common Lisp is a practical lisp

Lisp exposes two levels of syntax (s-expressions and lisp forms)

Syntax

  • CL has exact ratios:

    CL-USER> (+ 3/7 5/4)
    47/28
    
  • Symbols:

    • When reading symbol, the reader converts all unescaped chars to uppercase

    • Common conventions:

      • *global-variable*

      • %low-level-function or %%low-level-function (used by some programmers)

      • something->other — conversion function

  • reader macros

    • 'x → (quote x)

    • #'x → (function x)

Evaluation

  • It is possible for symbols to be self-evaluated—the value of the variable is the symbol itself

    (defconstant t 't)
    (defconstant nil 'nil)
    
  • When the reader interns keyword, it automatically defines a constant variable with the name and with the symbol as the value

    (defconstant :hello ':hello)
    

    (keywords are just symbols starting with :)

  • lists:

    • function call forms

    • macro forms

    • special forms

      • 25 of them (if, let, quote)

  • nil is false, everything else is true

  • nil is both an atom and a list. (eq nil ()) => T

Functions

;; general form
(defun name (parameters)
  "docstring."
  body)

;; optional parameters
(defun f (a b &optional c d) …)

;; specifying default value
(defun f (&optional (a 10)) …)

;; to know whether value was supplied
(defun f (&optional (a 3 a-supplied-p)) …)
;; “-supplied-p” suffix is a convention for such variables


;; rest
(defun f (a b &rest cs) …)


;; keyword
(defun f (&key a b c) …)
;; … then called
(f :a a-value :c c-value)
;; can also supply default value and a supplied-p
(defun f (&key (a 10 a-supplied-p)) …)
;; keyword can also be customized to be different from variable name
(defun f (&key ((:apple a)) ((:box b) 0 b-supplied-p)) …)

;; order:
;; 1. required
;; 2. &optional
;; 3. &rest
;; 4. &key

;; When both optional and keyword parameters are used, optional will
;; eat keywords until optional parameters are satisfied.
;;
;; You should probably avoid using optional and key arguments
;; together.
(defun foo (x &optional y &key z) (list x y z))
(foo 1 :z 3) ; => ERROR
(defun bar (a &optional b c &key d) (list a b c d))
(bar 1 :d 2) ; => (1 :D 2 NIL)

;; When both rest and keyword parameters are used, the arguments are
;; filled in both rest and keyword parameters.
(defun baz (&rest rest &key a b c) (list rest a b c))
(baz :a 1 :b 2 :c 3) ; => ((:A 1 :B 2 :C 3) 1 2 3)
;; This definition only allows specified keyword arguments and no
;; other arguments:
(baz "hello" "world") ; => ERROR: Unknown &KEY argument: "hello"
(baz :hello "world")  ; => ERROR: Unknown &KEY argument: :HELLO
;; To allow other arguments, add &allow-other-keys to the parameters
;; list

;; RETURN-FROM operator can be used to return value from the function
;; immediately
(defun foo (n)
  (dotimes (i 10)
    (dotimes (j 10)
      (when (> (* i j) n)
        (return-from foo (list i j))))))

;; get function object
#'foo

;; call it
(funcall #'foo 20)
(apply #'foo '(20))

;; anonymous functions
(lambda (parameters) body)

Variables

;;; defining global variables
;; If variable is already defined, its value is not overwritten.
(defvar *name* optional-value
  "optional docstring.")

;; `defparameter' always assigns the new value.
(defparameter *name* value
  "optional docstring.")

;; `defconstant' name cannot be used as a function parameter or
;; rebound with any other binding form. Many Lisp programmers follow
;; the plus +naming+ +convention+ for constants.
(defconstant name initial-value-form
  "optional docstring.")

;; All global variables are dynamically-scoped. (The name of every
;; variable defined with `defvar' or `defparameter' is automatically
;; declared “special.”) LET variables or function parameters can
;; override it.
(defvar *x* 10)
(defun foo () (format t "X: ~d~%" *x*))
(foo) ; => X: 10
(let ((*x* 20)) (foo)) ; => X: 20
(foo) ; => X: 10

In Common Lisp, all global variables are automatically “special” (use dynamic scoping)

In Common Lisp, global variables use star *naming* *convention* because of dynamic scoping

Loops

;; basic form
(do (variable-definition*)
    (end-test-form result-form*)
  statement*)
;; where variable-definition =
(var init-form step-form)


;; LOOP simple form—an infinite loop. Iterates forever unless you
;; `return' or break out.
(loop
  body-form*)

Macros

;; Macro parameters are destructuring. Macros can also have &body
;; parameters that work identically to &rest but IDEs can use it to
;; properly indent macros.
(defmacro do-primes ((var start end) &body body)
  …)

;; Use `gensym' if you need to generate a unique symbol.
(defmacro with-gensyms ((&rest names) &body body)
  `(let ,(loop for n in names collect `(,n (gensym)))
     ,@body))

(defmacro once-only ((&rest names) &body body)
  (let ((gensyms (loop for n in names collect (gensym))))
    `(let (,@(loop for g in gensyms collect `(,g (gensym))))
      `(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
        ,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
           ,@body)))))

Data structures

Association list

  • It’s a list of cons cells.

  • assoc find cons cell in a list

    ;; search by key
    (assoc 'a '((a . 1) (b . 2))) ; => (A . 1)
    
    ;; search by value
    (rassoc 2 '((a . 1) (b . 2))) ; => (B . 2)
    
  • add new value to the front

    (cons (cons 'new-key 'new-value) alist)
    ;; same as
    (acons 'new-key 'new-value alist)
    

Property list

  • It’s a list of alternating key-values

  • Get element

    ;; `getf'/`remf' always use `eq' for comparison.
    
    ;; get value
    (getf '(:hello "world" :hi 5) :hi) ; => 5
    
    ;; set value
    (setf (getf *plist* :a) 2)
    
    ;; remove value
    (remf *plist* :a)
    
    ;; `get-properties' to extract one of multiple properties.
    (defun process-properties (plist keys)
      (loop while plist do
        (multiple-value-bind (key value tail) (get-properties plist keys)
          (when key (process-property key value))
          (setf plist (cddr tail)))))
    
    
    ;; Every symbol object has an associated property list that you can
    ;; get with `symbol-plist' function.
    (symbol-plist '*x*)
    ;; There is a shortcut to get the necessary property rather than the
    ;; whole list.
    (get 'symbol 'key) ; === (getf (symbol-plist 'symbol) 'key)
    ;; `get' is `setf'able
    (setf (get 'symbol 'key) "information")
    ;; to remove property:
    (remprop 'symbol 'key) ; === (remf (symbol-plist 'symbol) 'key)
    

Common Lisp: Multiple Dispatch

Common Lisp: classes

Slots inheritance

During inheritance, all slot specifiers with the same name are merged to create a single slot specifier.

  • for :initform, the most specific class wins

  • :initarg values are merged to allow any of individual :initarg to initialize the slot

    • If the caller passes multiple keyword argument for the same field, the leftmost keyword in invocation wins

  • :reader, :writer, :accessor are not merged because previously created accessor functions will apply to the new class already

  • :allocation is determined by the most specific class

    • if subclass specified a :class slot, the slot will by shared by that subclass only (not shared with the original superclass)

Multiple inheritance and class specificity

  • classes specified earlier in defclass direct superclass list are considered more specific

Common Lisp: Format

Common Lisp: condition system

Common Lisp: special operators

Common Lisp: multiple values

Common Lisp: packages and symbols

Common Lisp: readtable

Resources

Backlinks