63 |
64 |
67 |
68 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/assets/atom-slime.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/atom-slime.png
--------------------------------------------------------------------------------
/assets/cl-flamegraph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/cl-flamegraph.png
--------------------------------------------------------------------------------
/assets/cl-logo-blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/cl-logo-blue.png
--------------------------------------------------------------------------------
/assets/cl-repl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/cl-repl.png
--------------------------------------------------------------------------------
/assets/clack-errors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/clack-errors.png
--------------------------------------------------------------------------------
/assets/commonlisp-vscode-alive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/commonlisp-vscode-alive.png
--------------------------------------------------------------------------------
/assets/commonlisp-vscode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/commonlisp-vscode.png
--------------------------------------------------------------------------------
/assets/coverage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/coverage.png
--------------------------------------------------------------------------------
/assets/editor-sublime.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/editor-sublime.png
--------------------------------------------------------------------------------
/assets/emacs-company-elisp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/emacs-company-elisp.png
--------------------------------------------------------------------------------
/assets/emacs-helpful.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/emacs-helpful.png
--------------------------------------------------------------------------------
/assets/emacs-teaser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/emacs-teaser.png
--------------------------------------------------------------------------------
/assets/emacs-which-key-minibuffer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/emacs-which-key-minibuffer.png
--------------------------------------------------------------------------------
/assets/geany.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/geany.png
--------------------------------------------------------------------------------
/assets/github.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Inspired by github's default code highlighting
3 | */
4 | pre { white-space: pre; background-color: #f8f8f8; border: 1px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; }
5 | pre code.hl-highlighted {white-space: pre; margin: 0; padding: 0; background: none; border: none; overflow-x: auto; font-size: 13px;}
6 | code.hl-highlighted {margin: 0 2px; padding: 0 5px; white-space: nowrap; font-family: Consolas, "Liberation Mono", Courier, monospace; background: #f8f8f8; border: 1px solid #eaeaea; border-radius: 3px;}
7 |
8 | code.hl-highlighted {color: #008080;}
9 | code.hl-highlighted .function {color: #008080;}
10 | code.hl-highlighted .function.known {color: #800603;}
11 | code.hl-highlighted .function.known.special {color: #2d2d2d; font-weight: bold;}
12 | code.hl-highlighted .keyword {color: #990073;}
13 | code.hl-highlighted .keyword.known {color: #990073;}
14 | code.hl-highlighted .symbol {color: #75a;}
15 | code.hl-highlighted .lambda-list {color: #966;}
16 | code.hl-highlighted .number {color: #800;}
17 | code.hl-highlighted .variable.known {color: #c3c;}
18 | code.hl-highlighted .variable.global {color: #939;}
19 | code.hl-highlighted .variable.constant {color: #229;}
20 | code.hl-highlighted .nil {color: #f00;}
21 | code.hl-highlighted .list {color: #222;}
22 |
23 | code.hl-highlighted .string, code.hl-highlighted .string * {color: #d14 !important;}
24 | code.hl-highlighted .comment,
25 | code.hl-highlighted .comment *,
26 | code.hl-highlighted .comment .string
27 | code.hl-highlighted .comment .string * {color: #777777 !important;}
28 | code.hl-highlighted .string .comment {color: #d14 !important;}
29 |
30 | code.hl-highlighted .list.active {display: inline-block; background: #aefff7;}
31 |
--------------------------------------------------------------------------------
/assets/gui/gtk3-hello-buttons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/gui/gtk3-hello-buttons.png
--------------------------------------------------------------------------------
/assets/gui/ltk-on-macos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/gui/ltk-on-macos.png
--------------------------------------------------------------------------------
/assets/gui/mediaplayer-nodgui-arc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/gui/mediaplayer-nodgui-arc.png
--------------------------------------------------------------------------------
/assets/gui/nodgui-feet2meters-yaru.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/gui/nodgui-feet2meters-yaru.png
--------------------------------------------------------------------------------
/assets/gui/nodgui-treeview-yaru.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/gui/nodgui-treeview-yaru.png
--------------------------------------------------------------------------------
/assets/gui/nuklear-test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/gui/nuklear-test.png
--------------------------------------------------------------------------------
/assets/gui/nuklear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/gui/nuklear.png
--------------------------------------------------------------------------------
/assets/gui/qtools-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/gui/qtools-intro.png
--------------------------------------------------------------------------------
/assets/img-ci-build.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/img-ci-build.png
--------------------------------------------------------------------------------
/assets/iup-demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/iup-demo.png
--------------------------------------------------------------------------------
/assets/jetbrains-slt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/jetbrains-slt.png
--------------------------------------------------------------------------------
/assets/jquery.toc/jquery.toc.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Table of Contents jQuery Plugin - jquery.toc
3 | *
4 | * Copyright 2013-2016 Nikhil Dabas
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License
12 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13 | * or implied. See the License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | (function ($) {
18 | "use strict";
19 |
20 | // Builds a list with the table of contents in the current selector.
21 | // options:
22 | // content: where to look for headings
23 | // headings: string with a comma-separated list of selectors to be used as headings, ordered
24 | // by their relative hierarchy level
25 | var toc = function (options) {
26 | return this.each(function () {
27 | var root = $(this),
28 | data = root.data(),
29 | thisOptions,
30 | stack = [root], // The upside-down stack keeps track of list elements
31 | listTag = this.tagName,
32 | currentLevel = 0,
33 | headingSelectors;
34 |
35 | // Defaults: plugin parameters override data attributes, which override our defaults
36 | thisOptions = $.extend(
37 | {content: "body", headings: "h1,h2,h3"},
38 | {content: data.toc || undefined, headings: data.tocHeadings || undefined},
39 | options
40 | );
41 | headingSelectors = thisOptions.headings.split(",");
42 |
43 | // Set up some automatic IDs if we do not already have them
44 | $(thisOptions.content).find(thisOptions.headings).attr("id", function (index, attr) {
45 | // In HTML5, the id attribute must be at least one character long and must not
46 | // contain any space characters.
47 | //
48 | // We just use the HTML5 spec now because all browsers work fine with it.
49 | // https://mathiasbynens.be/notes/html5-id-class
50 | var generateUniqueId = function (text) {
51 | // Generate a valid ID. Spaces are replaced with underscores. We also check if
52 | // the ID already exists in the document. If so, we append "_1", "_2", etc.
53 | // until we find an unused ID.
54 |
55 | if (text.length === 0) {
56 | text = "?";
57 | }
58 |
59 | var baseId = text.replace(/\s+/g, "_"), suffix = "", count = 1;
60 |
61 | while (document.getElementById(baseId + suffix) !== null) {
62 | suffix = "_" + count++;
63 | }
64 |
65 | return baseId + suffix;
66 | };
67 |
68 | return attr || generateUniqueId($(this).text());
69 | }).each(function () {
70 | // What level is the current heading?
71 | var elem = $(this), level = $.map(headingSelectors, function (selector, index) {
72 | return elem.is(selector) ? index : undefined;
73 | })[0];
74 |
75 | if (level > currentLevel) {
76 | // If the heading is at a deeper level than where we are, start a new nested
77 | // list, but only if we already have some list items in the parent. If we do
78 | // not, that means that we're skipping levels, so we can just add new list items
79 | // at the current level.
80 | // In the upside-down stack, unshift = push, and stack[0] = the top.
81 | var parentItem = stack[0].children("li:last")[0];
82 | if (parentItem) {
83 | stack.unshift($("<" + listTag + "/>").appendTo(parentItem));
84 | }
85 | } else {
86 | // Truncate the stack to the current level by chopping off the 'top' of the
87 | // stack. We also need to preserve at least one element in the stack - that is
88 | // the containing element.
89 | stack.splice(0, Math.min(currentLevel - level, Math.max(stack.length - 1, 0)));
90 | }
91 |
92 | // Add the list item
93 | $("").appendTo(stack[0]).append(
94 | $("").text(elem.text()).attr("href", "#" + elem.attr("id"))
95 | );
96 |
97 | currentLevel = level;
98 | });
99 | });
100 | }, old = $.fn.toc;
101 |
102 | $.fn.toc = toc;
103 |
104 | $.fn.toc.noConflict = function () {
105 | $.fn.toc = old;
106 | return this;
107 | };
108 |
109 | // Data API
110 | $(function () {
111 | toc.call($("[data-toc]"));
112 | });
113 | }(window.jQuery));
114 |
--------------------------------------------------------------------------------
/assets/jquery.toc/jquery.toc.min.js:
--------------------------------------------------------------------------------
1 | /*! Table of Contents jQuery Plugin - jquery.toc * Copyright (c) 2013-2016 Nikhil Dabas * http://www.apache.org/licenses/LICENSE-2.0 */
2 | !function(a){"use strict";var b=function(b){return this.each(function(){var c,d,e=a(this),f=e.data(),g=[e],h=this.tagName,i=0;c=a.extend({content:"body",headings:"h1,h2,h3"},{content:f.toc||void 0,headings:f.tocHeadings||void 0},b),d=c.headings.split(","),a(c.content).find(c.headings).attr("id",function(b,c){var d=function(a){0===a.length&&(a="?");for(var b=a.replace(/\s+/g,"_"),c="",d=1;null!==document.getElementById(b+c);)c="_"+d++;return b+c};return c||d(a(this).text())}).each(function(){var b=a(this),c=a.map(d,function(a,c){return b.is(a)?c:void 0})[0];if(c>i){var e=g[0].children("li:last")[0];e&&g.unshift(a("<"+h+"/>").appendTo(e))}else g.splice(0,Math.min(i-c,Math.max(g.length-1,0)));a("").appendTo(g[0]).append(a("").text(b.text()).attr("href","#"+b.attr("id"))),i=c})})},c=a.fn.toc;a.fn.toc=b,a.fn.toc.noConflict=function(){return a.fn.toc=c,this},a(function(){b.call(a("[data-toc]"))})}(window.jQuery);
--------------------------------------------------------------------------------
/assets/jupyterpreview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/jupyterpreview.png
--------------------------------------------------------------------------------
/assets/lem-sdl2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/lem-sdl2.png
--------------------------------------------------------------------------------
/assets/lem-terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/lem-terminal.png
--------------------------------------------------------------------------------
/assets/lem1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/lem1.png
--------------------------------------------------------------------------------
/assets/lispworks-graphical-inspector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/lispworks-graphical-inspector.png
--------------------------------------------------------------------------------
/assets/lispworks/class-browser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/lispworks/class-browser.png
--------------------------------------------------------------------------------
/assets/lispworks/function-call-browser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/lispworks/function-call-browser.png
--------------------------------------------------------------------------------
/assets/lispworks/process-browser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/lispworks/process-browser.png
--------------------------------------------------------------------------------
/assets/lispworks/stepper.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/lispworks/stepper.gif
--------------------------------------------------------------------------------
/assets/lispworks/toolbar-debugger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/lispworks/toolbar-debugger.png
--------------------------------------------------------------------------------
/assets/lispworks/two-sided-view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/lispworks/two-sided-view.png
--------------------------------------------------------------------------------
/assets/log4cl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/log4cl.png
--------------------------------------------------------------------------------
/assets/portacle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/portacle.png
--------------------------------------------------------------------------------
/assets/prove-report.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/prove-report.png
--------------------------------------------------------------------------------
/assets/slime-inspector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/slime-inspector.png
--------------------------------------------------------------------------------
/assets/slime-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/slime-menu.png
--------------------------------------------------------------------------------
/assets/slime.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/slime.png
--------------------------------------------------------------------------------
/assets/slimv.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/slimv.jpg
--------------------------------------------------------------------------------
/assets/sockets-lisp-chat.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/sockets-lisp-chat.gif
--------------------------------------------------------------------------------
/assets/style.scss:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | @charset 'utf-8';
5 |
6 | img{
7 | max-width:100%;
8 | }
9 | #toc a{
10 | color: #696969;
11 | }
12 | #title-xs{
13 | display:none;
14 | }
15 | #logo-container{
16 | z-index:1; // so the TOC is above the announce and the "page source" bar.
17 | height:90px;
18 | width:23%;
19 | margin-left:1%;
20 | margin-right:1%;
21 | position:fixed;
22 | }
23 |
24 | @media only screen and (max-width: 576px) {
25 | #searchform-container {
26 | display: none;
27 | }
28 | }
29 |
30 | #logo{
31 | width:auto;
32 | height:70px;
33 | margin:15px;
34 | }
35 | #toc-title{
36 | display:none;
37 | }
38 |
39 | #content-container {
40 | margin-left:25%;
41 | width:75%;
42 | padding:2%;
43 | position:static;
44 | overflow-y:scroll;
45 | max-height:100%;
46 | }
47 |
48 |
49 | // toc-btn display is handled by toggle-toc.js, to be able to not render it on root
50 |
51 | @media only screen and (max-width:576px){
52 | #logo{
53 | display:none;
54 | }
55 | #title-xs{
56 | padding:2%;
57 | width:100%;
58 | position:static;
59 | margin-left:0;
60 | display:block;
61 | }
62 | #title-non-xs{
63 | display:none;
64 | }
65 | #toc-title{
66 | display:block;
67 | background-color:#111111;
68 | color:orange;
69 | text-align:center;
70 | width:100%;
71 | padding:0;
72 | }
73 | #toc-container{
74 | display:block;
75 | position:fixed;
76 | margin:0;
77 | padding:2%;
78 | overflow-y:auto;
79 | top:0;
80 | width:80%;
81 | height:100vh;
82 | background-color:#eeeeee;
83 | transition:all 0.2s ease;
84 | }
85 | .toc-close{
86 | right:-85%;
87 | box-shadow:none;
88 | }
89 | .toc-open{
90 | right:0;
91 | box-shadow:0vw 0vw 20vw black;
92 | }
93 | #content-container{
94 | display:block;
95 | padding-top:0;
96 | margin:0;
97 | position:static;
98 | margin-left:0;
99 | width:100%;
100 | }
101 | #toc-btn{
102 | z-index: 2; // above the toc-container
103 | position:fixed;
104 | right:0%;
105 | bottom:10%;
106 | width:45px;
107 | padding:5px;
108 | font-size:1.25em;
109 | text-align:center;
110 | border-radius:5px;
111 | border-style:solid;
112 | // display:block;
113 | background-color:#111111;
114 | color:orange;
115 | font-weight:700;
116 | }
117 | }
118 |
119 | @media only screen and (min-width:576px){
120 | #toc-container {
121 | margin-left:1%;
122 | margin-right:1%;
123 | margin-top:25px;
124 | margin-bottom:10%;
125 | padding:1%;
126 | width:23%;
127 | position:fixed;
128 | height:calc(90% - 90px); // logo + searchbox
129 | overflow-y:auto;
130 | }
131 | }
132 |
133 | @media only screen and (min-width:576px) and (max-width:991px){
134 | #toc-container {
135 | width: 30%;
136 | }
137 | #content-container {
138 | width:70%;
139 | margin-left:30%;
140 | }
141 | }
142 |
143 | footer {
144 | color: darkgrey;
145 |
146 | }
147 |
148 | .page-source {
149 | background-color: #add8e6;
150 | padding: 1em;
151 | text-align: center;
152 | opacity: 0.9;
153 | }
154 |
155 | .announce-neutral {
156 | background-color: #add8e6;
157 | padding: 1em;
158 | text-align: center;
159 | opacity: 0.9;
160 | }
161 | .announce {
162 | // background-color: #8dd090;
163 | padding: 1em;
164 | text-align: center;
165 |
166 | // Orange announce:
167 | // background-color: #f48224;
168 | // color: white;
169 | background-color: gold;
170 | // background-color: rgba(255, 0, 0, 0.6);
171 | color: #3b3b3b;
172 | margin: 1em 0 1em 0;
173 | }
174 |
175 | .announce-green {
176 | // background-color: MediumAquamarine
177 | background-color: MediumAquamarine
178 | }
179 |
180 | .announce a {
181 | text-decoration: underline;
182 | // color: white;
183 | color: #3b3b3b;
184 | }
185 |
186 | ul{
187 | margin-left:0px;
188 | }
189 | ul ul{
190 | margin-left:-16px;
191 | }
192 | ul ul ul{
193 | margin-left:-26px;
194 | }
195 |
196 | // Alert / Info boxes
197 |
198 | .info-box {
199 | // note: if element is inside a
then bootstrap adds 10px padding to the bottom.
200 | padding: 17px;
201 | }
202 |
203 | .danger {
204 | background-color: #ffdddd;
205 | border-left: 6px solid #f44336;
206 | }
207 |
208 | .success {
209 | background-color: #ddffdd;
210 | border-left: 6px solid #4CAF50;
211 | }
212 |
213 | .info {
214 | background-color: #e7f3fe;
215 | border-left: 6px solid #2196F3;
216 | }
217 |
218 |
219 | .warning {
220 | background-color: #ffffcc;
221 | border-left: 6px solid #ffeb3b;
222 | }
223 |
--------------------------------------------------------------------------------
/assets/toggle-toc.js:
--------------------------------------------------------------------------------
1 | $(document).ready( function () {
2 | TOCVisible = false;
3 |
4 | function toggleTOCButton(e){
5 | if (window.location.pathname == "/cl-cookbook/"){
6 | document.getElementById("toc-btn").style.display = "none";
7 | }else if ($(document).width() <= 576){
8 | document.getElementById("toc-btn").style.display = "block";
9 | }else{
10 | document.getElementById("toc-btn").style.display = "none";
11 | }
12 | }
13 |
14 | setInterval(toggleTOCButton, 200);
15 |
16 | toggleTOC = function(){
17 | toc = document.getElementById("toc-container");
18 | if (!TOCVisible){
19 | toc.classList.remove("toc-close");
20 | toc.classList.add("toc-open");
21 | TOCVisible = true;
22 | }
23 | else{
24 | toc.classList.remove("toc-open");
25 | toc.classList.add("toc-close");
26 | TOCVisible = false;
27 | }
28 | }
29 |
30 | $('#toc-container').click(function(e) {
31 | if (($(document).width() <= 576) && ($(e.target).is('a'))){
32 | console.log("yes!");
33 | toggleTOC();
34 | }
35 | })
36 | $('#content-container').click(function(e) {
37 | if (($(e.target).is('#toc-btn'))
38 | || (($(document).width() <= 576) && TOCVisible)){
39 | toggleTOC();
40 | }
41 | })
42 | })
43 |
--------------------------------------------------------------------------------
/assets/vscode-gifs/attach-repl.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/vscode-gifs/attach-repl.gif
--------------------------------------------------------------------------------
/assets/vscode-gifs/compile.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/vscode-gifs/compile.gif
--------------------------------------------------------------------------------
/assets/vscode-gifs/debug-abort.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/vscode-gifs/debug-abort.gif
--------------------------------------------------------------------------------
/assets/vscode-gifs/debug-fix.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/vscode-gifs/debug-fix.gif
--------------------------------------------------------------------------------
/assets/vscode-gifs/disassemble.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/vscode-gifs/disassemble.gif
--------------------------------------------------------------------------------
/assets/vscode-gifs/docker-connect.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/vscode-gifs/docker-connect.gif
--------------------------------------------------------------------------------
/assets/vscode-gifs/eval.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/vscode-gifs/eval.gif
--------------------------------------------------------------------------------
/assets/vscode-gifs/in-line-eval.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/vscode-gifs/in-line-eval.gif
--------------------------------------------------------------------------------
/assets/vscode-gifs/macro-expand.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/vscode-gifs/macro-expand.gif
--------------------------------------------------------------------------------
/assets/vscode-gifs/skeleton.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/vscode-gifs/skeleton.gif
--------------------------------------------------------------------------------
/assets/weblocks-quickstart-check-task.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/weblocks-quickstart-check-task.gif
--------------------------------------------------------------------------------
/assets/youtube-little-bits-lisp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/youtube-little-bits-lisp.jpg
--------------------------------------------------------------------------------
/assets/zenity-prompt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/assets/zenity-prompt.png
--------------------------------------------------------------------------------
/cl21.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Common Lisp for the 21st century
3 | ---
4 |
5 | CAUTION: We were excited by `CL21`, but we must admit that the project
6 | is stalling and suffers from unresolved issues. We can't recommend it
7 | anymore. Hopefully, you'll now find many equivalent features in other
8 | libraries (`generic-cl`, `rutils`, `access` and many more).
9 |
10 |
11 | [CL21](http://cl21.org/) is an experimental project redesigning Common
12 | Lisp. It makes some common things that were tedious to do much easier
13 | and brings some new welcome features:
14 |
15 | * more functional programming facilities,
16 | * more object oriented, generic functions.
17 | * new syntax (for regular expressions, hash-tables, string-interpolation,…),
18 | * symbols organized into several packages,
19 | * written in pure Common Lisp.
20 |
21 | CL21 is a bit disruptive in the sense that it redefines usual symbols
22 | (specially for the generic functions). Nevertheless, in doubt, *you can always use a regular CL symbol* by accessing it in the `cl:` package. You might want to check
23 | other related projects, that go beyond
24 | [Alexandria](https://common-lisp.net/project/alexandria) and stay
25 | "good citizens" of the CL world:
26 |
27 | - [rutils](https://github.com/vseloved/rutils/blob/master/docs/ann-rutils.md) -
28 | a comprehensive and all-encompassing suite of syntactic utilities to
29 | support modern day-to-day Common Lisp development. It adds readtable
30 | literal syntax for shorter lambdas, hash-tables and vectors
31 | (#`…`, `#h` and `#{…}`, `#v`), some generic functions
32 | (whose name start with `generic-`),
33 | [shortcuts](https://github.com/vseloved/rutils/blob/master/core/abbr.lisp)
34 | for standard operators, and many helpful functions.
35 | - [Serapeum](https://github.com/TBRSS/serapeum) is a set of utilities
36 | beyond Alexandria, as a supplement. It defines a lot of helper
37 | functions (see its
38 | [reference](https://github.com/TBRSS/serapeum/blob/master/REFERENCE.md))
39 | for macros, data structures (including more functions for trees,
40 | queues), sequences (`partition`, `keep`,…), strings, definitions
41 | (`defalias`,…),… no new reader macros here.
42 | - [generic-cl](https://github.com/alex-gutev/generic-cl/) is a generic interface to standard Common Lisp functions.
43 |
44 | ## Motivation
45 |
46 | From [http://cl21.org/](http://cl21.org/) :
47 |
48 | > Dear Common Lispers,
49 | >
50 | > Common Lisp has the most expressive power of any modern language. It has first class functions with lexical closures, an object system with multiple-dispatch and a metaobject protocol, true macros, and more. It is ANSI standardized and has numerous high-performance implementations, many of which are free software.
51 | >
52 | > In spite of this, it has not had much success (at least in Japan). Its community is very small compared to languages like Ruby and most young Lispers are hacking with Clojure.
53 | >
54 | > Why? Common Lisp is much faster than them. Ruby has no macros and even Clojure doesn't have reader macros. Why then?
55 | >
56 | > Because these languages are well-designed and work for most people for most purposes. These languages are easy to use and the speed isn't an issue.
57 | >
58 | > Is Common Lisp sufficiently well-designed? I don't think so. You use different functions to do the same thing to different data types (elt, aref, nth). You have long names for commonly used macros (destructuring-bind, multiple-value-bind). There is no consistency in argument order (getf and gethash). To put it simply, the language is time-consuming to learn.
59 | >
60 | > Given this, how can programmers coming from other languages believe Common Lisp is the most expressive one?
61 | >
62 | > Fortunately in Common Lisp we can improve the interface with abstractions such as functions, macros, and reader macros. If you believe our language is the most expressive and powerful language, then let's justify that belief.
63 | >
64 | > We should consider the future of the language, not only for ourselves but for the next generation.
65 |
66 |
67 | ## Install and use
68 |
69 | CL21 is in Quicklisp.
70 |
71 | To get its latest version, do:
72 |
73 | ~~~lisp
74 | (ql-dist:install-dist "http://dists.cl21.org/cl21.txt")
75 | (ql:quickload :cl21)
76 | ~~~
77 |
78 | Use:
79 |
80 | ~~~lisp
81 | (in-package :cl21-user)
82 | (defpackage myapp (:use :cl21))
83 | (in-package :myapp)
84 | ~~~
85 |
86 |
87 | ## Features
88 |
89 | Please bear in mind that the following is only a summary of CL21
90 | features. To be sure not to miss anything, you should read CL21's
91 | [wiki](https://github.com/cl21/cl21/wiki/Language-Difference-between-CL21-and-Common-Lisp),
92 | its automated
93 | [list of major changes from standard CL](https://github.com/cl21/cl21/blob/master/CHANGES_AUTO.markdown)
94 | and better yet, its [sources](https://github.com/cl21/cl21/tree/master/).
95 |
96 | That said, go include CL21 in your new project and enjoy, it's worth it !
97 |
98 | ### Functional programming
99 |
100 | #### Shorter lambda
101 |
102 | `lm` is a macro for creating an anonymous function:
103 |
104 |
105 | ~~~lisp
106 | (lm (x) (typep x 'cons))
107 | ;=> #
108 |
109 | (map (lm (x) (+ 2 x)) '(1 2 3))
110 | ;=> (3 4 5)
111 | ~~~
112 |
113 | `^` is a reader macro which will be expanded to `lm`.
114 |
115 | ~~~lisp
116 | ^(typep % 'cons)
117 | ;=> #
118 |
119 | (map ^(+ 2 %) '(1 2 3))
120 | ;=> (3 4 5)
121 | ~~~
122 |
123 | Unused arguments will be ignored automatically.
124 |
125 | ~~~lisp
126 | (map ^(random 10) (iota 10))
127 | ;=> (6 9 5 1 3 5 4 0 7 4)
128 | ~~~
129 |
130 | `%n` designates the nth argument (1-based). `%` is a synonym for `%1`.
131 |
132 | ~~~lisp
133 | (sort '(6 9 5 1 3 5 4 0 7 4) ^(< %1 %2))
134 | ;=> (0 1 3 4 4 5 5 6 7 9)
135 | ~~~
136 |
137 | #### `function` and `compose`
138 |
139 | `function` is a special operator for getting a function value from a given form.
140 |
141 | If a symbol is given, `function` returns a function value of it.
142 |
143 | ~~~lisp
144 | (function integerp)
145 | ;=> #
146 | ~~~
147 |
148 | If a form which starts with `compose`, `and`, `or` or `not`, `function` returns a composed function.
149 |
150 | ~~~lisp
151 | (function (compose - *))
152 | <=> (compose (function -) (function *))
153 |
154 | (function (and integerp evenp))
155 | <=> (conjoin (function integerp) (function evenp))
156 |
157 | (function (or oddp zerop))
158 | <=> (disjoin (function oddp) (function zerop))
159 |
160 | (function (not zerop))
161 | <=> (complement (function zerop))
162 | ~~~
163 |
164 | `#'` is a reader macro for `function`.
165 |
166 | ~~~lisp
167 | #'(compose - *)
168 | #'(and integerp evenp)
169 | #'(or oddp zerop)
170 | #'(not zerop)
171 | #'(and integerp (or oddp zerop))
172 | ~~~
173 |
174 | #### Currying
175 |
176 | CL21 gets new symbols: `curry` and `rcurry`. See also
177 | [functions](functions.html#currying-functions).
178 |
179 | #### Lazy sequences
180 |
181 | Lazy sequences in CL21
182 | ([src](https://github.com/cl21/cl21/blob/master/src/stdlib/lazy.lisp))
183 | use the new
184 | [abstract classes (wiki)](https://github.com/cl21/cl21/wiki/Abstract-Classes).
185 |
186 | ~~~lisp
187 | (use-package :cl21.lazy)
188 |
189 | (defun fib-seq ()
190 | (labels ((rec (a b)
191 | (lazy-sequence (cons a (rec b (+ a b))))))
192 | (rec 0 1)))
193 |
194 | (take 20 (fib-seq))
195 | ;=> (0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)
196 |
197 | (take 3 (drop-while (lambda (x) (< x 500)) (fib-seq)))
198 | ;=> (610 987 1597)
199 | ~~~
200 |
201 | #### Immutable data structures
202 |
203 | This actually is not included in CL21 but may be worth the addition in
204 | this section. For immutable data structures, see the
205 | [Fset](https://github.com/slburson/fset) library (in Quicklisp).
206 |
207 |
208 | ### Generic functions
209 |
210 | There are several generic functions which have the same name to CL's normal functions.
211 |
212 | * getf
213 | * equalp
214 | * emptyp
215 | * coerce
216 |
217 | ~~~lisp
218 | (defvar *hash* #H(:name "Eitaro Fukamachi" :living "Japan"))
219 |
220 | (getf *hash* :name)
221 | ;=> "Eitaro Fukamachi"
222 |
223 | (coerce *hash* 'plist)
224 | ;=> (:LIVING "Japan" :NAME "Eitaro Fukamachi")
225 | ~~~
226 |
227 | You can define these methods for your own classes.
228 |
229 | There are also new functions:
230 |
231 | * `append`
232 | * `flatten`
233 | * `elt`: Returns the element at position INDEX of SEQUENCE or signals
234 | a ABSTRACT-METHOD-UNIMPLMENETED error if the sequence method is not
235 | implemented for the class of SEQUENCE.
236 | * `emptyp`
237 | * `equalp`
238 | * `split`, `split-if`
239 | * `drop`, `drop-while`
240 | * `take`, `take-while`
241 | * `join`
242 | * `length`
243 | * `keep`, `keep-if`, `nkeep`, `nkeep-if`
244 | * `partition`, `partition-if`
245 |
246 | ### Mapping
247 |
248 | In Common Lisp, functions which have a name starts with "map" are higher-order functions take a function and a sequence.
249 |
250 | It is same to CL21, but in CL21, "map" functions always return a value. This aims to clarify the roles of "iteration" and "mapping".
251 |
252 | For that reason, CL21 doesn't have CL's `mapc` and `mapl`. `maphash` exists, but it returns a new hash table.
253 |
254 | ~~~lisp
255 | (maphash (lm (k v)
256 | (cons k (1+ v)))
257 | #H(:a 1 :b 2))
258 | ;=> #H(:B 3 :A 2)
259 | ~~~
260 |
261 | `map` is similar to Common Lisp's `mapcar` except it can take any kind of sequences, not only list.
262 |
263 | ~~~lisp
264 | (map #'- '(1 2 3 4))
265 | ;=> (-1 -2 -3 -4)
266 |
267 | (map #'- #(1 2 3 4))
268 | ;=> #(-1 -2 -3 -4)
269 | ~~~
270 |
271 | CL21 doesn't have `mapcar`. Use `map` instead.
272 |
273 | Filter is provided with `keep`, `keep-if` (instead of
274 | `remove-if[-not]`) and `nkeep[-if]`.
275 |
276 | ### Iteration
277 |
278 | Common Lisp has simple iteration facilities: `dolist`, `dotimes` and `dolist`.
279 |
280 | In addition, CL21 provides another one: `doeach`.
281 |
282 | `doeach` is similar to `dolist`, but it can be used with any kind of sequences and hash-table.
283 |
284 | ~~~lisp
285 | (doeach (x '("al" "bob" "joe"))
286 | (when (> (length x) 2)
287 | (princ #"${x}\n")))
288 | ;-> bob
289 | ; joe
290 |
291 | (doeach ((key value) #H('a 2 'b 3))
292 | (when (> value 2)
293 | (print key)))
294 | ;=> B
295 | ~~~
296 |
297 | Destructuring binding form can be placed at the variable position.
298 |
299 | ~~~lisp
300 | (doeach ((x y) '((1 2) (2 3) (3 4)))
301 | (print (+ x y)))
302 | ;-> 3
303 | ; 5
304 | ; 7
305 | ~~~
306 |
307 | CL21 also gets a `while` keyword.
308 |
309 | ### New data types
310 |
311 | New data types were added.
312 |
313 | * proper-list
314 | * plist
315 | * alist
316 | * octet
317 | * file-associated-stream
318 | * character-designator
319 | * function-designator
320 | * file-position-designator
321 | * list-designator
322 | * package-designator
323 | * stream-designator
324 | * string-designator
325 |
326 | Most of these are imported from [trivial-types](https://github.com/m2ym/trivial-types).
327 |
328 | ### String
329 |
330 | A double quote character is a macro character which represents a string.
331 |
332 | ~~~lisp
333 | "Hello, World!"
334 | ;=> "Hello, World!"
335 | ~~~
336 |
337 | A backslash followed by some characters will be treated special.
338 |
339 | ~~~lisp
340 | "Hello\nWorld!"
341 | ;=> "Hello
342 | ; World!"
343 | ~~~
344 |
345 | #### String interpolation
346 |
347 | `#"` is similar to `"`, but it allows interpolation within strings.
348 |
349 | If `${...}` or `@{...}` is seen, it will be replaced by the result value of an expression (or the last expression) inside of it.
350 |
351 | ~~~lisp
352 | #"1 + 1 = ${(+ 1 1)}"
353 | ;=> "1 + 1 = 2"
354 | ~~~
355 |
356 | ### Hash table
357 |
358 | CL21 provides a notation for hash-tables.
359 |
360 | ~~~lisp
361 | #H(:name "Eitaro Fukamachi" :living "Japan")
362 | ;=> #H(:LIVING "Japan" :NAME "Eitaro Fukamachi")
363 | ~~~
364 |
365 | Note this always creates a hash-table whose test function is `EQUAL`. If you want to create it with another test function, a function `hash-table` is also available.
366 |
367 | ~~~lisp
368 | (hash-table 'eq :name "Eitaro Fukamachi")
369 | ;=> #H(:NAME "Eitaro Fukamachi")
370 | ; instead of
371 | ; (defvar *hash* (make-hash-table))
372 | ; (setf (gethash :name *hash*) "Eitaro Fukamachi")
373 | ~~~
374 |
375 | Accessing an element is also done with `getf` (instead of `gethash`):
376 |
377 | ~~~lisp
378 | (getf *hash* :name)
379 | ~~~
380 |
381 | Looping over a hash-table:
382 |
383 | ~~~lisp
384 | (doeach ((key val) *hash*)
385 | (when (< (length key) 2)
386 | (princ #"${x}\n")))
387 | ~~~
388 |
389 | Transform a hash-table into a plist:
390 |
391 | ~~~lisp
392 | (coerce *hash* 'plist)
393 | ~~~
394 |
395 | ### Vector
396 |
397 | `#(...)` is a notation for vectors. Unlike in Common Lisp, its elements will be evaluated and it creates an adjustable vector.
398 |
399 | ~~~lisp
400 | (let ((a 1)
401 | (b 2)
402 | (c 3))
403 | #(a b c))
404 | ;=> #(1 2 3)
405 | ~~~
406 |
407 | ~~~lisp
408 | (defvar *vec* #(0))
409 |
410 | (push 1 *vec*)
411 | ;=> #(1 0)
412 |
413 | (push-back 3 *vec*)
414 | ;=> #(1 0 3)
415 |
416 | (pop *vec*)
417 | ;=> 1
418 | ~~~
419 |
420 | ### Regular Expressions
421 |
422 | This new regexp reader macro uses the new
423 | ["Syntax"](https://github.com/cl21/cl21/wiki/Language-Difference-between-CL21-and-Common-Lisp#syntax)
424 | extension.
425 |
426 | ~~~lisp
427 | (use-package :cl21.re)
428 |
429 | (#/^(\d{4})-(\d{2})-(\d{2})$/ "2014-01-23")
430 |
431 | (re-replace #/a/ig "Eitaro Fukamachi" "α")
432 | ~~~
433 |
434 | ### Running external programs (cl21.process)
435 |
436 | With `run-process`:
437 |
438 | ~~~lisp
439 | (use-package :cl21.process)
440 |
441 | (run-process '("ls" "-l" "/Users"))
442 | ;-> total 0
443 | ; drwxrwxrwt 5 root wheel 170 Nov 1 18:00 Shared
444 | ; drwxr-xr-x+ 174 nitro_idiot staff 5916 Mar 5 21:41 nitro_idiot
445 | ;=> #
446 | ~~~
447 |
448 | or the #` reader macro:
449 |
450 | ~~~
451 | #`ls -l /Users`
452 | ;=> "total 0
453 | ; drwxrwxrwt 5 root wheel 170 Nov 1 18:00 Shared
454 | ; drwxr-xr-x+ 174 nitro_idiot staff 5916 Mar 5 21:41 nitro_idiot
455 | ; "
456 | ; ""
457 | ; 0
458 | ~~~
459 |
460 | ### Naming of constants
461 |
462 | All constant variables were renamed to the name added "+" signs before and after.
463 |
464 | ~~~lisp
465 | +pi+
466 | ;=> 3.141592653589793d0
467 |
468 | +array-rank-limit+
469 | ;=> 65529
470 | ~~~
471 |
472 | ### CL21 Standard Library
473 |
474 | CL21 Standard Library is a set of libraries that are distributed with CL21. It is intended to offer a wide range of facilities.
475 |
476 | We are working on increasing the number of it. Currently, the following packages are provided.
477 |
478 | * cl21.re
479 | * cl21.process
480 | * cl21.os
481 | * cl21.lazy
482 | * cl21.abbr
483 |
--------------------------------------------------------------------------------
/contributors.md:
--------------------------------------------------------------------------------
1 | # APPENDIX: Contributors
2 |
3 | Thank you to all contributors, as well as to the people reviewing pull requests whose name won't appear here.
4 |
5 | The contributors on Github are:
6 |
7 |
8 |
9 | * vindarel
10 | * Paul Nathan
11 | * nhabedi [^nhabedi]
12 | * Fernando Borretti
13 | * bill_clementson
14 | * chuchana
15 | * Ben Dudson
16 | * YUE Daian
17 | * Pierre Neidhardt
18 | * Rommel MARTINEZ
19 | * digikar99
20 | * nicklevine
21 | * Dmitry Petrov
22 | * otjura
23 | * skeptomai
24 | * alx-a
25 | * jgart
26 | * thegoofist
27 | * Francis St-Amour
28 | * Johan Widén
29 | * emres
30 | * jdcal
31 | * Boutade
32 | * airfoyle
33 | * contrapunctus
34 | * mvilleneuve
35 | * Alex Ponomarev
36 | * Alexander Artemenko
37 | * Johan Sjölén
38 | * Mariano Montone
39 | * albertoriva
40 | * Blue
41 | * Daniel Keogh
42 | * David Pflug
43 | * David Sun
44 | * Jason Legler
45 | * Jiho Sung
46 | * Kilian M. Haemmerle
47 | * Matteo Landi
48 | * Nikolaos Chatzikonstantinou
49 | * Nisar Ahmad
50 | * Nisen
51 | * Vityok
52 | * ctoid
53 | * ozten
54 | * reflektoin
55 | * Ahmad Edrisy
56 | * Alberto Ferreira
57 | * Amol Dosanjh
58 | * Andrew
59 | * Andrew Hill
60 | * André Alexandre Gomes
61 | * Ankit Chandawala
62 | * August Feng
63 | * B1nj0y
64 | * Bibek Panthi
65 | * Bo Yao
66 | * Brandon Hale
67 | * Burhanuddin Baharuddin
68 | * Coin Okay
69 | * Colin Woodbury
70 | * Daniel Uber
71 | * Eric Timmons
72 | * Giorgos Makris
73 | * HiPhish
74 | * Inc0n
75 | * John Zhang
76 | * Justin
77 | * Kevin Layer
78 | * Kevin Secretan
79 | * LdBeth
80 | * Matthew Kennedy
81 | * Momozor
82 | * NCM
83 | * Noor
84 | * Paul Donnelly
85 | * Pavel Kulyov
86 | * Phi-Long Nguyen
87 | * R Primus
88 | * Ralf Doering
89 | * Salad Tea
90 | * Victor Anyakin
91 | * alaskasquirrel
92 | * blackeuler
93 | * contrapunctus-1
94 | * convert-repo
95 | * dangerdyke
96 | * grobe0ba
97 | * jthing
98 | * mavis
99 | * mwgkgk
100 | * paul-donnelly
101 | * various-and-sundry
102 | * Štěpán Němec
103 |
104 | (this list is sorted by number of commits)
105 |
106 | And the contributors on the original SourceForge version are:
107 |
108 | * Marco Antoniotti
109 | * [Zach Beane](mailto:xach@xach.com)
110 | * Pierpaolo Bernardi
111 | * [Christopher Brown](mailto:skeptomai@mac.com)
112 | * [Frederic Brunel](mailto:brunel@mail.dotcom.fr)
113 | * [Jeff Caldwell](mailto:jdcal@yahoo.com)
114 | * [Bill Clementson](mailto:bill_clementson@yahoo.com)
115 | * Martin Cracauer
116 | * [Gerald Doussot](mailto:gdoussot@yahoo.com)
117 | * [Paul Foley](mailto:mycroft@actrix.gen.nz)
118 | * Jörg-Cyril Höhle
119 | * [Nick Levine](mailto:ndl@ravenbrook.com)
120 | * [Austin King](mailto:shout@ozten.com)
121 | * [Lieven Marchand](mailto:mal@wyrd.be)
122 | * [Drew McDermott](mailto:drew.mcdermott@yale.edu)
123 | * [Kalman Reti](mailto:reti@ai.mit.edu)
124 | * [Alberto Riva](mailto:alb@chip.org)
125 | * [Rudi Schlatte](mailto:rschlatte@ist.tu-graz.ac.at)
126 | * [Emre Sevinç](mailto:emres@bilgi.edu.tr)
127 | * Paul Tarvydas
128 | * Kenny Tilton
129 | * [Reini Urban](mailto:rurban@x-ray.at)
130 | * [Matthieu Villeneuve](mailto:matthieu@matthieu-villeneuve.net)
131 | * [Edi Weitz](mailto:edi@agharta.de)
132 |
133 | Finally, the credit for finally giving birth to the project probably goes to
134 | Edi Weitz who posted [this message][msg] to [comp.lang.lisp][cll].
135 |
136 | [cll]: news:comp.lang.lisp
137 | [msg]: http://groups.google.com/groups?selm=76be8851.0201222259.70ecbcb1%40posting.google.com
138 |
139 | [^nhabedi]: nhabedi is Edmund Weitz ;)
140 |
--------------------------------------------------------------------------------
/dandelion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LispCookbook/cl-cookbook/b5320251bb01d98b64d41c2b7d56fcc6efabfa8d/dandelion.png
--------------------------------------------------------------------------------
/drafts/defmodel.lisp.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: A "defmodel" macro
3 | ---
4 |
5 |
6 |
7 |
8 | ~~~lisp
9 | (defmacro defmodel (name slot-definitions)
10 | `(progn
11 | (defclass ,name ()
12 | ((id :col-type serial :reader ,(symb name 'id))
13 | ,@slot-definitions)
14 | (:metaclass dao-class)
15 | (:keys id))
16 | (with-connection (db-params)
17 | (unless (table-exists-p ',name)
18 | (execute (dao-table-definition ',name))))
19 | ;; Create
20 | (defmacro ,(symb name 'create) (&rest args)
21 | `(with-connection (db-params)
22 | (make-dao ',',name ,@args)))
23 | ;; Read
24 | (defun ,(symb name 'get-all) ()
25 | (with-connection (db-params)
26 | (select-dao ',name)))
27 | (defun ,(symb name 'get) (id)
28 | (with-connection (db-params)
29 | (get-dao ',name id)))
30 | (defmacro ,(symb name 'select) (sql-test &optional sort)
31 | `(with-connection (db-params)
32 | (select-dao ',',name ,sql-test ,sort)))
33 | ;; Update
34 | (defun ,(symb name 'update) (,name)
35 | (with-connection (db-params)
36 | (update-dao ,name)))
37 | ;; Delete
38 | (defun ,(symb name 'delete) (,name)
39 | (with-connection (db-params)
40 | (delete-dao ,name)))))
41 | ~~~
42 |
--------------------------------------------------------------------------------
/editor-support.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Editor support
3 | ---
4 |
5 | The editor of choice is still [Emacs](https://www.gnu.org/software/emacs/), but it is not the only one.
6 |
7 | ## Emacs
8 |
9 | [SLIME](https://github.com/slime/slime/) is the Superior Lisp
10 | Interaction Mode for Emacs. It has support for interacting with a
11 | running Common Lisp process for compilation, debugging, documentation
12 | lookup, cross-references, and so on. It works with many implementations.
13 |
14 | [Portacle](https://shinmera.github.io/portacle/) is a portable and
15 | **multi-platform** Common Lisp environment. It ships Emacs, SBCL,
16 | Quicklisp, SLIME and Git.
17 |
18 |
20 |
21 | [plain-common-lisp](https://github.com/pascalcombier/plain-common-lisp/)
22 | is a crafted, easy-to-install Common Lisp environment for
23 | **Windows**. It ships Emacs, SBCL, Slime, Quicklisp. It also shows how
24 | to display GUI windows with Win32, Tk, IUP, ftw and Opengl.
25 |
26 |
28 |
29 |
30 | ### Using Emacs as an IDE
31 |
32 | See ["Using Emacs as an IDE"](emacs-ide.html).
33 |
34 |
35 | ## Vim & Neovim
36 |
37 | [Slimv](https://github.com/kovisoft/slimv) is a full-blown
38 | environment for Common Lisp inside of Vim.
39 |
40 | [Vlime](https://github.com/vlime/vlime) is a Common Lisp dev
41 | environment for Vim (and Neovim), similar to SLIME for Emacs and SLIMV
42 | for Vim.
43 |
44 |
46 |
47 | [cl-neovim](https://github.com/adolenc/cl-neovim/) makes it possible to write
48 | Neovim plugins in Common Lisp.
49 |
50 | [quicklisp.nvim](https://gitlab.com/HiPhish/quicklisp.nvim) is a Neovim
51 | frontend for Quicklisp.
52 |
53 | [Slimv_box](https://github.com/justin2004/slimv_box) brings Vim, SBCL, ABCL,
54 | and tmux in a Docker container for a quick installation.
55 |
56 | See also:
57 |
58 | * [Lisp in Vim](https://susam.net/blog/lisp-in-vim.html) demonstrates usage and
59 | compares both Slimv and Vlime
60 |
61 | ## Pulsar (ex Atom)
62 |
63 | See [SLIMA](https://github.com/neil-lindquist/slima). This package
64 | allows you to interactively develop Common Lisp code, turning Atom, or
65 | now [Pulsar](https://github.com/pulsar-edit/pulsar), into a pretty
66 | good Lisp IDE. It features:
67 |
68 | * REPL
69 | * integrated debugger
70 | * (not a stepping debugger yet)
71 | * jump to definition
72 | * autocomplete suggestions based on your code
73 | * compile this function, compile this file
74 | * function arguments order
75 | * integrated profiler
76 | * interactive object inspection.
77 |
78 | It is based on the Swank backend, like Slime for Emacs.
79 |
80 |
82 |
83 | ## VSCode
84 |
85 | [Alive](https://marketplace.visualstudio.com/items?itemName=rheller.alive) makes
86 | VSCode a powerful Common Lisp development. It hooks directly into the Swank
87 | server that Emacs Slime uses and is fully compatible with VSCode's ability to
88 | develop remotely in containers, WSL, Remote machines, etc. It has no
89 | dependencies beyond a version of Common Lisp on which to run the Swank server.
90 | It can be configured to run with Quicklisp, CLPM, and Roswell. It currently
91 | supports:
92 |
93 | - Syntax highlighting
94 | - Code completion
95 | - Code formatter
96 | - Jump to definition
97 | - Snippets
98 | - REPL integration
99 | - Interactive Debugger
100 | - REPL history
101 | - Inline evaluation
102 | - Macro expand
103 | - Disassemble
104 | - Inspector
105 | - Hover Text
106 | - Rename function args and let bindings
107 | - Code folding
108 |
109 |
110 |
111 | [commonlisp-vscode
112 | extension](https://marketplace.visualstudio.com/items?itemName=ailisp.commonlisp-vscode)
113 | works via the [cl-lsp](https://github.com/ailisp/cl-lsp) language server and
114 | it's possible to write LSP client that works in other editors. It depends
115 | heavily on [Roswell](https://roswell.github.io/Home.html). It currently
116 | supports:
117 |
118 | - running a REPL
119 | - evaluate code
120 | - auto indent,
121 | - code completion
122 | - go to definition
123 | - documentation on hover
124 |
125 |
126 |
127 | ### Using VSCode with Alive
128 |
129 | See [Using VSCode with Alive](vscode-alive.html).
130 |
131 | ## JetBrains - NEW in Jan, 2023!
132 |
133 | [SLT](https://github.com/Enerccio/SLT) is a new (published on January,
134 | 2023) plugin for the suite of JetBrains' IDEs. It uses a modified SLIME/Swank
135 | protocol to commmunicate with SBCL, providing IDE capabilities for
136 | Common Lisp.
137 |
138 | It has a very good [user guide](https://github.com/Enerccio/SLT/wiki/User-Guide).
139 |
140 | At the time of writing, for its version 0.4, it supports:
141 |
142 | - REPL
143 | - symbol completion
144 | - send expressions to the REPL
145 | - interactive debugging, breakpoints
146 | - documentation display
147 | - cross-references
148 | - find symbol by name, global class/symbol search
149 | - inspector (read-only)
150 | - graphical threads list
151 | - SDK support, automatic download for Windows users
152 | - multiple implementations support: SBCL, CCL, ABCL and AllegroCL.
153 |
154 |
155 |
156 |
157 | ## Eclipse
158 |
159 | [Dandelion](https://github.com/Ragnaroek/dandelion) is a plugin for the
160 | Eclipse IDE.
161 |
162 | Available for Windows, Mac and Linux, built-in SBCL and CLISP support
163 | and possibility to connect other environments, interactive debugger
164 | with restarts, macro-expansion, parenthesis matching,…
165 |
166 |
167 |
168 | ## Lem
169 |
170 | [Lem](https://github.com/lem-project/lem/) is an editor tailored for Common Lisp development. Once you
171 | install it, you can start developing. Its interface resembles Emacs
172 | and SLIME (same shortcuts). It comes with an ncurses and an SDL2
173 | frontend, and other programming modes thanks to its built-in LSP client:
174 | Python, Go, Rust, JS, Nim, Scheme, HTML, CSS, plus a directory mode, a **vim layer**, and more.
175 |
176 |
178 |
179 | It can be started as a REPL right away in the terminal. Run it with:
180 |
181 | lem --eval "(lem-lisp-mode:start-lisp-repl t)"
182 |
183 | So you probably want a shell alias:
184 |
185 | alias ilem='lem --eval "(lem-lisp-mode:start-lisp-repl t)"'
186 |
187 | There is more:
188 |
189 | * 🚀 [Lem on the cloud](https://www.youtube.com/watch?v=IMN7feOQOak) (video presentation) Rooms is a product that runs Lem, a text editor created in Common Lisp, in the Cloud and can be used by multiple users.
190 | * Lem on the cloud is NEW as of April, 2024. In private beta at the time of writing.
191 |
192 |
193 |
194 | ## Sublime Text
195 |
196 | [Sublime Text](http://www.sublimetext.com/3) has now good support for
197 | Common Lisp.
198 |
199 | First install the "SublimeREPL" package and then see the options
200 | in Tools/SublimeREPL to choose your CL implementation.
201 |
202 | Then [Slyblime](https://github.com/s-clerc/slyblime) ships IDE-like
203 | features to interact with the running Lisp image. It is an
204 | implementation of SLY and it uses the same backend (SLYNK). It
205 | provides advanced features including a debugger with stack frame
206 | inspection.
207 |
208 |
210 |
211 | ## LispWorks (proprietary)
212 |
213 | [LispWorks](http://www.lispworks.com/) is a Common Lisp implementation that
214 | comes with its own Integrated Development Environment (IDE) and its share of
215 | unique features, such as the CAPI GUI toolkit. It is **proprietary** and
216 | provides a **free limited version**.
217 |
218 | You can [read our LispWorks review here](lispworks.html).
219 |
220 |
221 |
222 |
223 | ## Geany (experimental)
224 |
225 | [Geany-lisp](https://github.com/jasom/geany-lisp) is an experimental
226 | lisp mode for the [Geany](https://geany.org/) editor. It features completion of symbols,
227 | smart indenting, jump to definition, compilation of the current file and
228 | highlighting of errors and warnings, a REPL, and a project skeleton creator.
229 |
230 |
231 |
232 |
233 | ## Notebooks
234 |
235 | [common-lisp-jupyter](https://github.com/yitzchak/common-lisp-jupyter) is a Common Lisp
236 | kernel for Jupyter notebooks.
237 |
238 | You can [see a live Jupyter notebook written in Lisp here](https://nbviewer.jupyter.org/github/yitzchak/common-lisp-jupyter/blob/master/examples/about.ipynb). It is easy to install (Roswell, repo2docker and Docker recipes).
239 |
240 |
242 |
243 | There is also [Darkmatter](https://github.com/tamamu/darkmatter), a notebook-style
244 | Common Lisp environment, built in Common Lisp.
245 |
246 |
247 | ## REPLs
248 |
249 | [cl-repl](https://github.com/lisp-maintainers/cl-repl) is an ipython-like REPL. It supports symbol completion, magic and shell commands, multi-line editing, editing command in a file and a simple debugger.
250 |
251 | It is available as a binary.
252 |
253 | You might also like [sbcli](https://github.com/hellerve/sbcli), an even simpler REPL with readline capabilities. It handles errors gracefully instead of showing a debugger.
254 |
255 |
257 |
258 |
259 | ## Others
260 |
261 | There are some more editors out there, more or less discontinued, and
262 | free versions of other Lisp vendors, such as Allegro CL.
263 |
--------------------------------------------------------------------------------
/ffi.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Foreign Function Interfaces
3 | ---
4 |
5 | The ANSI Common Lisp standard doesn't mention this topic. So almost everything that can be said here depends on your OS and your implementation. However these days, we can use the [CFFI](https://github.com/cffi/cffi) library, a portable and easy-to-use C foreign function interface.
6 |
7 | > CFFI, the Common Foreign Function Interface, purports to be a portable FFI for Common Lisp. It abstracts away the differences between the API of the native FFI's of the various Common Lisp implementations.
8 |
9 | We'll see an example right now.
10 |
11 |
12 | ## CFFI: calling a C function from the `math.h` header file.
13 |
14 | Let's use `defcfun` to interface with the foreign [ceil](https://en.cppreference.com/w/c/numeric/math/ceil) C function from `math.h`.
15 |
16 | [defcfun](https://cffi.common-lisp.dev/manual/html_node/defcfun.html) is a macro in the cffi library that generates a function with the name you give it.
17 |
18 | ~~~lisp
19 | CL-USER> (cffi:defcfun ("ceil" c-ceil) :double (number :double))
20 | ~~~
21 |
22 | We say that the "ceil" C function will be called "c-ceil" on our Lisp side, it takes one argument that is a double float, and it returns a number that is also a double float.
23 |
24 | Here is the above function macroexpanded with `macrostep-expand`:
25 |
26 | ~~~lisp
27 | (progn
28 | nil
29 | (defun c-ceil (number)
30 | (let ((#:g312 number))
31 | (cffi-sys:%foreign-funcall "ceil" (:double #:g312 :double) :convention
32 | :cdecl :library :default))))
33 | ~~~
34 |
35 | The reason we called it `c-ceil` and not `ceil` is only for the example, so we know this is a wrapper around C. You can name it "ceil", since it doesn't designate a built-in Common Lisp function or macro.
36 |
37 | Now that we have a c-ceil function from `math.h`, let's use it! We must give it double float.
38 |
39 | ~~~lisp
40 | CL-USER> (c-ceil 5.4d0)
41 | 6.0d0
42 | ~~~
43 |
44 | As you can see, it works! The double gets rounded up to `6.0d0` as expected.
45 |
46 | Let's try another one! This time, we'll use [floor](https://en.cppreference.com/w/c/numeric/math/floor), and we couldn't name it "floor" because this Common Lisp function exists.
47 |
48 | ~~~lisp
49 | CL-USER> (cffi:defcfun ("floor" c-floor) :double (number :double))
50 | C-FLOOR
51 | CL-USER> (c-floor 5d0)
52 | 5.0d0
53 | CL-USER> (c-floor 5.4d0)
54 | 5.0d0
55 | ~~~
56 |
57 | Great!
58 |
59 | One more, let's try `sqrt` from math.h, still with double floats:
60 |
61 | ~~~lisp
62 | CL-USER> (cffi:defcfun ("sqrt" c-sqrt) :double (number :double))
63 | C-SQRT
64 | CL-USER> (c-sqrt 36.50d0)
65 | 6.041522986797286d0
66 | ~~~
67 |
68 | We can do arithmetic with our new `c-sqrt`:
69 |
70 | ~~~lisp
71 | CL-USER> (+ 2 (c-sqrt 3d0))
72 | 3.732050807568877d0
73 | ~~~
74 |
75 | We can even use our new shiny `c-sqrt` to map over a list of doubles and take the square root of all of them!
76 |
77 | ~~~lisp
78 | CL-USER> (mapcar #'c-sqrt '(3d0 4d0 5d0 6d0 7.5d0 12.75d0))
79 | (1.7320508075688772d0 2.0d0 2.23606797749979d0 2.449489742783178d0
80 | 2.7386127875258306d0 3.570714214271425d0)
81 | ~~~
82 |
--------------------------------------------------------------------------------
/fix-epub-links.sed:
--------------------------------------------------------------------------------
1 | # sed script to change internal links in markdown file
2 | # to links pandoc can use when generating EPUB from markdown.
3 | # Currently, the transformed links do not work in the web server,
4 | # so the script should change a file that is not used in the web server.
5 | # Examples of how links are transformed:
6 | # - In error_handling.md:
7 | # "[debugging section](debugging.html)" to "[debugging section][Debugging]"
8 | # It is expected that there is a section header with the title "Debugging".
9 | # - In data-structures.md:
10 | # "[strings](strings.html)" to "[strings](#strings)"
11 | # pandoc associates each section header with a unique key, here "strings".
12 | # We specify the target header represented by "key", using the syntax (#key).
13 | # This has to be used when the target syntax [section header] is ambiguous.
14 | # Usage:
15 | # sed -i -f fix-epub-links.sed full.md
16 | # Note:
17 | # The links with format like (#numbers), are there for a reason.
18 | # The (#foo) format is used when the [foo] format is ambiguous.
19 | #
20 | # arrays.md
21 | s/\(\[\(Arrays and vectors\)\]\)(data-structures.html)/\1[\2]/
22 | # databases.md
23 | s/\(\[CLOS\]\)(clos.html)/\1[Fundamentals of CLOS]/
24 | s/\(\[clos\]\)(clos.html)/\1[Fundamentals of CLOS]/
25 | # data-structures.md
26 | s/\(\[pattern matching\]\)(pattern_matching.html)/\1[Pattern Matching]/
27 | s/\(\[strings\]\)(strings.html)/\1(#strings)/
28 | s/\(\[CLOS section\]\)(clos.html)/\1[Fundamentals of CLOS]/
29 | # debugging.md
30 | s/\(\[error handling\]\)(error_handling.html)/\1[Error and exception handling]/
31 | s/\(\[testing\]\)(testing.html)/\1[Testing the code]/
32 | # dynamic-libraries.md
33 | s/\(\[\(foreign function interface\)\]\)(ffi.html)/\1[Foreign Function Interfaces]/
34 | # editor-support.md
35 | s/\(\["\(Using Emacs as an IDE\)"\]\)(emacs-ide.html)/\1[\2]/
36 | s/\(\[\(Using VSCode with Alive\)\]\)(vscode-alive.html)/\1[\2]/
37 | s/\(\[read our LispWorks review here\]\)(lispworks.html)/\1[LispWorks review]/
38 | # error_handling.md
39 | s/\(\[debugging section\]\)(debugging.html)/\1[Debugging]/
40 | # getting-started.md
41 | s/\(\[editor-support\]\)(editor-support.html)/\1[Editor support]/
42 | # index.md
43 | s/\(\[\(License\)\]\)(license.html)/\1[\2]/
44 | s/\(\[Getting started\]\)(getting-started.html)/\1[Getting started with Common Lisp]/
45 | s/\(\[\(Editor support\)\]\)(editor-support.html)/\1[\2]/
46 | s/\(\[\(Using Emacs as an IDE\)\]\)(emacs-ide.html)/\1[\2]/
47 | s/\(\[The LispWorks IDE\]\)(lispworks.html)/\1[LispWorks review]/
48 | s/\(\[Functions\]\)(functions.html)/\1(#functions)/
49 | s/\(\[Data Structures\]\)(data-structures.html)/\1[Data structures]/
50 | s/\(\[Strings\]\)(strings.html)/\1(#strings)/
51 | s/\(\[\(Regular Expressions\)\]\)(regexp.html)/\1[\2]/
52 | s/\(\[Numbers\]\)(numbers.html)/\1(#numbers)/
53 | s/\(\[Loops, iteration, mapping\]\)(iteration.html)/\1[Loop, iteration, mapping]/
54 | s/\(\[Multidimensional Arrays\]\)(arrays.html)/\1[Multidimensional arrays]/
55 | s/\(\[\(Dates and Times\)\]\)(dates_and_times.html)/\1[\2]/
56 | s/\(\[\(Pattern Matching\)\]\)(pattern_matching.html)/\1[\2]/
57 | s/\(\[\(Input\/Output\)\]\)(io.html)/\1[\2]/
58 | s/\(\[\(Files and Directories\)\]\)(files.html)/\1[\2]/
59 | s/\(\[Error and condition handling\]\)(error_handling.html)/\1[Error and exception handling]/
60 | s/\(\[\(Packages\)\]\)(packages.html)/\1[\2]/
61 | s/\(\[Macros and Backquote\]\)(macros.html)/\1[Macros]/
62 | s/\(\[CLOS (the Common Lisp Object System)\]\)(clos.html)/\1[Fundamentals of CLOS]/
63 | s/\(\[\(Type System\)\]\)(type.html)/\1[\2]/
64 | s/\(\[Sockets\]\)(sockets.html)/\1[TCP\/UDP programming with sockets]/
65 | s/\(\[\(Interfacing with your OS\)\]\)(os.html)/\1[\2]/
66 | s/\(\[\(Foreign Function Interfaces\)\]\)(ffi.html)/\1[\2]/
67 | s/\(\[\(Building Dynamic Libraries\)\]\)(dynamic-libraries.html)/\1[\2]/
68 | s/\(\[\(Threads\)\]\)(process.html)/\1[\2]/
69 | s/\(\[\(Defining Systems\)\]\)(systems.html)/\1[\2]/
70 | s/\(\[\(Using the Win32 API\)\]\)(win32.html)/\1[\2]/
71 | s/\(\[\(Debugging\)\]\)(debugging.html)/\1[\2]/
72 | s/\(\[Performance Tuning\]\)(performance.html)/\1[Performance Tuning and Tips]/
73 | s/\(\[Scripting. Building executables\]\)(scripting.html)/\1[Scripting. Command line arguments. Executables.]/
74 | s/\(\[Testing and Continuous Integration\]\)(testing.html)/\1[Testing the code]/
75 | s/\(\[Databases\]\)(databases.html)/\1[Database Access and Persistence]/
76 | s/\(\[GUI programming\]\)(gui.html)/\1[GUI toolkits]/
77 | s/\(\[\(Web development\)\]\)(web.html)/\1[\2]/
78 | s/\(\[\(Web Scraping\)\]\)(web-scraping.html)/\1[\2]/
79 | s/\(\[\(WebSockets\)\]\)(websockets.html)/\1[\2]/
80 | s/\(\[\(Miscellaneous\)\]\)(misc.html)/\1[\2]/
81 | # iteration.md
82 | s/\(\[data-structures chapter\]\)(data-structures.html)/\1[Data structures]/
83 | # scripting.md
84 | s/\(\[error and condition handling\]\)(error_handling.html)/\1[Error and exception handling]/
85 | # strings.md
86 | s/\(\[regexp\]\)(regexp.html)/\1[Regular Expressions]/
87 | # web.md
88 | s/\(\[databases section\]\)(databases.html)/\1[Database Access and Persistence]/
89 |
--------------------------------------------------------------------------------
/foreword.md:
--------------------------------------------------------------------------------
1 | # Foreword
2 |
3 |
4 |
5 | > Cookbook, n.
6 | > a book containing recipes and other information about the preparation and cooking of food.
7 |
8 | The Common Lisp Cookbook is a collaborative resource to help you learn
9 | Common Lisp the language, its ecosystem and to get you started in a
10 | wide range of programming areas. It can be used by Lisp newcomers as a
11 | tutorial (getting started, functions, etc) and by everybody as a
12 | reference (loop!).
13 |
14 | We hope that these EPUB and PDF versions make the learning experience
15 | even more practical and enjoyable.
16 |
17 |
18 | > Vincent "vindarel" Dardel, for the Cookbook contributors
19 |
--------------------------------------------------------------------------------
/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: The Common Lisp Cookbook
3 | ---
4 |
5 | > Cookbook, n.
6 | > a book containing recipes and other information about the preparation and cooking of food.
7 |
8 | A Cookbook is an invaluable resource, as it shows how to do various things in a clear fashion without all the theoretical context. Sometimes you just need to look things up. While cookbooks can never replace proper documentation such as the HyperSpec or books such as Practical Common Lisp, every language deserves a good cookbook, Common Lisp included.
9 |
10 | The CL Cookbook aims to tackle all sort of topics, for the beginner and for the more advanced developer.
11 |
12 |
13 | # Content
14 |
15 | ### Getting started
16 |
17 | * [License](license.html)
18 | * [Getting started](getting-started.html)
19 | * How to install a Common Lisp implementation
20 | * How to start a Lisp REPL
21 | * How to install third-party libraries with Quicklisp
22 | * How to work with projects
23 | * [Editor support](editor-support.html)
24 | * [Using Emacs as an IDE](emacs-ide.html)
25 | * [The LispWorks IDE](lispworks.html)
26 | * [Using VSCode with Alive](vscode-alive.html)
27 |
28 | ### Language basics
29 |
30 |
31 |
32 | * [Functions](functions.html)
33 | * [Data Structures](data-structures.html)
34 | * [Strings](strings.html)
35 | + [format](strings.html#string-formatting-format)
36 | * [Regular Expressions](regexp.html)
37 | * [Numbers](numbers.html)
38 | * [Equality](equality.html)
39 | * [Loops, iteration, mapping](iteration.html)
40 | * [Multidimensional Arrays](arrays.html)
41 | * [Dates and Times](dates_and_times.html)
42 | * [Pattern Matching](pattern_matching.html)
43 | * [Input/Output](io.html)
44 | * [Files and Directories](files.html)
45 | * [CLOS (the Common Lisp Object System)](clos.html)
46 |
47 | ### Advanced topics
48 |
49 |
50 |
51 | * [Packages](packages.html)
52 | * [Defining Systems](systems.html)
53 | * [Error and condition handling](error_handling.html)
54 | * [Debugging](debugging.html)
55 | * [Macros and Backquote](macros.html)
56 | * [Type System](type.html)
57 | * [Concurrency and Parallelism](process.html)
58 | * [Performance Tuning](performance.html)
59 | * [Testing and Continuous Integration](testing.html)
60 | * [Scripting. Building executables](scripting.html)
61 |
62 | * [Miscellaneous](misc.html)
63 |
64 |
65 | ### Outside world
66 |
67 |
68 |
69 | * [Interfacing with your OS](os.html)
70 | * [Databases](databases.html)
71 | * [Foreign Function Interfaces](ffi.html)
72 | * [Building Dynamic Libraries](dynamic-libraries.html)
73 | * [GUI programming](gui.html)
74 | * [Sockets](sockets.html)
75 | * [WebSockets](websockets.html)
76 | * [Web development](web.html)
77 | * [Web Scraping](web-scraping.html)
78 |
79 | * [Using the Win32 API](win32.html)
80 |
81 |
82 |
83 | ## Download in EPUB
84 |
85 | The Cookbook is also available in EPUB (and PDF) format.
86 |
87 | You can [download it directly in EPUB](https://github.com/LispCookbook/cl-cookbook/releases/download/2023-12-13/common-lisp-cookbook.epub) and [PDF](https://github.com/LispCookbook/cl-cookbook/releases/download/2023-12-13/common-lisp-cookbook.pdf), and you can **pay what you want** to further support its development:
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | Donate and download the EPUB version
96 |
97 |
98 |
99 |
100 |
101 |
102 | Thank you!
103 |
104 |
105 | ## Translations
106 |
107 | The Cookbook has been translated to:
108 |
109 | * [Chinese simplified](https://oneforalone.github.io/cl-cookbook-cn/#/) ([Github](https://github.com/oneforalone/cl-cookbook-cn))
110 | * [Portuguese (Brazilian)](https://lisp.com.br/cl-cookbook/) ([Github](https://github.com/commonlispbr/cl-cookbook))
111 |
112 | # Other CL Resources
113 |
114 |
115 |
116 | * [lisp-lang.org](http://lisp-lang.org/): success stories, tutorials and style guide
117 | * [Awesome-cl](https://github.com/CodyReichert/awesome-cl), a curated list of libraries
118 | * [List of Lisp Communities](https://github.com/CodyReichert/awesome-cl#community)
119 | * [Lisp Koans](https://github.com/google/lisp-koans/) - a language learning exercise, which guides the learner progressively through many language features.
120 | * [Learn X in Y minutes - Where X = Common Lisp](https://learnxinyminutes.com/docs/common-lisp/) - Small Common Lisp tutorial covering the essentials.
121 | * [Common Lisp Libraries Read the Docs](https://common-lisp-libraries.readthedocs.io/) - the documentation of popular libraries ported to the modern and good looking Read The Docs style.
122 | * [lisp-tips](https://github.com/lisp-tips/lisp-tips/issues/)
123 | * [Common Lisp and CLOG tutorial series](https://github.com/rabbibotton/clog/blob/main/LEARN.md), a tutorial for Common Lisp and CLOG, a GUI-like library for Common Lisp based on web technologies.
124 | * [Lisp and Elements of Style](http://web.archive.org/web/20190316190256/https://www.nicklevine.org/declarative/lectures/) by Nick Levine
125 | * Pascal Costanza's [Highly Opinionated Guide to Lisp](http://www.p-cos.net/lisp/guide.html)
126 | * [Cliki](http://www.cliki.net/), Common Lisp's wiki
127 | * 📹 [Common Lisp programming: from novice to effective developer](https://www.udemy.com/course/common-lisp-programming/?referralCode=2F3D698BBC4326F94358), a video course on the Udemy platform (paywall), by one of the main Cookbook contributor. *"Thanks for supporting my work on Udemy. You can ask me for a free coupon if you are a student."* vindarel
128 |
129 | and also: [Common Lisp Pitfalls](https://github.com/LispCookbook/cl-cookbook/issues/479) by Jeff Dalton.
130 |
131 |
132 |
133 | Books
134 |
135 | * [Practical Common Lisp](http://www.gigamonkeys.com/book/) by Peter Seibel
136 | * [Common Lisp Recipes](http://weitz.de/cl-recipes/) by Edmund Weitz, published in 2016,
137 | * [Common Lisp: A Gentle Introduction to Symbolic Computation](http://www-2.cs.cmu.edu/~dst/LispBook/) by David S. Touretzky
138 | * [Successful Lisp: How to Understand and Use Common Lisp](https://successful-lisp.blogspot.com/p/httpsdrive.html) by David B. Lamkins
139 | * [On Lisp](http://www.paulgraham.com/onlisptext.html) by Paul Graham
140 | * [Common Lisp the Language, 2nd Edition](http://www-2.cs.cmu.edu/Groups/AI/html/cltl/cltl2.html) by Guy L. Steele
141 | * [CLtL2, in PDF format](https://github.com/mmontone/cltl2-doc)
142 | * [A Tutorial on Good Lisp Style](https://www.cs.umd.edu/%7Enau/cmsc421/norvig-lisp-style.pdf) by Peter Norvig and Kent Pitman
143 |
144 | Advanced books
145 |
146 | * [Loving Lisp - the Savy Programmer's Secret Weapon](https://leanpub.com/lovinglisp/) by Mark Watson
147 | * [Programming Algorithms](https://leanpub.com/progalgs) - A comprehensive guide to writing efficient programs with examples in Lisp.
148 |
149 |
150 | Specifications
151 |
152 | * [The Common Lisp HyperSpec](http://www.lispworks.com/documentation/HyperSpec/Front/index.htm) by Kent M. Pitman (also available in [Dash](https://kapeli.com/dash), [Zeal](https://zealdocs.org/) and [Velocity](https://velocity.silverlakesoftware.com/))
153 | * [The Common Lisp Community Spec](https://cl-community-spec.github.io/pages/index.html) - a new rendering produced from the ANSI specification draft, that everyone has the right to edit.
154 |
155 | # Further remarks
156 |
157 | This is a collaborative project that aims to provide for Common Lisp something
158 | similar to the [Perl Cookbook][perl] published by O'Reilly. More details about
159 | what it is and what it isn't can be found in this [thread][thread] from
160 | [comp.lang.lisp][cll].
161 |
162 | If you want to contribute to the CL Cookbook, please send a pull request in or
163 | file a ticket!
164 |
165 | Yes, we're talking to you! We need contributors - write a chapter that's missing
166 | and add it, find an open question and provide an answer, find bugs and report
167 | them, (If you have no idea what might be missing but would like to help, take a
168 | look at the [table of contents][toc] of the Perl Cookbook.) Don't worry about
169 | the formatting, just send plain text if you like - we'll take care about that
170 | later.
171 |
172 | Thanks in advance for your help!
173 |
174 | The pages here on Github are kept up to date. You can also download a
175 | [up to date zip file][zip] for offline browsing. More info can be found at the
176 | [Github project page][gh].
177 |
178 |
179 |
180 |
"
106 | […]
107 | ~~~
108 |
109 | And to see their *textual* content (the user-visible text inside the
110 | html), we can use `(text)` instead:
111 |
112 | ~~~lisp
113 | (lquery:$ *parsed-content* "#content" (text))
114 | #("License" "Editor support" "Strings" "Dates and Times" "Hash Tables"
115 | "Pattern Matching / Regular Expressions" "Functions" "Loop" "Input/Output"
116 | "Files and Directories" "Packages" "Macros and Backquote"
117 | "CLOS (the Common Lisp Object System)" "Sockets" "Interfacing with your OS"
118 | "Foreign Function Interfaces" "Threads" "Defining Systems"
119 | […]
120 | "Pascal Costanza’s Highly Opinionated Guide to Lisp"
121 | "Loving Lisp - the Savy Programmer’s Secret Weapon by Mark Watson"
122 | "FranzInc, a company selling Common Lisp and Graph Database solutions.")
123 | ~~~
124 |
125 | All right, so we see we are manipulating what we want. Now to get their
126 | `href`, a quick look at lquery's doc and we'll use `(attr
127 | "some-name")`:
128 |
129 |
130 | ~~~lisp
131 | (lquery:$ *parsed-content* "#content li a" (attr :href))
132 | ;; => #("license.html" "editor-support.html" "strings.html" "dates_and_times.html"
133 | ;; "hashes.html" "pattern_matching.html" "functions.html" "loop.html" "io.html"
134 | ;; "files.html" "packages.html" "macros.html"
135 | ;; "/cl-cookbook/clos-tutorial/index.html" "os.html" "ffi.html"
136 | ;; "process.html" "systems.html" "win32.html" "testing.html" "misc.html"
137 | ;; […]
138 | ;; "http://www.nicklevine.org/declarative/lectures/"
139 | ;; "http://www.p-cos.net/lisp/guide.html" "https://leanpub.com/lovinglisp/"
140 | ;; "https://franz.com/")
141 | ~~~
142 |
143 | *Note*: using `(serialize)` after `attr` leads to an error.
144 |
145 | Nice, we now have the list (well, a vector) of links of the
146 | page. We'll now write an async program to check and validate they are
147 | reachable.
148 |
149 | External resources:
150 |
151 | - [CSS selectors](https://developer.mozilla.org/en-US/docs/Glossary/CSS_Selector)
152 |
153 | ## Async requests
154 |
155 | In this example we'll take the list of url from above and we'll check
156 | if they are reachable. We want to do this asynchronously, but to see
157 | the benefits we'll first do it synchronously !
158 |
159 | We need a bit of filtering first to exclude the email addresses (maybe
160 | that was doable in the CSS selector ?).
161 |
162 | We put the vector of urls in a variable:
163 |
164 | ~~~lisp
165 | (defvar *urls* (lquery:$ *parsed-content* "#content li a" (attr :href)))
166 | ~~~
167 |
168 | We remove the elements that start with "mailto:": (a quick look at the
169 | [strings](strings.html) page will help)
170 |
171 | ~~~lisp
172 | (remove-if (lambda (it)
173 | (string= it "mailto:" :start1 0
174 | :end1 (length "mailto:")))
175 | *urls*)
176 | ;; => #("license.html" "editor-support.html" "strings.html" "dates_and_times.html"
177 | ;; […]
178 | ;; "process.html" "systems.html" "win32.html" "testing.html" "misc.html"
179 | ;; "license.html" "http://lisp-lang.org/"
180 | ;; "https://github.com/CodyReichert/awesome-cl"
181 | ;; "http://www.lispworks.com/documentation/HyperSpec/Front/index.htm"
182 | ;; […]
183 | ;; "https://franz.com/")
184 | ~~~
185 |
186 | Actually before writing the `remove-if` (which works on any sequence,
187 | including vectors) I tested with a `(map 'vector …)` to see that the
188 | results where indeed `nil` or `t`.
189 |
190 | As a side note, there is a handy `starts-with-p` function in the "str" library
191 | available in Quicklisp. So we could do:
192 |
193 | ~~~lisp
194 | (map 'vector (lambda (it)
195 | (str:starts-with-p "mailto:" it))
196 | *urls*)
197 | ~~~
198 |
199 | While we're at it, we'll only consider links starting with "http", in
200 | order not to write too much stuff irrelevant to web scraping:
201 |
202 | ~~~lisp
203 | (remove-if-not (lambda (it)
204 | (string= it "http" :start1 0 :end1 (length "http")))
205 | *)
206 | ~~~
207 |
208 | All right, we put this result in another variable:
209 |
210 | ~~~lisp
211 | (defvar *filtered-urls* *)
212 | ~~~
213 |
214 | and now to the real work. For every url, we want to request it and
215 | check that its return code is 200. We have to ignore certain
216 | errors. Indeed, a request can timeout, be redirected (we don't want
217 | that) or return an error code.
218 |
219 | To be in real conditions we'll add a link that times out in our list:
220 |
221 | ~~~lisp
222 | (setf (aref *filtered-urls* 0) "http://lisp.org") ;; :/
223 | ~~~
224 |
225 | We'll take the simple approach to ignore errors and return `nil` in
226 | that case. If all goes well, we return the return code, that should be
227 | 200.
228 |
229 | As we saw at the beginning, `dex:get` returns many values, including
230 | the return code. We'll access only this one with `nth-value` (instead
231 | of all of them with `multiple-value-bind`) and we'll use
232 | `ignore-errors`, that returns nil in case of an error. We could also
233 | use `handler-case` and handle specific error types (see examples in
234 | dexador's documentation).
235 |
236 | (*ignore-errors has the caveat that when there's an error, we can not
237 | return the element it comes from. We'll get to our ends though.*)
238 |
239 |
240 | ~~~lisp
241 | (map 'vector (lambda (it)
242 | (ignore-errors
243 | (nth-value 1 (dex:get it))))
244 | *filtered-urls*)
245 | ~~~
246 |
247 | we get:
248 |
249 | ```
250 | #(NIL 200 200 200 200 200 200 200 200 200 200 NIL 200 200 200 200 200 200 200
251 | 200 200 200 200)
252 | ```
253 |
254 | it works, but *it took a very long time*. How much time precisely ?
255 | with `(time …)`:
256 |
257 | ```
258 | Evaluation took:
259 | 21.554 seconds of real time
260 | 0.188000 seconds of total run time (0.172000 user, 0.016000 system)
261 | 0.87% CPU
262 | 55,912,081,589 processor cycles
263 | 9,279,664 bytes consed
264 | ```
265 |
266 | 21 seconds ! Obviously this synchronous method isn't efficient. We
267 | wait 10 seconds for links that time out. It's time to write and
268 | measure an async version.
269 |
270 | After installing `lparallel` and looking at
271 | [its documentation](https://lparallel.org/), we see that the parallel
272 | map [pmap](https://lparallel.org/pmap-family/) seems to be what we
273 | want. And it's only a one word edit. Let's try:
274 |
275 | ~~~lisp
276 | (time (lparallel:pmap 'vector
277 | (lambda (it)
278 | (ignore-errors
279 | (let ((status (nth-value 1 (dex:get it)))) status)))
280 | *filtered-urls*)
281 | ;; Evaluation took:
282 | ;; 11.584 seconds of real time
283 | ;; 0.156000 seconds of total run time (0.136000 user, 0.020000 system)
284 | ;; 1.35% CPU
285 | ;; 30,050,475,879 processor cycles
286 | ;; 7,241,616 bytes consed
287 | ;;
288 | ;;#(NIL 200 200 200 200 200 200 200 200 200 200 NIL 200 200 200 200 200 200 200
289 | ;; 200 200 200 200)
290 | ~~~
291 |
292 | Bingo. It still takes more than 10 seconds because we wait 10 seconds
293 | for one request that times out. But otherwise it proceeds all the http
294 | requests in parallel and so it is much faster.
295 |
296 | Shall we get the urls that aren't reachable, remove them from our list
297 | and measure the execution time in the sync and async cases ?
298 |
299 | What we do is: instead of returning only the return code, we check it
300 | is valid and we return the url:
301 |
302 | ~~~lisp
303 | ... (if (and status (= 200 status)) it) ...
304 | (defvar *valid-urls* *)
305 | ~~~
306 |
307 | we get a vector of urls with a couple of `nil`s: indeed, I thought I
308 | would have only one unreachable url but I discovered another
309 | one. Hopefully I have pushed a fix before you try this tutorial.
310 |
311 | But what are they ? We saw the status codes but not the urls :S We
312 | have a vector with all the urls and another with the valid ones. We'll
313 | simply treat them as sets and compute their difference. This will show
314 | us the bad ones. We must transform our vectors to lists for that.
315 |
316 | ~~~lisp
317 | (set-difference (coerce *filtered-urls* 'list)
318 | (coerce *valid-urls* 'list))
319 | ;; => ("http://lisp-lang.org/" "http://www.psg.com/~dlamkins/sl/cover.html")
320 | ~~~
321 |
322 | Gotcha !
323 |
324 | BTW it takes 8.280 seconds of real time to me to check the list of
325 | valid urls synchronously, and 2.857 seconds async.
326 |
327 | Have fun doing web scraping in CL !
328 |
329 |
330 | More helpful libraries:
331 |
332 | - we could use [VCR](https://github.com/tsikov/vcr), a store and
333 | replay utility to set up repeatable tests or to speed up a bit our
334 | experiments in the REPL.
335 | - [cl-async](https://github.com/orthecreedence/cl-async),
336 | [carrier](https://github.com/orthecreedence/carrier) and others
337 | network, parallelism and concurrency libraries to see on the
338 | [awesome-cl](https://github.com/CodyReichert/awesome-cl) list,
339 | [Cliki](http://www.cliki.net/) or
340 | [Quickdocs](https://quickdocs.org/-/search?q=web).
341 |
--------------------------------------------------------------------------------
/websockets.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: WebSockets
3 | ---
4 |
5 | The Common Lisp ecosystem boasts a few approaches to building WebSocket servers.
6 | First, there is the excellent
7 | [Hunchensocket](https://github.com/joaotavora/hunchensocket) that is written as
8 | an extension to [Hunchentoot](https://edicl.github.io/hunchentoot/), the classic
9 | web server for Common Lisp. I have used both and I find them to be wonderful.
10 |
11 | Today, however, you will be using the equally excellent
12 | [websocket-driver](https://github.com/fukamachi/websocket-driver) to build a WebSocket server with
13 | [Clack](https://github.com/fukamachi/clack). The Common Lisp web development community has expressed a
14 | slight preference for the Clack ecosystem because Clack provides a uniform interface to
15 | a variety of backends, including Hunchentoot. That is, with Clack, you can pick and choose the
16 | backend you prefer.
17 |
18 | In what follows, you will build a simple chat server and connect to it from a
19 | web browser. The tutorial is written so that you can enter the code into your
20 | REPL as you go, but in case you miss something, the full code listing can be found at the end.
21 |
22 | As a first step, you should load the needed libraries via quicklisp:
23 |
24 | ~~~lisp
25 |
26 | (ql:quickload '(clack websocket-driver alexandria))
27 |
28 | ~~~
29 |
30 |
31 | ## The websocket-driver Concept
32 |
33 | In websocket-driver, a WebSocket connection is an instance of the `ws` class,
34 | which exposes an event-driven API. You register event handlers by passing your
35 | WebSocket instance as the second argument to a method called `on`. For example,
36 | calling `(on :message my-websocket #'some-message-handler)` would invoke
37 | `some-message-handler` whenever a new message arrives.
38 |
39 | The `websocket-driver` API provides handlers for the following events:
40 |
41 | - `:open`: When a connection is opened. Expects a handler with zero arguments.
42 | - `:message` When a message arrives. Expects a handler with one argument, the message received.
43 | - `:close` When a connection closes. Expects a handler with two keyword args, a
44 | "code" and a "reason" for the dropped connection.
45 | - `:error` When some kind of protocol level error occurs. Expects a handler with
46 | one argument, the error message.
47 |
48 | For the purposes of your chat server, you will want to handle three cases: when
49 | a new user arrives to the channel, when a user sends a message to the channel,
50 | and when a user leaves.
51 |
52 | ## Defining Handlers for Chat Server Logic
53 |
54 | In this section you will define the functions that your event handlers will
55 | eventually call. These are helper functions that manage the chat server logic.
56 | You will define the WebSocket server in the next section.
57 |
58 | First, when a user connects to the server, you need to give that user a nickname
59 | so that other users know whose chats belong to whom. You will also need a data
60 | structure to map individual WebSocket connections to nicknames:
61 |
62 | ~~~lisp
63 |
64 | ;; make a hash table to map connections to nicknames
65 | (defvar *connections* (make-hash-table))
66 |
67 | ;; and assign a random nickname to a user upon connection
68 | (defun handle-new-connection (con)
69 | (setf (gethash con *connections*)
70 | (format nil "user-~a" (random 100000))))
71 |
72 | ~~~
73 |
74 | Next, when a user sends a chat to the room, the rest of the room should be
75 | notified. The message that the server receives is prepended with the nickname of
76 | the user who sent it.
77 |
78 | ~~~lisp
79 |
80 | (defun broadcast-to-room (connection message)
81 | (let ((message (format nil "~a: ~a"
82 | (gethash connection *connections*)
83 | message)))
84 | (loop :for con :being :the :hash-key :of *connections* :do
85 | (websocket-driver:send con message))))
86 | ~~~
87 |
88 | Finally, when a user leaves the channel, by closing the browser tab or
89 | navigating away, the room should be notified of that change, and the user's
90 | connection should be dropped from the `*connections*` table.
91 |
92 | ~~~lisp
93 | (defun handle-close-connection (connection)
94 | (let ((message (format nil " .... ~a has left."
95 | (gethash connection *connections*))))
96 | (remhash connection *connections*)
97 | (loop :for con :being :the :hash-key :of *connections* :do
98 | (websocket-driver:send con message))))
99 | ~~~
100 |
101 | ## Defining A Server
102 |
103 | Using Clack, a server is started by passing a function to `clack:clackup`. You
104 | will define a function called `chat-server` that you will start by
105 | calling `(clack:clackup #'chat-server :port 12345)`.
106 |
107 | A Clack server function accepts a single plist as its argument. That plist
108 | contains environment information about a request and is provided by the system.
109 | Your chat server will not make use of that environment, but if you want to learn
110 | more you can check out Clack's documentation.
111 |
112 | When a browser connects to your server, a websocket will be instantiated and
113 | handlers will be defined on it for each of the the events you want to support.
114 | A WebSocket "handshake" will then be sent back to the browser, indicating
115 | that the connection has been made. Here's how it works:
116 |
117 | ~~~lisp
118 | (defun chat-server (env)
119 | (let ((ws (websocket-driver:make-server env)))
120 |
121 | (websocket-driver:on :open ws
122 | (lambda () (handle-new-connection ws)))
123 |
124 | (websocket-driver:on :message ws
125 | (lambda (msg)
126 | (broadcast-to-room ws msg)))
127 |
128 | (websocket-driver:on :close ws
129 | (lambda (&key code reason)
130 | (declare (ignore code reason))
131 | (handle-close-connection ws)))
132 |
133 | (lambda (responder)
134 | (declare (ignore responder))
135 | ;; Send the handshake:
136 | (websocket-driver:start-connection ws))))
137 | ~~~
138 |
139 | You may now start your server, running on port `12345`:
140 |
141 | ~~~lisp
142 | ;; Keep the handler around so that
143 | ;; you can stop your server later on:
144 | (defvar *chat-handler* (clack:clackup #'chat-server :port 12345))
145 | ~~~
146 |
147 |
148 | ## A Quick HTML Chat Client
149 |
150 | So now you need a way to talk to your server. Using Clack, define a simple
151 | application that serves a web page to display and send chats. First the web page:
152 |
153 | ~~~lisp
154 |
155 | (defvar *html*
156 | "
157 |
158 |
159 |
160 |
161 | LISP-CHAT
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
192 |
193 |
194 | ")
195 |
196 |
197 | (defun client-server (env)
198 | (declare (ignore env))
199 | `(200 (:content-type "text/html")
200 | (,*html*)))
201 |
202 | ~~~
203 |
204 | You might prefer to put the HTML into a file, as escaping quotes is kind of annoying.
205 | Keeping the page data in a `defvar` was simpler for the purposes of this
206 | tutorial.
207 |
208 | You can see that the `client-server` function just serves the HTML content. Go
209 | ahead and start it, this time on port `8080`:
210 |
211 | ~~~lisp
212 | (defvar *client-handler* (clack:clackup #'client-server :port 8080))
213 | ~~~
214 |
215 | ## Check it out!
216 |
217 | Now open up two browser tabs and point them to `http://localhost:8080` and you
218 | should see your chat app!
219 |
220 |
222 |
223 | ## All The Code
224 |
225 | ~~~lisp
226 | (ql:quickload '(clack websocket-driver alexandria))
227 |
228 | (defvar *connections* (make-hash-table))
229 |
230 | (defun handle-new-connection (con)
231 | (setf (gethash con *connections*)
232 | (format nil "user-~a" (random 100000))))
233 |
234 | (defun broadcast-to-room (connection message)
235 | (let ((message (format nil "~a: ~a"
236 | (gethash connection *connections*)
237 | message)))
238 | (loop :for con :being :the :hash-key :of *connections* :do
239 | (websocket-driver:send con message))))
240 |
241 | (defun handle-close-connection (connection)
242 | (let ((message (format nil " .... ~a has left."
243 | (gethash connection *connections*))))
244 | (remhash connection *connections*)
245 | (loop :for con :being :the :hash-key :of *connections* :do
246 | (websocket-driver:send con message))))
247 |
248 | (defun chat-server (env)
249 | (let ((ws (websocket-driver:make-server env)))
250 | (websocket-driver:on :open ws
251 | (lambda () (handle-new-connection ws)))
252 |
253 | (websocket-driver:on :message ws
254 | (lambda (msg)
255 | (broadcast-to-room ws msg)))
256 |
257 | (websocket-driver:on :close ws
258 | (lambda (&key code reason)
259 | (declare (ignore code reason))
260 | (handle-close-connection ws)))
261 | (lambda (responder)
262 | (declare (ignore responder))
263 | (websocket-driver:start-connection ws))))
264 |
265 | (defvar *html*
266 | "
267 |
268 |
269 |
270 |
271 | LISP-CHAT
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
302 |
303 |
304 | ")
305 |
306 | (defun client-server (env)
307 | (declare (ignore env))
308 | `(200 (:content-type "text/html")
309 | (,*html*)))
310 |
311 | (defvar *chat-handler* (clack:clackup #'chat-server :port 12345))
312 | (defvar *client-handler* (clack:clackup #'client-server :port 8080))
313 | ~~~
314 |
--------------------------------------------------------------------------------
/windows.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Setting up Emacs on Windows or Mac
3 | ---
4 |
5 | Emacs is the preferred Lisp source code editor for most CL developers.
6 |
7 | If you want to get going easily, get
8 | [Portacle](https://shinmera.github.io/portacle/), a **portable** and
9 | **multi-platform** CL development environment. You get:
10 |
11 | - Emacs, slightly customized,
12 | - Slime,
13 | - SBCL,
14 | - Quicklisp,
15 | - Git.
16 |
17 | You only need to download Portacle and double-click to run it.
18 |
--------------------------------------------------------------------------------