| .github | ||
| src | ||
| .gitignore | ||
| ABOUT.org | ||
| after-plus.jpeg | ||
| before.jpeg | ||
| build-image.lisp | ||
| ciel.asd | ||
| FAQ.org | ||
| Makefile | ||
| README.org | ||
| repl.lisp | ||
- What is this ?
- TODOs
- Install
- Libraries
- Final words
DISCLAIMER: this is an EARLY DRAFT, but it is usable.
CIEL Is an Extended Lisp
What is this ?
CIEL is a collection of useful Quicklisp libraries.
It's Common Lisp, batteries included.
Questions, doubts? See the FAQ.
TODOs
- settle on libraries that help newcomers
- automate the documentation
- distribute (Quicklisp, Qlot, Quicklisp distribution, Ultralisp, Ultralisp distribution (upcoming)…)
- ship a core image and a binary
- optionnal: create a tool that, given a CIEL code base, explains what packages to import in order to switch to "plain CL".
How to procede ?
This is an experiment. I'd be happy to give push rights to more maintainers. We will send pull requests, discuss, and in case we don't find a consensus for what should be on by default, we can create other packages.
Rules
- don't install libraries that need a Slime helper to work in the REPL (cl-annot).
- reader syntax changes may not be enabled by default.
Install
With Quicklisp
You need a Lisp implementation and Quicklisp installed.
CIEL is not yet on Quicklisp (but it is on Ultralisp), so clone this
repository and load the .asd (with load or C-c C-k in
Slime).
git clone https://github.com/ciel-lang/CIEL ~/quicklisp/local-projects/CIEL
Then, quickload it:
(ql:quickload "ciel")
and enter the ciel-user package, instead of the default
common-lisp-user (or cl-user):
(in-package :ciel-user)
With a core image
You need a Lisp implementation, but you don't need Quicklisp.
Build a core image for your lisp with all CIEL's dependencies:
sbcl --load build-image.lisp
and use it:
sbcl --core ciel --eval '(in-package :ciel-user)'
TODO: we will distribute ready-to-use core images.
With a binary. Use CIEL's own REPL.
You don't need anything, just download the CIEL executable and run its REPL.
TODO: build it on CI for different platforms.
To build it, clone this repository and run make build.
Start it with ./ciel-repl.
You are dropped into a custom Lisp REPL, freely based on sbcli.
This REPL is more user friendly than the default SBCL one. It handles errors gracefully, it is not too verbose and it has readline capabilities, including multiline input and reset.
Libraries
Data structures
Generic and nested access to datastructures (access)
From Access, we import access and accesses (plural).
It's always
(access my-structure :elt)
for an alist, a hash-table, a struct, an object… Use accesses for
nested access (specially useful with JSON).
Hash-table utilities (Serapeum)
We import functions from Serapeum. https://github.com/ruricolist/serapeum/blob/master/REFERENCE.md#hash-tables
;; create a hash-table:
(dict :a 1 :b 2 :c 3)
Sequences utilities (Serapeum)
String manipulation (str)
Available with the str prefix.
Data formats
CSV
You have cl-csv, under its cl-csv package name and the csv
local nickname.
;; read a file into a list of lists
(cl-csv:read-csv #P"file.csv")
=> (("1" "2" "3") ("4" "5" "6"))
;; read csv from a string (streams also supported)
(cl-csv:read-csv "1,2,3
4,5,6")
=> (("1" "2" "3") ("4" "5" "6"))
;; read a file that's tab delimited
(cl-csv:read-csv #P"file.tab" :separator #\Tab)
;; loop over a CSV for effect
(let ((sum 0))
(cl-csv:do-csv (row #P"file.csv")
(incf sum (parse-integer (nth 0 row))))
sum)
See also:
- auto-text, automatic detection for text files (encoding, end of line, column width, csv delimiter etc). inquisitor for detection of asian and far eastern languages.
- CLAWK, an AWK implementation embedded into Common Lisp, to parse files line-by-line.
Databases
Pattern matching
Use Trivia, also available with the match local nickname.
Numbers
parse-float
parse-number
Parsing numbers, floats, decimals
cl-decimals: parse and format decimal numbers
https://github.com/tlikonen/cl-decimals
The main interface are the functions parse-decimal-number and
format-decimal-number. The former is for parsing strings for decimal
numbers and the latter for pretty-printing them as strings.
Reading:
DECIMALS> (parse-decimal-number "0.24")
6/25
DECIMALS> (parse-decimal-number "−12,345"
:decimal-separator #\,
:negative-sign #\−)
-2469/200
Parsing:
DECIMALS> (format-decimal-number -100/6 :round-magnitude -3)
"-16.667"
("-" "16" "." "667")
DECIMALS> (loop for e from -5 upto 5
do (print (format-decimal-number
(expt 10 e) :round-magnitude -5
:decimal-separator ","
:integer-minimum-width 7
:integer-group-separator " "
:fractional-minimum-width 7
:fractional-group-separator " ")))
" 0,000 01"
" 0,000 1 "
" 0,001 "
" 0,01 "
" 0,1 "
" 1 "
" 10 "
" 100 "
" 1 000 "
" 10 000 "
"100 000 "
NIL
Regular expressions
Use ppcre.
Threads, monitoring, scheduling
Bordeaux-Threads (bt prefix)
Moira (monitor and restart background threads)
Trivial Monitored Thread offers a very simple (aka trivial) way of spawning threads and being informed when one any of them crash and die.
cl-cron (with our fork here)
For example, run a function every minute:
(defun say-hi () (print "Hi!"))
(cl-cron:make-cron-job #'say-hi)
(cl-cron:start-cron)
Wait a minute to see some output.
Stop all jobs with stop-cron.
make-cron's keyword arguments are:
(minute :every) (step-min 1) (hour :every) (step-hour 1) (day-of-month :every)
(step-dom 1) (month :every) (step-month 1) (day-of-week :every)
(step-dow 1)
(boot-only nil) (hash-key nil))
Web
Imported:
- Hunchentoot
- Easy-routes
- Djula
- Spinneret
- Quri
- Lquery
Syntax extensions
Pythonic triple quotes docstring
https://github.com/smithzvk/pythonic-string-reader
We can use triple quotes for docstrings, and double quotes within them.
(defun foo ()
"""foo "bar"."""
t)
Lambda shortcuts
You have to enable cl-punch's syntax yourself.
https://github.com/windymelt/cl-punch/ - Scala-like anonymous lambda literal.
(cl-punch:enable-punch-syntax)
;; ^() is converted into (lambda ...) .
;; Each underscore is converted into a lambda argument.
(mapcar ^(* 2 _) '(1 2 3 4 5))
;; => '(2 4 6 8 10)
;; One underscore corresponds one argument.
(^(* _ _) 2 3)
;; => 6
;; <_ reuses last argument.
(mapcar ^(if (oddp _) (* 2 <_) <_) '(1 2 3 4 5))
;; => '(2 2 6 4 10)
;; _! corresponds one argument but it is brought to top of the argument list.
;; It can be useful when you want to change argument order.
(^(cons _ _!) :a :b)
;; => (:b . :a)
(^(list _! _! _!) 1 2 3)
;; => '(3 2 1)
Development
Testing (Fiveam)
The FiveAM test framework is available for use.
Below we create a package to contain our tests and we define the most simple one:
(defpackage ciel-5am
(:use :cl :5am))
(in-package :ciel-5am)
(test test-one
(is (= 1 1)))
Run the test with:
(run! 'test-one)
You'll see an output with explanations:
However if the test fails you will see explanations:
Use run to not print explanations.
You can use (!) to re-run the last run test.
You can ask 5am to open the interactive debugger on an error:
(setf *debug-on-error* t)
Logging (log4cl)
https://github.com/sharplispers/log4cl/
(log:info …)
repl-utilities (readme, summary,…)
(repl-utilities:readme repl-utilities)
printv
https://github.com/danlentz/printv
(:printv
(defvar *y*)
(defparameter *x* 2)
(setf *y* (sqrt *x*))
(setf *y* (/ 1 *y*)))
;; This produces the following text to PRINTV's output stream, and still results in the same returned value: 0.70710677.
;;; (DEFVAR *Y*) => *Y*
;;; (DEFPARAMETER *X* 2) => *X*
;;; (SETF *Y* (SQRT *X*)) => 1.4142135
;;; (SETF *Y* (/ 1 *Y*)) => 0.70710677
Getting a function's arguments list (trivial-arguments)
https://github.com/Shinmera/trivial-arguments
(defun foo (a b c &optional d) nil)
(arglist #'foo)
;; (a b c &optional d)
generic-cl
https://github.com/alex-gutev/generic-cl/
todo:
generic-ciel
Example:
;; with a struct or class "point":
(defmethod equalp ((p1 point) (p2 point))
(…))
Final words
That was your life in CL:

and now:
