Common LISP
Install
mkdir ~/temp
cd ~/temp
wget http://prdownloads.sourceforge.net/sbcl/sbcl-2.3.9-x86-64-linux-binary.tar.bz2
tar xvjf sbcl-2.3.9-x86-64-linux-binary.tar.bz2
cd sbcl-2.3.9-x86-64-linux/
sudo INSTALL_ROOT=/opt/sbcl sh install.sh
export SBCL_HOME=/opt/sbcl/lib/sbcl
Information
ANSI INCITS 226-1994 (S20018)[1] (formerly X3.226-1994 (R1999)). ANSI common LISP.
Created 1968.
Hello World
Simple as that:
"Hello, World!"
More complex:
(format t "Hello, World!~%")
or:
(concatenate 'string "Hello, " "World!")
Difference with Clojure: https://clojure.org/reference/lisps
Installation
CentOS, Rocky Linux
Steel bank common lisp
dnf install -y sbcl
dnf install -y clisp
Windows
Download and install SBCL - Steel Bank Common Lisp.
Post install
Install Quicklisp.
Usage, tips and tricks
Working with System (Project)
;; Load main system
(asdf:already-loaded-systems)
(pushnew (truename "./") ql:*local-project-directories*)
;(require "cl-start-project")
(ql:quickload :cl-start-project)
(asdf:already-loaded-systems)
;; Load tests system
(ql:quickload :rove)
;(require "cl-start-project/tests")
(ql:quickload :cl-start-project/tests)
(asdf:already-loaded-systems)
;; Run tests
(rove:run :cl-start-project/tests)
;; Make executable
(asdf:make :cl-start-project)
Run system (project/app)
TODO : how to download dependencies, so application can be executed, locally, by developer with all its dependencies ( for example with alexandria)?
Packaging system (project/app)
TODO : how to package it with all its dependencies, so it can be executed in docker for example.
Compiling
To FASL (fast-load file) binary format.
NB! Works with same version of sbcl that compiled the file. Version compatibility can also lead to hard debuggable bugs.
(compile-file "tests/hello.lisp")
ASDF
~/common-lisp
~/.local/share/common-lisp/source/
~/.config/common-lisp/source-registry.conf.d/
~/common-lisp/asdf/
# C:/Users/USERNAME/quicklisp
# C:/Users/USERNAME/common-lisp
~/.config/common-lisp/source-registry.conf
System files
Usual example file:
;; Usual Lisp comments are allowed here
(defsystem "hello-lisp"
:description "hello-lisp: a sample Lisp system."
:version "0.0.1"
:author "Imre Tabur <imre.tabur@mail.ee>"
:licence "MIT"
:depends-on ("optima.ppcre" "command-line-arguments")
:components
((:file "packages")
(:file "macros" :depends-on ("packages"))
(:file "hello" :depends-on ("macros"))))
Created with quicklisp
(defsystem "first-app"
:version "0.1.0"
:author "Imre Tabur <info@setmy.info>"
:license "MIT"
:depends-on ("alexandria")
:components
((:module "src"
:components
((:file "main"))))
:description "A sample Lisp system(project)."
:in-order-to ((test-op (test-op "first-app/tests"))))
(defsystem "first-app/tests"
:author "imret"
:license "MIT"
:depends-on
("first-app"
"rove")
:components
((:module "tests"
:components
((:file "main"))))
:description "Test system for first-app"
:perform (test-op (op c) (symbol-call :rove :run c)))
IDE
Currently, setmy.info standard IDE for Common LISP is VSCode.
With plugins:
Data types
Collections
List
Plain list.
'(1 2.2 1/2 "Hello" nil)
;; same as previous
;; (1 2.2 1/2 "Hello" NIL)
(list 1 2.2 1/2 "Hello" nil)
Association Lists
(defparameter *example-asso-list* (list (cons :a 1) (cons :b 2) (cons :c 3) (cons :d 1.1) (cons :e "Hello, world") (cons :f T) (cons :g nil)))
(cdr (first (member ':a *example-asso-list* :key 'car)))
(cdr (first (member ':b *example-asso-list* :key 'car)))
(cdr (first (member ':c *example-asso-list* :key 'car)))
(cdr (first (member ':d *example-asso-list* :key 'car)))
(cdr (first (member ':e *example-asso-list* :key 'car)))
(cdr (first (member ':f *example-asso-list* :key 'car)))
(cdr (first (member ':g *example-asso-list* :key 'car)))
(assoc ':e *example-asso-list*)
Plist
Collection of elements where any other element “describes” next element.
(defparameter *exampleplist* (list :a 1 :b 2 :c 3 :d 1.1 :e "Hello, world" :f T :g nil))
(getf *exampleplist* :a)
(getf *exampleplist* :b)
(getf *exampleplist* :c)
(getf *exampleplist* :d)
(getf *exampleplist* :e)
(getf *exampleplist* :f)
(getf *exampleplist* :g)
(defparameter *ages* (list 'Peter 34 'Meeter 23 'Tom 72))
(getf *ages* 'Tom)
Vector
Integer indexed collection.
#(1 2 3)
;; same as previous
(vector 1 2 3)
Set
Hash Tables (Map?)
(defparameter *h* (make-hash-table))
(gethash 'foo *h*)
(setf (gethash 'foo *h*) 'foo1)
(gethash 'foo *h*)
(setf (gethash 'foo *h*) "Foo2")
(gethash 'foo *h*)
Misc
Some miscellaneous code parts - unsorted, systemized, structured etc.
Executing Steel bank common lisp script
sbcl --script src\lisp\main\main.lisp
Some unselected code parts about loading and working with asdf and quicklisp.
(require "asdf")
;; When quicklisp is loaded?
(require "cl-start-project")
(asdf:already-loaded-systems)
(load "quicklisp.lisp")
(asdf:load-asd
(merge-pathnames "cl-start-project" "C:/sources/setmy.info/incubation/lisp"))
;(asdf:load-asd (merge-pathnames "cl-start-project" (uiop:getcwd)))
(asdf:already-loaded-systems)
(asdf:load-system "cl-start-project")
;; Like package/system/library search
(ql:system-apropos "alexandria")
;; Make bin file?
(asdf:make :cl-start-project)
(prove:run #P"myapp/tests/my-test.lisp")
Functions
(in-package :cl-user)
(defpackage cl-start-project/main
# |Avoid :use . Instead, do this :
(:use :cl)
(:import-from :cl-start-project/foo :hello-world)
(:import-from :cl-start-project/style :+golden-ratio+)
(:import-from :cl-start-project/lesson :show-math)
Use :import-from
|#
(:use :cl)
(:import-from :cl-start-project/foo :hello-world)
(:import-from :cl-start-project/style :+golden-ratio+)
(:import-from :cl-start-project/lesson :show-math)
;(:export :show-math)
(:export :main))
(in-package :cl-start-project/main)
(defun hello-world ()
(format t "Hello, world~1%"))
(hello-world)
(defun say-hello (name)
"Say hello to 'name'."
(let ((hello-string (format nil "Hello ~a!" name)))
(format t "~a~%" hello-string)
hello-string))
(say-hello "Imre Tabur")
(defun say-hello (name &optional age gender)
"Say hello to 'name'."
(let ((hello-string (format nil "Hello ~a (~d, ~a)!" name age gender)))
(format t "~a~%" hello-string)
hello-string))
(say-hello "Imre Tabur" 20 "M")
(defun say-hello (name &optional age gender)
"Say hello to 'name'."
(let
((hello-string (format nil "Hello ~a (~d, ~a, ~a)!" name age gender status)))
(format t "~a~%" hello-string)
hello-string))
(say-hello "Imre Tabur" :status "Working" 20 "M")
(defun say-hello (name &key status location)
"Say hello to 'name'."
(let
((hello-string (format nil "Hello ~a (~a at ~a)!" name status location)))
(format t "~a~%" hello-string)
hello-string))
(say-hello "Imre Tabur" :status "Working" :location "Home")
(defun say-hello (name &key status (location "Home"))
"Say hello to 'name'."
(let
((hello-string (format nil "Hello ~a (~a at ~a)!" name status location)))
(format t "~a~%" hello-string)
hello-string))
(say-hello "Imre Tabur" :status "Working")
(defun say-hello (&key (name "") (status "Working") (location "Home"))
"Say hello to 'name'."
(let
((hello-string (format nil "Hello ~a (~a at ~a)!" name status location)))
(format t "~a~%" hello-string)
hello-string))
(say-hello)
(say-hello :name "Imre Tabur")
(say-hello :name "Imre Tabur" :status "Vacation")
(say-hello :name "Imre Tabur" :status "Vacation" :location "Africa")
;; Returns same string
(write-line "Hello World!")
;; Hello Geek
;; "Hello Geek"
;; Short comment.
(defparameter *global-variable* "Global variable in earmuffs. All variables should be writen
complete words. You should use lower case. You must not use / or . instead of -")
(defconstant +golden-ratio+ 575 "Global constants should be with plus sign.")
(defconstant +mix32+ #x12b9b0a1 "pi, an arbitrary number")
(defconstant +mix64+ #x2b992ddfa23249d6 "more digits of pi")
(format t "~d ~%" +mix32+)
(format t "~f ~%" +mix64+)
(format t "~s ~%" "Hello World")
;; Print out symbol.
(format t "~s ~%" :this-is-symbol)
(parse-integer "-64")
(parse-integer "no integer string" :junk-allowed t)
;; T
(string= "foo" "foo")
;; NIL
(string= "foo" "Foo")
;; T
(string= "petronas pizza" "pizza" :start1 10 :end1 14 :start2 1)
(ql:quickload "parse-float")
(parse-float:parse-float "1.2e2")
(if (< 3 5)
(format t "~s ~%" "True")
(format t "~s ~%" "False"))
; Like if-elseif-else switch-case
;(defparameter *global-variable* 4)
;(defparameter *global-variable* 5)
(defparameter *global-variable* 6)
(cond ((< *global-variable* 5) (format t "~s ~%" "First"))
((< *global-variable* 6) (format t "~s ~%" "Second"))
(t (format t "~s ~%" "Third")))
;; Short comment.
(defun is-it-good-p (input-data)
"Function that returns T or NIL should end with -p (predicate). If the rest of the function
name is a single word, e.g: abstractp, bluep, evenp. If the rest of the function name is more
than one word, e.g largest-planet-p, request-throttled-p."
;; Region 2. Two semicolons.
nil)
;; Class example
(defclass person ()
((first-name
:initarg :first-name
:initform "")
(last-name
:initarg :last-name
:initform "")))
(defparameter *person* (make-instance 'person :first-name "John" :last-name "Doe"))
;; See the ~a and ~s difference
(format t "Person: first name ~a last name ~s ~%" (slot-value *person* 'first-name) (slot-value *person* 'last-name))
;; Same thing to return string
(format NIL "Person: first name ~a last name ~s ~%" (slot-value *person* 'first-name) (slot-value *person* 'last-name))
;;--- TODO(info@setmy.info): Refactor to provide a better API. Remove this code after release 1.7
;; or before 2099-12-31.
(defun foo-bar ()
(format t "Hello, world~1%"))
(type-of :abc)
;; KEYWORD
(type-of "abc")
;; (SIMPLE-ARRAY CHARACTER (3))
(type-of ':abc)
;; KEYWORD
(type-of *person*)
;; PERSON
(type-of 'hello-world)
;; SYMBOL
(type-of #'hello-world)
;; COMPILED-FUNCTION
(type-of nil)
;; NULL
(type-of t)
;; BOOLEAN
(reduce #'+ '(1 2 3 4))
(mapcar #'sqrt '(1 2 3 4))
(mapcan (lambda (x) (if (oddp x) (list x))) '(1 2 3 4 5))
(defun do-fun (in-func)
(funcall in-func 1 2))
(do-fun #'+)
(defun do-fun-again (in-func &rest other-arguments)
(apply in-func other-arguments))
(do-fun-again #'+ 1 2)
(do-fun-again #'- 1 (do-fun-again #'+ 1 2))
(defparameter *a-list* (list 1 2 3 4))
;; x is actually a list
(maplist (lambda (x) x) *a-list*)
;;((1 2 3 4) (2 3 4) (3 4) (4))
(maplist (lambda (a-list) (+ (car a-list) 1)) *a-list*)
;; (2 3 4 5)
(mapcar (lambda (a-list-item) (+ a-list-item 1)) *a-list*)
;; (2 3 4 5)
;; filters - named as Remove If Not
(remove-if-not (lambda (x) (>= x 2)) *a-list*)
(defun Σ (a-list)
(reduce #'+ a-list))
(Σ *a-list*)
;; 10
(Σ (remove-if-not (lambda (x) (>= x 2)) *a-list*))
;; 9
(reduce (lambda (acc x) (append acc (list (+ x 1)))) *a-list* :initial-value nil)
;; (2 3 4 5)
(first *a-list*)
;; 1
(second *a-list*)
;; 2
(setf (second *a-list*) 6)
*a-list*
;; 6
;; (1 6 3 4)
(nth 0 *a-list*)
(nth 1 *a-list*)
;; 1
;; 6
(setf (nth 1 *a-list*) 65)
*a-list*
(1 65 3 4)
;; Reset as initial list was.
(setf (nth 1 *a-list*) 2)
(mapcar #'evenp *a-list*)
;; (NIL T NIL T)
(mapcar #'string-upcase (list "Hello" "world!"))
;; ("HELLO" "WORLD!")
(concatenate 'string "hello " "world")
;; "hello world"
(sort (list 9 2 4 7 3 0 8) #'<)
(sort (list "B" "A" "C") #'string<)
;; ("A" "B" "C")
(defparameter *person-list*
(list
(make-instance 'person :first-name "John1" :last-name "Doe1")
(make-instance 'person :first-name "John2" :last-name "Doe2")
(make-instance 'person :first-name "John3" :last-name "Doe3")))
(slot-value
(first
(sort
*person-list*
#'(lambda (person-left person-right)
"case-sensitive: string=, string/=, string>, string<; and case insensitive: string-equal, string-not-equal, string-greaterp, string-lessp, string-not-lessp, string-not-greaterp"
(string<
(slot-value person-left 'first-name)
(slot-value person-right 'first-name)))))
'first-name)
;; "John1"
;; new line
(terpri)
;; Curring
(defun add (x)
(lambda (y) (+ x y)))
(defvar add-five (add 5))
(defvar result (funcall add-five 3))
Function composition
(defun h (x) (* x 2))
(defun g (x) (+ x 3))
(defun f (x) (expt x 2))
(defun compose (a b c)
(lambda (x) (funcall c (funcall b (funcall a x)))))
(defun composed-function (x)
(funcall (compose #'f #'g #'h) x))
(composed-function 5)
#!/opt/sbcl/bin/sbcl --script
(require :asdf)
(load "~/.quicklisp/setup.lisp")
(load "~/.sbclrc")
(ql:quickload "vecto")
(write-line "Hello World")
Some libraries
- Loads of utilities (alexandria - (ql:quickload : alexandria)) https://alexandria.common-lisp.dev/ https://gitlab.common-lisp.net/alexandria/alexandria
- Regular expressions (cl-ppcre - (ql:quickload :cl-ppcre)) https://github.com/edicl/cl-ppcre
- Clojure-like arrow macros (cl-arrows - (ql:quickload :cl-arrows)) https://github.com/nightfly19/cl-arrows
- String manipulation (cl-strings - (ql:quickload :cl-strings)) https://github.com/diogoalexandrefranco/cl-strings
- https://quickdocs.org/ningle
- https://edicl.github.io/hunchentoot/
- https://edicl.github.io/cl-who/
- https://github.com/fukamachi/clack
- https://github.com/m2ym/cl-annot
- https://github.com/fukamachi/caveman
- https://github.com/arielnetworks/cl-markup
- https://github.com/fukamachi/prove
- https://github.com/fukamachi/rove
- https://github.com/arielnetworks/cl-pattern
- https://github.com/fukamachi/cl-project
- https://github.com/lokedhs/cl-rabbit
- https://github.com/vseloved/cl-redis
- https://marijnhaverbeke.nl/postmodern
- https://quickref.common-lisp.net/local-time.html
- https://quickref.common-lisp.net/local-time.html
- https://cl-library-docs.github.io/common-lisp-libraries/local-time/
- https://local-time.common-lisp.dev/manual.html
- https://github.com/dlowe-net/local-time
- http://naggum.no/lugm-time.html
- https://quickdocs.org/birch
- https://quickdocs.org/maiden
- https://quickdocs.org/cl-irc
- https://github.com/fukamachi/cl-dbi
- https://github.com/fukamachi/mito
- https://quickref.common-lisp.net/cl-jpeg.html
- https://quickref.common-lisp.net/png.html
- https://github.com/eudoxia0/cl-yaml
- https://github.com/mabragor/cl-yaclyaml
- https://github.com/sharplispers/cl-jpeg
- https://github.com/3b/cl-opengl
- https://www.xach.com/lisp/zpng/
- https://github.com/mmontone/cl-rest-server
- https://github.com/hankhero/cl-json
- https://edicl.github.io/drakma/
- https://lisp-journey.gitlab.io/web-dev/
- https://awesome-cl.com/
- https://github.com/CodyReichert/awesome-cl
- https://github.com/fukamachi/dexador
- https://github.com/joaotavora/snooze
- https://github.com/Shinmera/lquery
- https://github.com/Shinmera/plump
See also
Clim-IRC: https://github.com/sjl/clim-irc
IBCL: https://github.com/drewc/ibcl
lisp-irc-client: https://github.com/svetlyak40wt/lisp-irc-client
lispbot: https://github.com/rkoeninger/lispbot
ERC: https://www.emacswiki.org/emacs/ErC
CL-IRC: https://github.com/alex-gutev/cl-irc
LispIRC: https://github.com/shinmera/lispirc
Lichat: https://github.com/harley/lisp-irc
SICL IRC: https://github.com/robert-strandh/SICL-IRC