Title: Showing some Common Lisp features | |
Author: Solène | |
Date: 05 December 2017 | |
Tags: lisp | |
Description: | |
# Introduction: comparing LISP to Perl and Python | |
We will refer to Common LISP as CL in the following article. | |
I wrote it to share what I like about CL. I'm using Perl to compare CL | |
features. I am using real world cases for the average programmer. If | |
you are a CL or perl expert, you may say that some example could be | |
rewritten with very specific syntax to make it smaller or faster, but | |
the point here is to show usual and readable examples for usual | |
programmers. | |
This article is aimed at people with programming interest, some basis | |
of programming knowledge are needed to understand the following. If | |
you know how to read C, Php, Python or Perl it should be | |
enough. Examples have been choosed to be easy. | |
I thank my friend killruana for his contribution as he wrote the | |
python code. | |
## Variables | |
### Scope: global | |
**Common Lisp code** | |
(defparameter *variable* "value") | |
Defining a variable with defparameter on top-level (= outside of a | |
function) will make it global. It is common to surround the name of | |
global variables with **\*** character in CL code. This is only for | |
readability for the programmer, the use of **\*** has no | |
incidence. | |
**Perl code** | |
my $variable = "value"; | |
**Python code** | |
variable = "value"; | |
### Scope: local | |
This is where it begins interesting in CL. Declaring a local variable | |
with **let** create a new scope with parenthesis where the variable | |
isn't known outside of it. This prevent doing bad things with | |
variables not set or already freed. **let** can define multiple | |
variables at once, or even variables depending on previously declared | |
variables using **let\*** | |
**Common Lisp code** | |
(let ((value (http-request))) | |
(when value | |
(let* ((page-title (get-title value)) | |
(title-size (length page-title))) | |
(when page-title | |
(let ((first-char (subseq page-title 0 1))) | |
(format t "First char of page title is ~a~%" | |
first-char)))))) | |
**Perl code** | |
{ | |
local $value = http_request; | |
if($value) { | |
local $page_title = get_title $value; | |
local $title_size = get_size $page_title; | |
if($page_title) { | |
local $first_char = substr $page_title, 0, 1; | |
printf "First char of page title is %s\n", $first_char; | |
} | |
} | |
} | |
a if/while/for/foreach or plain brakets. | |
**Python code** | |
if True: | |
hello = 'World' | |
print(hello) # displays World | |
There is no way to define a local variable in python, the scope of the | |
variable is limited to the parent function. | |
## Printing and format text | |
CL has a VERY powerful function to print and format text, it's even | |
named format. It can even manage plurals of words (in english only) ! | |
**Common Lisp code** | |
(let ((words (list "hello" "Dave" "How are you" "today ?"))) | |
(format t "~{~a ~}~%" words)) | |
format can loop over lists using **~{** as start and **~}** as end. | |
**Perl code** | |
my @words = @{["hello", "Dave", "How are you", "today ?"]}; | |
foreach my $element (@words) { | |
printf "%s ", $element; | |
} | |
print "\n"; | |
**Python code** | |
# Printing and format text | |
# Loop version | |
words = ["hello", "Dave", "How are you", "today ?"] | |
for word in words: | |
print(word, end=' ') | |
print() | |
words = ["hello", "Dave", "How are you", "today ?"] | |
print(*words) | |
## Functions | |
### function parameters: rest | |
Sometimes we need to pass to a function a not known number of | |
arguments. CL supports it with **&rest** keyword in the function | |
declaration, while perl supports it using the **@\_** sigil. | |
**Common Lisp code** | |
(defun my-function(parameter1 parameter2 &rest rest) | |
(format t "My first and second parameters are ~a and ~a.~%Others | |
parameters are~%~{ - ~a~%~}~%" | |
parameter1 parameter2 rest)) | |
**Perl code** | |
sub my_function { | |
my $parameter1 = shift; | |
my $parameter2 = shift; | |
my @rest = @_; | |
parameters are\n", | |
$parameter1, $parameter2; | |
printf " - %s\n", $element; | |
} | |
} | |
**Python code** | |
def my_function(parameter1, parameter2, *rest): | |
print("My first and second parameters are {} and | |
{}".format(parameter1, parameter2)) | |
print("Others parameters are") | |
for parameter in rest: | |
print(" - {}".format(parameter)) | |
The trick in python to handle rests arguments is the wildcard | |
character in the function definition. | |
### function parameters: named parameters | |
CL supports named parameters using a keyword to specify its | |
name. While it's not at all possible on perl. Using a hash has | |
parameter can do the job in perl. | |
CL allow to choose a default value if a parameter isn't set, | |
it's harder to do it in perl, we must check if the key is already set | |
in the hash and give it a value in the function. | |
**Common Lisp code** | |
(defun my-function(&key (key1 "default") (key2 0)) | |
(format t "Key1 is ~a and key2 (~a) has a default of 0.~%" | |
key1 key2)) | |
There is no way to pass named parameter to a perl function. The best | |
way it to pass a hash variable, check the keys needed and assign a | |
default value if they are undefined. | |
**Perl code** | |
sub my_function { | |
my $hash = shift; | |
$hash->{key1} = "default"; | |
} | |
$hash->{key2} = 0; | |
} | |
$hash->{key1}, $hash->{key2}; | |
} | |
**Python code** | |
def my_function(key1="default", key2=0): | |
print("My key1 is {} and key2 ({}) default to 0.".format(key1, | |
key2)) | |
## Loop | |
CL has only one loop operator, named loop, which could be seen as an | |
entire language itself. Perl has do while, while, for and foreach. | |
### loop: for | |
**Common Lisp code** | |
(loop for i from 1 to 100 | |
do | |
(format t "Hello ~a~%" i)) | |
**Perl code** | |
for(my $i=1; $i <= 100; $i++) { | |
printf "Hello %i\n"; | |
} | |
**Python code** | |
for i in range(1, 101): | |
print("Hello {}".format(i)) | |
### loop: foreach | |
**Common Lisp code** | |
(let ((elements '(a b c d e f))) | |
(loop for element in elements | |
counting element into count | |
do | |
(format t "Element number ~s : ~s~%" | |
count element))) | |
**Perl code** | |
# verbose and readable version | |
my @elements = @{['a', 'b', 'c', 'd', 'e', 'f']}; | |
my $count = 0; | |
foreach my $element (@elements) { | |
$count++; | |
printf "Element number %i : %s\n", $count, $element; | |
} | |
for(my $i=0; $i<$#elements+1;$i++) { | |
printf "Element number %i : %s\n", $i+1, $elements[$i]; | |
} | |
**Python code** | |
# Loop foreach | |
elements = ['a', 'b', 'c', 'd', 'e', 'f'] | |
count = 0 | |
for element in elements: | |
count += 1 | |
print("Element number {} : {}".format(count, element)) | |
elements = ['a', 'b', 'c', 'd', 'e', 'f'] | |
for index, element in enumerate(elements): | |
print("Element number {} : {}".format(index, element)) | |
## LISP only tricks | |
### Store/restore data on disk | |
The simplest way to store data in LISP is to write a data structure | |
into a file, using **print** function. The code output with **print** | |
can be evaluated later with **read**. | |
**Common Lisp code** | |
(defun restore-data(file) | |
(when (probe-file file) | |
(with-open-file (x file :direction :input) | |
(read x)))) | |
(with-open-file (x file | |
:direction :output | |
:if-does-not-exist :create | |
:if-exists :supersede) | |
(print data x))) | |
(save-data "books.lisp" *books*) | |
(defparameter *books* (restore-data "books.lisp")) | |
This permit to skip the use of a data storage format like XML or | |
JSON. Common LISP can read Common LISP, this is all it needs. It can | |
store objets like arrays, lists or structures using plain text | |
format. **It can't dump hash tables directly.** | |
### Creating a new syntax with a simple macro | |
Sometimes we have cases where we need to repeat code and there is no | |
way to reduce it because it's too much specific or because it's due to | |
the language itself. Here is an example where we can use a simple | |
macro to reduce the written code in a succession of conditions doing | |
the same check. | |
We will start from this | |
**Common Lisp code** | |
(when value | |
(when (string= line-type "3") | |
(progn | |
(print-with-color "error" 'red line-number) | |
(log-to-file "error"))) | |
(when (string= line-type "4") | |
(print-with-color text)) | |
(when (string= line-type "5") | |
(print-with-color "nothing"))) | |
to this, using a macro | |
**Common Lisp code** | |
(defmacro check(identifier &body code) | |
`(progn | |
(when (string= line-type ,identifier) | |
,@code))) | |
(check "3" | |
(print-with-color "error" 'red line-number) | |
(log-to-file "error")) | |
(check "4" | |
(print-with-color text)) | |
(check "5" | |
(print-with-color "nothing"))) | |
The code is much more readable and the macro is easy to | |
understand. One could argue that in another language a switch/case | |
could work here, I choosed a simple example to illustrate the use of a | |
macro, but they can achieve more. | |
### Create powerful wrappers with macros | |
I'm using macros when I need to repeat code that affect variables. A | |
lot of CL modules offers a structure like **with-something**, it's a | |
wrapper macro that will do some logic like opening a database, | |
checking it's opened, closing it at the end and executing your code | |
inside. | |
Here I will write a tiny http request wrapper, allowing me to write | |
http request very easily, my code being able to use variables from the | |
macro. | |
**Common Lisp code** | |
(defmacro with-http(url) | |
`(progn | |
(multiple-value-bind (content status head) | |
(drakma:http-request ,url :connection-timeout 3) | |
(when content | |
,@code)))) | |
(format t "We fetched headers ~a with status ~a. Content size is | |
~d bytes.~%" | |
status head (length content))) | |
In Perl, the following would be written like this | |
**Perl code** | |
sub get_http { | |
my $url = $1; | |
my %http = magic_http_get $url; | |
if($http{content}) { | |
return %http; | |
} else { | |
return undef; | |
} | |
} | |
local %data = get_http "https://dataswamp.org/"; | |
if(%data) { | |
printf "We fetched headers %s with status %d. Content size | |
is %d bytes.\n", | |
$http{headers}, $http{status}, length($http{content}); | |
} | |
} | |
The curly brackets are important there, I want to emphase that the | |
**local** %data variable is only available inside the curly | |
brackets. Lisp is written in a successive of local scope and this is | |
something I really like. | |
**Python code** | |
import requests | |
with requests.get("https://dataswamp.org/") as fd: | |
print("We fetched headers %s with status %d. Content size is %s | |
bytes." \ | |
% (list(fd.headers.keys()), fd.status_code, | |
len(fd.content))) | |