LFE Hyperpolyglot

A syntactical comparison of
LFE, Erlang, Common Lisp, & Clojure

basics

LFE Erlang common lisp clojure
version used
LFE 0.1.0 Erlang 18.0 SBCL 1.2 Clojure 1.6
show version
displayed by repl on startup
(lutil:get-lfe-version)
displayed by repl on startup
erlang:system_info(otp_release).
$ sbcl --version displayed by repl on startup
grammar and execution

LFE Erlang common lisp clojure
compiler
$ lfec module-name.lfe
$ erlc module_name.erl


standalone executable Use escript
Use escript
(sb-ext:save-lisp-and-die
"executable"
:executable t
:toplevel 'function)

interpreter
$ lfe [flags] filename.lfe [args]

$ sbcl --script foo.lisp specify full path to clojure jar:

java -cp clojure.jar clojure.main foo.clj
shebang #!/usr/bin/env lfe

#!/usr/bin/env sbcl --script specify full path to clojure jar:

#!/usr/bin/env java -jar clojure.jar
repl
$ lfe
Erlang doesn't have a true REPL; it has a shell
$ erl
$ sbcl $ java -jar /PATH/TO/clojure.jar
command line program $ lfe -eval \
'(lfe_io:format "~p" (list (+ 1 2 3)))'
$ erl -eval \
'io:format("~p", [1 + 2 + 3])' \
-noshell -run init stop


word separator
whitespace whitespace whitespace whitespace and commas
end-of-line comment (+ 1 1) ; adding 1 + 1. % adding (+ 1 1) ; adding (+ 1 1) ; adding
multiple line comment Only in source code; not in REPL
(+ 1 #| adding
        stuff  |#
   1)

(+ 1 #| adding
        stuff  |#
   1)

variables and expressions

LFE Erlang common lisp clojure
identifier
Cannot start with:
|, \', ', ,, or #

permitted characters:
!, #, $, %, &, ', *, +, ,, -, ., /, 0-9, <, =, >, ?, @, A-Z, a-z, \, ^, _, |, ~, \% , ¡, ¢, £, ¤, ¥, ¦, §, ¨, ©, ª, «, ¬, \­, ®, ¯, °, ±, ², ³, ´, µ, ¶, ·, ¸, ¹, º, », ¼, ½, ¾, ¿, À, Á, Â, Ã, Ä, Å, Æ, Ç, È, É, Ê, Ë, Ì, Í, Î, Ï, Ð, Ñ, Ò, Ó, Ô, Õ, Ö, ×, Ø, Ù, Ú, Û, Ü, Ý, Þ, ß, à, á, â, ã, ä, å, æ, ç, è, é, ê, ë, ì, í, î, ï, ð, ñ, ò, ó, ô, õ, ö, ÷, ø, ù, ú, û, ü, ý, þ, ÿ

This has special meaning and is reserved:
:

case insensitive, cannot start with digit

excluded characters:
SP ( ) " , ' ` : ; # | \

reserved for user macros:
? ! [ ] { }
case sensitive, cannot start with digit

permitted characters:
A-Z a-z 0-9 * + ! - _ ?

these have special meaning or are reserved:
/ . :
quoted identifier
and escaped identifier
(set |white space symbol| 3)

(setq |white space symbol| 3)

(setq white\ space\ symbol 3)
none

none
local variable ; parallel assignment:
(let ((x 3) (y 4))
(+ x y))

; sequential assignment:
(let* ((x 3) (y (* x x)))
(+ x y))

; parallel assignment:
(let ((x 3) (y 4))
(+ x y))

; sequential assignment:
(let* ((x 3) (y (* x x)))
(+ x y))
(let [x 3 y 4]
(+ x y))

(let [[x y] [3 4]]
(+ x y))

(let [x 3 y (* x x)]
(+ x y))
global variable

none

none
(defparameter *x* 3)

; doesn't change x if already set:
(defvar *x* 3)
(def x 3)
remove variable

none

Only in the shell:
f(Var).
(makunbound 'x) (ns-unmap *ns* 'x)
null

none

none
nil '() ; same value as null in Java:
nil
null test

none

none
(null x) (nil? x)
identifier as value 'x
(quote x)

'x
(quote x)
'x
(quote x)
identifier test

(is_atom x)

is_atom(X).
(symbolp 'x) (symbol? 'x)
identifier equality test
(== 'x 'x)
(=:= 'x 'x)

(eq 'x 'x) (= 'x 'x)
non-referential identifier
'foo
:foo :foo
identifier attributes
set, get, remove

none

none
(set 'x 13)

(setf (get 'x :desc) "unlucky")
(get 'x :desc)
(remprop 'x :desc)
; value must be instance of clojure.lang.IObj:

(def x (with-meta [13] {:desc "unlucky"}))
(get (meta x) :desc)
; none
arithmetic and logic

LFE Erlang common lisp clojure
true and false 'true 'false true false t nil true false
falsehoods
'false false nil () false nil
logical operators
(or (not 'true) (and 'true 'false)) (not true) or (true and false). (or (not t) (and t nil)) (or (not true) (and true false))
relational operators
== =:= /= =/= < > =< >= == =:= /= =/= < > =< >= = /= < > <= >= = not= < > <= >=
min and max (lists:min 1 2 3)
(lists:max 1 2 3)
lists:min(1 2 3)
lists:max(1 2 3)
(min 1 2 3)
(max 1 2 3)
(min 1 2 3)
(max 1 2 3)
numeric predicates is_number is_integer is_float is_number is_integer is_float numberp integerp
rationalp floatp
realp complexp
number? integer?
rational? float?
none none
arithmetic operators
+ - * / + - * / + - * / mod + - * / mod
integer division
and remainder
(div 7 3)
(rem 7 3)
div(7 3).
rem(7 3).
(truncate 7 3)
(rem 7 3)
(quot 7 3)
(rem 7 3)
integer division by zero exception error: badarith exception error: an error occurred when evaluating an arithmetic expression division-by-zero error
float division float:
(/ 7 (* 3 1.0))
(/ 7 (* 3 1))
float:
7 / 3 * 1.0.
7 / 3 * 1.
rational:
(/ 7 3)

float:
(/ 7 (* 3 1.0))
rational:
(/ 7 3)

float:
(/ 7 (* 3 1.0))
float division by zero exception error: badarith exception error: an error occurred when evaluating an arithmetic expression division-by-zero error
power (math:pow 2 32) math:pow(2, 32). (expt 2 32) returns float:
(Math/pow 2 32)
sqrt
(math:sqrt 2) math:sqrt(2). (sqrt 2) (Math/sqrt 2)
sqrt -1
none none #c(0.0 1.0) (Math/sqrt -1): NaN
transcendental functions math:exp math:log math:sin math:cos math:tan math:asin math:acos math:atan math:atan2 math:exp math:log math:sin math:cos math:tan math:asin math:acos math:atan math:atan2 exp log sin cos tan asin acos atan atan Math/exp Math/log Math/sin Math/cos Math/tan Math/asin Math/acos Math/atan Math/atan2
float truncation trunc trunc return two values, first is integer:
truncate round ceiling floor
return integers:
int Math/round
return floats:
Math/ceil Math/floor
absolute value
and signum
abs abs abs signum Math/abs Math/signum
integer overflow
none none none; arbitrary-precision integers clojure.lang.Numbers.throwIntOverflow exception
float overflow
none none floating-point-overflow error not literals:
-Infity NaN Infinity
rational construction none none (/ 3 7)

; literal:
3/7
(/ 3 7)

; literal:
3/7
rational decomposition none none (numerator 3/7)
(denominator 3/7)
(numerator 3/7)
(denominator 3/7)
complex construction none none #c(1 2) none
complex decomposition none none (realpart #c(1 2))
(imagpart #c(1 2))
(phase #c(1 2))
(abs #c(1 2))
(conjugate #c(1 2))
none
none
random number
uniform integer, uniform float, normal float
(random:uniform 100)
(random:uniform 1.0)
random:uniform(100).
random:uniform(1.0).
(random 100)
(random 1.0)
none
(def rnd (java.util.Random.))
(.nextInt rnd 100)
(.nextFloat rnd)
(.nextGaussian rnd)
random seed (random:seed)
(random:seed 4 2 0)
random:seed().
random:seed(4 2 0).
(setq *random-state*
(sb-ext:seed-random-state 17))

bit operators bsl bsr band bor bxor bnot bsl bsr band bor bxor bnot ash left shift when 2nd argument positive logand logior logxor lognot bit-shift-left bit-shift-right bit-and bit-or bit-xor bit-not
binary, octal, and hex literals #b101010
#o52
#x2a
2#101010
8#52
16#2a
#b101010
#o52
#x2a

radix
#7r60 7#60. (format nil "~7r" 42)
strings

LFE Erlang common lisp clojure
string test
(io_lib:printable_latin1_list "foo") io_lib:printable_latin1_list("foo"). (stringp "foo") (string? "foo")
string literal
"foo bar" "foo bar" "foo bar" "foo bar"
unicode test
(io_lib:printable_unicode_list "foo") io_lib:printable_unicode_list("foo").

unicode literal
#"foo bar"
(binary ("foo bar" utf8))



newline in literal
yes yes yes yes
literal escapes \b \t \n \v \f \r \e \s \d
\" \\ \b \t \n \f \r \" \\ \ooo \uhhhh
constructor
(list #\f #\o #\o)


format string (lfe_io:format "~p: ~p ~.2f~n" '("Foo" 7 13.457)) io:format("~p: ~p ~.2f~n", ["Foo", 7, 13.457]). (format nil "~a: ~a ~,2f" "Foo" 7 13.457) (String/format "%s: %d %.2f"
(to-array ["Foo" 7 13.457]))
format specifiers The following are for lfe_io:format
~~    literal ~
~c    ASCII character code
~f    scientific notation
~g    float for small numbers, scientific for large
Each of the previous three may take additional precision formatting options
~s    string
~w    standard LFE syntax
~p    pretty-printed LFE syntax
~W    same as ~w, but takes maximum depth argument
~P    same as ~p, but takes maximum depth argument
~B    formats integer to given base
~X    like ~B, but takes a prefix argument
~#    like ~B, but uses #-separated Erlang prefix
~b    like ~B, but prints lowercase
~x    like ~X, but prints lowercase
~+    like ~#, but prints lowercase
~n    newline
~i    ignores next term
The following are for io:format
~~    literal ~
~c    ASCII character code
~f    scientific notation
~g    float for small numbers, scientific for large
Each of the previous three may take additional precision formatting options
~s    string
~w    standard Erlang syntax
~p    pretty-printed Erlang syntax
~W    same as ~w, but takes maximum depth argument
~P    same as ~p, but takes maximum depth argument
~B    formats integer to given base
~X    like ~B, but takes a prefix argument
~#    like ~B, but uses #-separated Erlang prefix
~b    like ~B, but prints lowercase
~x    like ~X, but prints lowercase
~+    like ~#, but prints lowercase
~n    newline
~i    ignores next term
~a    any type, human readable
~s    any time, read parseable
~%    newline
~~    tilde
~c    character
~,5f  5 digits right of decimal mark
~d    decimal
~x    hex
~o    octal
~b    binary

compare strings (string:equal "foo" "bar")
Also the comparison operators work, e.g: (== "foo" "bar")
(< "foo" "bar")
(=< "foo" "bar")

(string= "foo" "bar")
(string< "foo" "bar")
(.equals "foo" "bar")
(.compareTo "foo" "bar")
concatenate
(string:concat "foo" "bar")
(++ "foo" "bar")

(concatenate 'string "foo " "bar " "bar") (str "foo " "bar " "baz")
replicate (string:copies "foo" 3)
(string:chars #\f 3)
(lists:duplicate 3 #\f)

(make-string 3 :initial-element #\f) (String. (into-array
(. Character TYPE)
(repeat 3 \f)))
translate case (string:to_lower "FOO")
(string:to_upper "foo")

(string-downcase "FOO")
(string-upcase "foo")
(.toLowerCase "FOO")
capitalize none none ; "Foo Bar":
(string-capitalize "foo bar")

trim (string:strip " foo " 'both #\ )

(string-trim
'(#\space #\tab #\newline)
" foo ")
(.trim " foo ")
pad
on right, on left
; right
(string:right "foo" 10)
(lfe_io:format "~10.8s~n" '("foo"))
; left
(string:left "foo" 10)
(lfe_io:format "~-10.8s~n" '("foo"))

(format nil "~10a" "foo")
(format nil "~10@a" "foo")

number to string (++ "Value: " (integer_to_list 8))
(concatenate 'string
"value: "
(princ-to-string 8))
(str "Value: " 8)
string to number (+ 7 (list_to_integer "12"))
(+ 7 (parse-integer "12"))

(+ 73.9 (read-from-string ".037"))
(+ 7 (Integer/parseInt "12"))

(+ 73.9 (Float/parseFloat ".037"))
split (string:tokens "foo \tbar\nbaz" '(9 10 #\ ))
(cl-ppcre:split
"[ \t\n]+"
"foo bar baz")
(seq
(.split "foo bar baz"
"[ \t\n]+"))
string join (string:join '("foo" "bar" "baz") " ")
(reduce
(lambda (m o)
(concatenate 'string m " " o))
'("foo" "bar" "baz"))
(reduce #(str %1 " " %2)
'("foo" "bar" "baz"))
length
(length "foo") length("foo"). (length "foo") (.length "foo")
index of substring (string:str "foo bar" "bar") string:str("foo bar", "bar"). (search "bar" "foo bar") (.indexOf "foo bar" "bar")
extract substring
; until position n
(string:sub_string "foo bar" 5 7)
; for n chars
(string:substr "foo bar" 5 3)

(subseq "foo bar" 4 7) (.substring "foo bar" 4 7)
character literal #\A #\Z #\a #\z #\? #\#
Currently control characters are ASCII-code only

#\a #\space #\newline #\backspace #\tab #\linefeed #\page #\return #\rubout \a \newline \space \backspace \tab ? \formfeed \return ?
test characters
(and (>= x 0) (=< x 255))
(characterp #\x)
(alpha-char-p #\x)
(alphanumericp #\x)
(digit-char-p #\7)
(lower-case-p #\x)
(upper-case-p #\X)
(char? \x)
chr and ord '(97)
#\a

(code-char 97)
(char-code #\a)
(char 97)
(int \a)
to array of characters '(#\f #\o #\o)


character lookup
(lists:nth 1 "foo")
(char "foo" 0) (.charAt "foo" 0)
regular expressions

LFE Erlang common lisp clojure
literal use a string:
"\\b\\d{5}\\b"
use a string:
"\\b\\d{5}\\b"
use a string:
"\\b\\d{5}\\b"
#"\b\d{5}\b"
character class abbrevations . \d \D \s \S \w \W . \d \D \s \S \w \W . \d \D \s \S \w \W . \d \D \s \S \w \W
anchors ^ $ \b \B ^ $ \A \b \B \G \z \Z ^ $ \b \B ^ $ \A \b \B \G \z \Z
match test (re:run "foo bar" "bar")
(ql:quickload "cl-ppcre")

(if (cl-ppcre:all-matches "1999" s)
(format t "party!"))
(re-find #"bar" "foo bar")
case insensitive match test (re:run "foo BAR" "bar" '(caseless))

(re-find #"(?i:lorem)" "Lorem")
substitution (re:replace "foo bar baz" "ba" "EL")
(re:replace "foo bar baz" "ba" #"EL" '(global))

(cl-ppcre:regex-replace "[^l]l"
"hello"
"EL")

(cl-ppcre:regex-replace-all "[^l]l"
"hello hello"
"EL")
(.replaceFirst "hello" "[^l]l" "XX")

(.replaceAll "hello hello"
"[^l]l" "XX")
group capture (re:run "2010-06-03" "(\\d{4})-(\\d{2})-(\\d{2})")

(let [[_ yr mn dy]
(re-find #"(\d{4})-(\d{2})-(\d{2})"
"2010-06-03")]
yr)
scan


(re-seq #"\w+" "dolor sit amet")
backreference in match and substitution (re:run "sense and sensibility" "(sens|respons)e and \\1ibility")
(re:replace "sense and sensibility" "(sens|respons)e and \\1ibility" "dumb and dumber")



dates and time

LFE Erlang common lisp clojure
broken-down datetime type No dedicated type; a 3-tuple is used for timestamps, and
a 2-tuple for date and time:


#(megaseconds seconds microseconds)

#(#(year month day) #(hour minute second))

No dedicated type; a list of 9 values is used:

second: 0-59
minute: 0-59
hour: 0-23
day of month: 1-31
month: 1-12
year: 4 digits
day of week: 0-6 for Mon-Sun
is daylight savings time: t or nil
timezone: negated UTC offset in hours

current datetime (erlang:timestamp)
(calendar:local_time)

(get-decoded-time) (def dt (new java.util.Date))
current unix epoch (erlang:system_time 'seconds)
; seconds since Jan 1, 1900
(get-universal-time)
(/ (System/currentTimeMillis) 1000.0)
unix epoch to broken-down datetime

(decode-universal-time
(get-unversal-time))
(def dt (new java.util.Date
(System/currentTimeMillis)))
broken-down datetime to unix epoch

(encode-universal-time 0 22 10 31 5 2015) (/ (.getTime (new java.util.Date)) 1000.0)
format datetime


(def s "yyyy-MM-dd HH:mm:ss")
(def fmt (new java.text.SimpleDateFormat s))

(.format fmt (new java.util.Date))
parse datetime


(def s "yyyy-MM-dd HH:mm:ss")
(def fmt (new java.text.SimpleDateFormat s))

(.parse fmt "2015-05-30 09:14:14")
date parts

(multiple-value-bind
(ss mi hr dy mo yr)
(get-decoded-time)
(list ss mi hr) ; quiesce warning
(list dy mo yr))
(def cal (new java.util.GregorianCalendar))
(.setTime cal dt)

(.get cal java.util.Calendar/DAY_OF_MONTH)
(+ (.get cal java.util.Calendar/MONTH) 1)
(.get cal java.util.Calendar/YEAR)
time parts

(multiple-value-bind
(ss mi hr)
(get-decoded-time)
(list ss mi hr))
(def cal (new java.util.GregorianCalendar))
(.setTime cal dt)

(.get cal java.util.Calendar/HOUR_OF_DAY)
(.get cal java.util.Calendar/MINUTE)
(.get cal java.util.Calendar/SECOND)
build broken-down datetime

(encode-universal-time 0 22 10 31 5 2015) (let
[yr 2015 mo 5 dy 31 hr 10 mi 22 ss 0]
(def cal
(new java.util.GregorianCalendar
yr (- mo 1) dy hr mi ss)))
lists

LFE Erlang common lisp clojure
literal
'(1 2 3)
(quote (1 2 3))

'(1 2 3)
(quote (1 2 3))
'(1 2 3)
(quote (1 2 3))
constructor
(list 1 2 3)
(list 1 2 3) (list 1 2 3)
predicate
(is_list x)
(listp x) (list? x)
empty test (== x '())
nil and '() are synonyms and evaluate as false in a boolean context. All other values are true. (empty? ())
evaluating the empty list ()
nil ()
cons
(cons 1 '(2 3))
(cons 1 '(2 3)) (cons 1 '(2 3))
head
(car '(1 2 3))
(set `(,h . ,_) '(1 2 3))

(car '(1 2 3))
(first '(1 2 3))
first
tail
(cdr '(1 2 3))
(set `(,_ . ,t) '(1 2 3))

(cdr '(1 2 3))
(rest '(1 2 3))
(rest '(1 2 3))
(next '(1 2 3))
head and tail of empty list head
exception error: badarg
tail
()
cons pattern
exception error: #(badmatch ())

both evaluate to nil ()
length
(length '(1 2 3))
(length '(1 2 3)) (count '(1 2 3))
equality test
(= '(1 2 3) '(1 2 3))
(equal '(1 2 3) '(1 2 3)) (= '(1 2 3) '(1 2 3))
nth element indexed from one
(lists:nth 3 '(1 2 3 4))

indexed from zero
(nth 2 '(1 2 3 4))
(nth '(1 2 3 4) 2)
out-of-bounds behavior exception error: function_clause exception error: no function clause matching nil raises IndexOutOfBoundsException
element index none none (position 7 '(5 6 7 8)) none
concatenate
(++ '(1 2 3) '(4 5 6))
(lists:append '(1 2 3) '(4 5 6))

(append '(1 2 3) '(4 5 6)) (concat '(1 2 3) '(4 5 6))
take
(lists:sublist '(1 2 3 4) 2)
none (take 2 '(1 2 3 4))
drop
(lists:nthtail 2 '(1 2 3 4)) (nthcdr 2 '(1 2 3 4)) (drop 2 '(1 2 3 4))
last element
(lists:last '(1 2 3)) (car (last '(1 2 3))) (last '(1 2 3))
all but last element (lists:droplast '(1 2 3)) (butlast '(1 2 3)) (butlast '(1 2 3))
reverse
(lists:reverse '(1 2 3)) (reverse '(1 2 3)) (reverse '(1 2 3))
sort
(lists:sort '(3 2 4 1))
(lists:sort #'>/2 '(3 2 4 1))

(sort '(3 2 4 1) '<) (sort < '(3 2 4 1))
dedupe
(lists:usort '(1 1 2 3))
(remove-duplicates '(1 1 2 3))
membership
(lists:member 7 '(1 2 3))
(member 7 '(1 2 3))
map (lists:map (lambda (x)(* x x)) '(1 2 3))
(mapcar (lambda (x) (* x x)) '(1 2 3)) (map #(* % %) '(1 2 3))
filter (lists:filter (lambda (x) (> x 2)) '(1 2 3))
(remove-if-not (lambda (x) (> x 2)) '(1 2 3))

; remove-if returns complement
(filter #(> % 2) '(1 2 3))

; remove returns complement
reduce (lists:foldl #'*/2 1 '(1 2 3 4))
(reduce '* '(1 2 3 4) :initial-value 1) (reduce * 1 '(1 2 3 4))
right fold (lists:foldr #'-/2 0 '(1 2 3 4))
(reduce '-
'(1 2 3 4)
:initial-value 0
:from-end t)
none
iterate (lists:foreach
  (lambda (x)
    (io:format "~p~n" `(,x))
    (io:format "~p~n" `(,(- x))))
  '(1 2 3))

(dolist (x '(1 2 3))
  (print x)
  (print (- x)))
(doseq [x '(1 2 3)]
  (println x)
  (println (- x)))
universal predicate (lists:all
  (lambda (i) (== 0 (rem i 2)))
  '(1 2 3 4))

(every
  (lambda (i) (= 0 (rem i 2)))
  '(1 2 3 4))
(every? #(= 0 (rem % 2)) '(1 2 3 4))
existential predicate (lists:any
  (lambda (i) (== 0 (rem i 2)))
  '(1 2 3 4))

(some
  (lambda (i) (= 0 (rem i 2)))
  '(1 2 3 4))
(some #(= 0 (rem % 2)) '(1 2 3 4))
list comprehension (lc ((<- file "ABCDEFGH")
     (<- rank (lists:seq 1 9)))
    (++ `(,file) (integer_to_list rank)))


(for
  [file "ABCDEFGH" rank (range 1 9)]
  (format "%c%d" file rank))
shuffle
none none none (shuffle '(1 2 3 4))
set head none none (defparameter *a* '(1 2 3))
(setf (car *a*) 3)
none
set tail none none (defparameter *a* '(1 2 3))
(setf (cdr *a*) '(4 5 6))
none
manipulate back none none (defparameter *a* '(1 2 3))
(push 4 *a*)
(pop *a*)

flatten (lists:flatten '(1 2 (3 (4))))

(flatten '(1 2 (3 (4))))
associative array lookup
(assoc 'key2 '((key1 "val1") (key2 "val2")))
(assoc :key2 '((:key1 "val1") (:key2 "val2"))) none, see note
flat associative array lookup
none none (getf '(:key1 "val1" :key2 "val2") :key2) none
pair literal
'(1 . 2)
'(1 . 2) none
cons cell test none none (cons '(1 . 2))
(not (atom '(1 . 2)))
none
translate elements recursively (sublis '((1 . 2) (3 . 4))
'(1 (3 3 (1))))

(sublis '((1 . 2) (3 . 4))
'(1 (3 3 (1))))

fixed-length arrays

LFE Erlang common lisp clojure
literal
#(1 2 3) {1,2,3}. #(1 2 3) [1 2 3]
constructor
(tuple 1 2 3) {1,2,3}. (vector 1 2 3) (vector 1 2 3)
size
(size #(1 2 3)) size({1,2,3}). (length #(1 2 3)) (count [1 2 3])
lookup (element 1 #(1 2 3)) element(1, {1,2,3}). (elt #(1 2 3) 0) or
(aref #(1 2 3) 0)
(nth [1 2 3] 0)
update (setelement 3 #(1 2 3) 4) setelement(3, {1,2,3}, 4). (setq v [1 2 3])
(setf (aref v 2) 4)
(replace {2 4} [1 2 3])
out-of-bounds behavior exception error: badarg exception error: bad argument raises sb-kernel:index-too-large-error
array to list
(tuple_to_list #(1 2 3)) tuple_to_list({1,2,3}). (coerce #(1 2 3) 'list) (seq [1 2 3])
list to array
(list_to_tuple '(1 2 3)) list_to_tuple [1,2,3]). (coerce '(1 2 3) 'vector) (vec '(1 2 3))
reverse none none (reverse #(1 2 3))
sort none none (sort #(2 4 1 3) #'<)
map

(map 'vector (lambda (x) (* x x)) #(1 2 3))
filter none none (remove-if-not (lambda (x) (> x 2)) #(1 2 3))

; also remove-if

reduce none none

dictionaries

LFE Erlang common lisp clojure
literal
#M(a 1 b 2 c 3) #{a => 1,b => 2,c => 3}. none ; clojure.lang.PersistentArrayMap:
{"t" 1 "f" 0}
constructor (map 'a 1 'b 2 'c 3)
(maps:new)
(maps:from_list '(#(a 1) #(b 2) #(c 3)))
maps:new().
maps:from_list([{a,1},{b,2},{c,3}]).
(defparameter *h* (make-hash-table :test 'equal))

; default equality test is 'eql
; immutable:
(def ih (hash-map "t" 1 "f" 0))
predicate
(is_map m) is_map(M). (hash-table-p *h*) (map? ih)
size
(maps:size m) maps:size(M). (hash-table-count *h*) (count ih)
lookup
(maps:get 'c #m(a 1 b 2 c 3))
(mref #m(a 1 b 2 c 3) 'c)
(set `#m(c ,val) #m(a 1 b 2 c 3))
maps:get(c,#{a => 1,b => 2,c => 3}).
#{c := Val} = #{a => 1,b => 2,c => 3}.
(gethash "t" *h*) (get ih "t")
(find ih "t")

; return -1 if not found:
(get ih "m" -1)
update (maps:update 'c 4 #m(a 1 b 2 c 3))
(mupd #m(a 1 b 2 c 3) 'c 4)
(maps:put 'c 4 #m(a 1 b 2 c 3))
maps:update(c,4,#{a => 1,b => 2,c => 3}).
maps:put(c,4,#{a => 1,b => 2,c => 3}).
(setf (gethash "t" *h*) 1) (def ih2 (assoc ih "t" 2))
missing key behavior exception error: #(badkey ...) exception error: {badkey,...} returns nil returns nil
is key present
(maps:is_key 'c m) maps:is_key(c,M). (nth-value 1 (gethash "t" *h*)) (contains? ih "t")
delete (maps:remove 'c m)
(maps:remove keys m)
maps:remove(c,M).
maps:without([Keys], M)
(remhash "t" *h*) (def ih2 (dissoc ih "t"))
merge (maps:merge m1 m2) maps:merge(M1,M2).
; values in ih2 take precedence:
(define ih3 (merge ih ih2))
invert none none
(require 'clojure.set)

(define ih4 (clojure.set/map-invert ih))
iterate (maps:map ...) creates new map maps:map(...). creates new map (maphash
(lambda (k v)
(print k)
(print v))
*h*)
(doseq [p ih]
(println (first p))
(println (second p)))
keys and values as lists (maps:keys m)
(maps:values m)
(maps:to_list
maps:keys(M).
maps:values(M).
maps:to_list(M).
none (def hkeys (map (fn [p] (first p)) ih))
(def hvals (map (fn [p] (second p)) ih))
user-defined types

LFE Erlang common lisp clojure
defstruct (defrecord account
  (id 0)
  (balance 0.0))
or
(defrecord account id balance)

(defstruct account id balance) (defstruct account :id :balance)
struct (set a (make-account))
or
(set a (make-account id 3 balance 17.12))

(setq a
(make-account
:id 3
:balance 17.12))
(def a (struct account 3 17.12))
struct getter
(account-id a)
(account-id a) (:id a)
struct setter
(set-account-balance a 100)
(setf (account-balance a) 0) none
struct predicate
none none (account-p a) none
functions

LFE Erlang common lisp clojure
define function
(defun add (x y) (+ x y)) add(X,Y) -> X + Y. (defun add (x y) (+ x y)) (defn add [x y] (+ x y))
can function and variable share name yes no yes no
optional argument Convention: pass option tuple, list of tuples, or map Convention: pass option tuple, list of tuples, or map (defun add (a &optional b)
(if (null b) a (+ a b)))
(defn add ([a] a) ([a b] (+ a b)))
no syntax error if called with more than 2 args:
(defn add [a & [b]]
(if (nil? b) a (+ a b)))
variable number of arguments no no (defun add (a &rest b)
(if (null b)
a
(+ a (eval (cons '+ b)))))
(defn add [a & b]
(if (nil? b) a (+ a (apply + b))))
default value none none (defun add (a &optional (b 0))
(+ a b))
(defn add
([a] (add a 0))
([a b] (+ a b)))
named parameter Convention: pass option tuple, list of tuples, or map Convention: pass option tuple, list of tuples, or map (defun logarithm (&key number base)
(/ (log number) (log base)))

(logarithm :base 2 :number 8)
(defn logarithm [{x :number b :base}] (/ (Math/log x) (Math/log b)))
(logarithm {:base 2 :number 8})
return multiple values (defun sqrts (x)
  `#(,(math:sqrt x) ,(- (math:sqrt x))))

(defun sqrts (x)
(values (sqrt x) (- (sqrt x))))
(defn sqrts [x] (list (Math/sqrt x) (- (Math/sqrt x))))
assign multiple values to local variables (let ((`#(,r1 ,r2) (sqrts 3))) r2)
(multiple-value-bind (r1 r2)
(sqrts 3)
r2)
(let [[r1 r2] (sqrts 3)] r2)
assign multiple values to global variables none none (multiple-value-setq (r1 r2)
(sqrts 3))
none
convert list to multiple values multiple values are lists multiple values are lists (values-list '(1 2 3)) multiple values are lists
assign multiple values to list multiple values are lists multiple values are lists (multiple-value-list (sqrts 3)) multiple values are lists
tail call optimization yes yes yes for sbcl yes with recur
lambda (lambda (x) (* x x)) fun(X) -> X * X end. (lambda (x) (* x x)) #(* % %)
(fn [x] (* x x))

; shortcut notation with two args:
#(* %1 %2)
apply (funcall (lambda (x) (* x x)) 2)
(apply (lambda (x) (* x x)) '(2))
apply(fun(X) -> X * X end, [2]). ((lambda (x) (* x x)) 2)

(apply #'(lambda (x) (* x x)) '(2))
(#(* % %) 2)

((fn [x] (* x x)) 2)

(apply #(* % %) '(2))
execution control

LFE Erlang common lisp clojure
progn
progn prog1 prog2 none progn prog1 prog2 do none none
loop none none (setq i 1)
(loop (print "hello")
(if (> i 10)
(return)
(setq i (+ i 1))))
(loop [i 1]
(if (<= i 10)
(do (println "hello")
(recur (+ i 1)))))
do none none (do ((i 1) (sum 0))
((> i 100) sum)
(setq sum (+ sum i))
(setq i (+ i 1)))
do* initializes serially
none
dotimes none none (dotimes (i 10 nil)
(format t "hello~%"))
(dotimes [_ 10]
(println "hello"))
if
(if (< x 0) (- x) x) if X < 0 -> -X;
   X >= 0 -> X
end
(if (< x 0) (- x) x) (if (< x 0) (- x) x)
when Only used in guards
(defun print
 ((x y) (when (< x y))
   "x is less than y")
 ((_ _)    'false))
Only used in guards
(when (< x y)
(print "x is less ")
(print "than y"))
(when (< x y)
(println "x is less ")
(println "than y"))
cond (cond
  ((> x 0) 1)
  ((=:= x 0) 0)
  ('true -1))

(cond
  ((> x 0) 1)
  ((= x 0) 0)
  (t -1))
(cond
  (> x 0) 1
  (= x 0) 0
  true -1)
exceptions

LFE Erlang common lisp clojure
error


(error "failed") (throw (Exception. "failed"))
handle error

(handler-case
(error "failed")
(simple-error (e)
(format t "error: ~a" e)))
(try (throw (Exception. "failure"))
(catch Exception e
(printf "error: %s"
(.getMessage e))))
define exception

(define-condition odd-err (error)
((num :accessor odd-err-num
:initarg :num))
(:report
(lambda (e s)
(format s "odd number: ~a"
(odd-err-num e)))))

throw exception

(error 'odd-err :num 7) (throw (Exception. "failed"))
catch exception

(handler-case (/ 1 0)
(division-by-zero ()
(progn
(format t "division by zero")
nil)))
(try (/ 1 0) (catch ArithmeticException _ (do (println "division by zero") nil)))
restart-case

(defun halve (l)
(mapcar (lambda (x)
(restart-case
(if (= (rem x 2) 0) (/ x 2)
(error 'odd-error :num x))
(round-down () (/ (- x 1) 2))
(round-up () (/ (+ x 1) 2)))) l))
none
invoke-restart

(handler-bind
((odd-err
(lambda (c)
(invoke-restart
'round-down))))
(halve '(1 2 4 9)))
none
finally clause

(unwind-protect
(error "failure")
(print "clean up"))
(try (throw (Exception. "failure"))
(finally (println "clean up")))
streams

LFE Erlang common lisp clojure
standard file handles

*standard-input*
*standard-output*
*error-output*
*in*
*out*
*err*
end-of-file behavior

read-line returns two values, the 2nd set to T at end-of-file.

EOF-OF-FILE is signaled when reading past end of file.
.readLine on a java.io.Reader object returns nil.
read line from stdin

(setq line (read-line)) (let [s (read-line)]
(comment use s))
chomp



read-line discards newline
write line to stdout

(defun println (s)
(format t "~a~%" s))

(println "hello")
(println "hello")
write formatted string to stdout

(format t "~s ~d: ~2$~%"
"foo"
7
13.7)
(printf "%s %d %.2f\n" "foo" 7 13.7)
open file for reading

(setq in (open "/etc/hosts")) ; f is java.io.Reader object:
(let [f (clojure.java.io/reader "/etc/hosts")]
(.readLine f))
open file for writing

(setq out (open "/tmp/test" :direction :output :if-exists :supersede)) ; f is java.io.Writer object:
(let [f (clojure.java.io/writer "/tmp/foo")]
(.write f "lorem ipsum\n")
(.close f))
open file for appending

(setq out (open "/tmp/test" :direction :output :if-exists :append)) (let [f (clojure.java.io/writer "/tmp/foo"
:append true)]
(.write f "lorem ipsum\n")
(.close f))
close file


(close in) (.close f)
close file implicitly

(with-open-file (out #P"/tmp/test" :direction :output) (write-line "lorem ipsum" out)) (with-open [f
(clojure.java.io/reader "/etc/hosts")]
(comment use f))
read line


(setq line (read-line f)) (.readLine f)
iterate over file by line


(loop [line (.readLine f)]
(if (not= line nil)
(do (println line)
(recur (.readLine f)))))
read file into array of strings


(vec (line-seq f))
read file into string


(let [s (slurp "/etc/hosts")]
(print s))
write string



(.write f s)
write line


(.write f (println-str s))
flush file handle



(f .flush)
file handle position

get, set



; arg is characters from current position;
; moving backward not possible:

(.skip f 1000)

; arg is max characters to buffer:
(.mark f 1000000)
; move to position saved when .mark was called:
(.rest f)
in memory stream

(setq f (make-string-input-stream
"lorem ipsum"))
(read-line f)

(setq f2 (make-string-output-stream)
(write-string "lorem ipsum)
(get-output-stream-string out)
; use *in* to read from string:
(with-in-str "lorem ispum"
(read-line))

; use *out* to write to string:
(with-out-str
(println "lorem ipsum"))
emacs buffers





list buffers



current buffer
get and set




clear buffer



point
get and set




search and set point



insert at string point



current buffer as string



insert file contents at point



mark
get and set




files

LFE Erlang common lisp clojure
file test, regular file test

(osicat:file-exists-p "/tmp/foo")
(osicat:regular-file-exists-p "/tmp/foo")
(.exists (io/file "/etc/hosts"))
file size


(.length (io/file "/etc/hosts"))
is file readable, writable, executable


(.canRead (io/file "/etc/hosts"))
(.canWrite (io/file "/etc/hosts"))
(.canExecute (io/file "/etc/hosts"))
set file permissions



last modification time


; Unix epoch in milliseconds:
(.lastModified (java.io.File. "/tmp/foo"))
copy file, remove file, rename file

(cl-fad:copy-file #P"/tmp/foo"
#P"/tmp/bar")

(delete-file #P"/tmp/foo")

(rename-file #P"/tmp/bar"
#P"/tmp/foo")
(clojure.java.io/copy
(java.io.File. "/tmp/foo")
(java.io.File. "/tmp/bar"))
(clojure.java.io/delete-file "/tmp/foo")
(.renameTo (java.io.File. "/tmp/bar")
(java.io.File. "/tmp/foo"))
create symlink, symlink test, get target

(osicat:make-link "/tmp/hosts" :target "/etc/hosts")
temporary file


; java.io.File:
(java.io.File/createTempFile "foo" ".txt")
directories

LFE Erlang common lisp clojure
build pathname

(make-pathname
:directory '(:absolute "etc")
:name "hosts")
(require '[clojure.java.io :as io])

; returns java.io.File;
; convert to string with .getPath:

(io/file "/etc" "hosts")
dirname and basename

(pathname-directory #P"/etc/hosts")

(pathname-name #P"/etc/hosts")
(require '[clojure.java.io :as io])

(.getParent (io/file "/etc/hosts"))
(.getName (io/file "/etc/hosts"))
absolute pathname


(.getCanonicalPath (java.io.File. ".."))
iterate over directory by file

(dolist (file (osicat:list-directory "/tmp")) (format t "~a~%" file)) ; file-seq returns java.io.File objects for files
; in arg directory and any subdirs recursively.

(filter #(= (.getParent %) "/etc")
(file-seq (clojure.java.io/file "/etc")))
make directory


(require '[clojure.java.io :as io])

(.mkdir (io/file "/tmp/foo"))
recursive copy



remove empty directory

(delete-directory "/tmp/foo.d") (clojure.java.io/delete-file "/tmp/foo.d")
remove directory and contents

(osicat:delete-directory-and-files "/tmp/foo.d")
directory test

(osicat:directory-exists-p #P"/etc") (.isDirectory (io/file "/etc"))
processes and environment

LFE Erlang common lisp clojure
command line arguments

*posix-argv* *command-line-args*
program name



environment variables

(posix-getenv "HOME") (System/getenv "HOME")
user id and name



exit



external command

(run-program "ls" '( "/etc")) (.exec (Runtime/getRuntime) "ls")
command substitution



libraries and namespaces

LFE Erlang common lisp clojure
complete example


$ cat b/a.clj
(ns b.a)
(def x 3)

$ java -cp clojure.jar:. clojure.main
=> (require 'b.a)
=> b.a/x
3
compile library

(compile-file "a.lisp") (compile 'a)
load library


(load "a.lisp") (require 'a)
load library in subdirectory

(load "b/a.lisp") (require 'b.a)
hot patch

(load "a.lisp") (require 'b.a :reload)
load error

raises sb-int:simple-file-error raises FileNotFoundException
library path

contains working directory at startup same as path used by java VM
library path environment variable

none CLASSPATH
library path command line option

none $ java -cp /foo/bar:/baz/quux
namespace declaration

(defpackage :foo) (ns mconst)
subnamespace declaration

none ; must be in b/a.clj:
(ns b.a)
namespace separator

: . and /
import definitions

; set current *package* to foo and import symbol twiddle from bar:
(defpackage :foo
(:import-from :bar :twiddle))

import all definitions in namespace

; set current *package* to foo and import symbols from bar:
(defpackage :foo
(:use :bar))

namespace shadow avoidance



identifier shadow avoidance



package manager help



list installed packages



search packages

(ql:system-apropos "time")
install package

; install quicklisp
(load "~/quicklisp/setup.lisp")
(ql:quickload "osicat")

remove package



objects

LFE Erlang common lisp clojure
define class

(defclass rectangle ()
(
(height
:accessor rectangle-height
:initarg :height)
(width
:accessor rectangle-width
:initarg :width)))
use java:
public class Rectangle {
public float height;
public float width;
public Rectangle(float h, float w) {
this.height = h;
this.width = w;
}
public void setHeight(float h) {
this.height = h;
}
public void setWidth(float w) {
this.width = w;
}
make instance

(make-instance 'rectangle
:height 3
:width 7)
(import 'Rectangle)
(def r (Rectangle. 7 3))
read attribute


(rectangle-height rect) (.height r)
write attribute


(setf (rectangle-height rect) 4) (.setHeight r 8)
define method

(defmethod area ((figure rectangle))
(* (rectangle-height figure)
(rectangle-width figure)))
(defmulti area class)
(defmethod area Rectangle [r] (* (.height r) (.width r)))
invoke method


(area rect) (area r)
universal superclass

standard-object t Object
multiple inheritance

yes only one direct superclass; can implement multiple interfaces
lisp macros

LFE Erlang common lisp clojure
backquote and comma

(setq op '+)
(eval `(,op 1 1))
(def op +)
(eval `(,op 1 1))
defmacro

(defmacro rpn (arg1 arg2 op)
(list op arg1 arg2))
(defmacro rpn [arg1 arg2 op]
(list op arg1 arg2))
defmacro w/ backquote

(defmacro rpn (arg1 arg2 op)
`(,op ,arg1 ,arg2))
(defmacro rpn [arg1 arg2 op] `(~op ~arg1 ~arg2))
macro predicate

(macro-function rpn) none
macroexpand

(macroexpand ’(rpn 1 2 +)) (macroexpand '(rpn 1 2 +))
splice quote

(defmacro add ( &rest args )
`(+ ,@args))
(defmacro add [ & args ] `(+ ~@args))
recursive macro

(defmacro add (a &rest b)
`(if (null ',b)
(+ ,a)
(+ ,a (add ,@b))))
(defmacro add ([a] `(+ ~a)) ([a & b] `(+ ~a (add ~@b))))
hygienic


no with # suffix
local values

(defmacro square-sum (x y)
(let ((sum (gensym)))
`(let ((,sum (+ ,x ,y)))
(* ,sum ,sum))))
(defmacro two-list [x] `(let [arg# ~x] (list arg# arg#)))
reflection

LFE Erlang common lisp clojure
inspect type


(type-of '(1 2 3))
(typep '(1 2 3) 'list)
(listp '(1 2 3))
(= (type 1) java.lang.Long)
(= (class 1) java.lang.Long)
(integer? 1)
instance-of


instance?
basic types

logical and numeric:
bignum bit complex double-float fixnum float integer long-float nil null number ratio rational real short-float signed-btye single-float t unsigned-byte

symbols and strings:
base-character character extended-character keyword simple-string standard-char string symbol

data structures:
array atom bit-vector cons hash-table list sequence simple-array simple-bit-vector simple-vector vector

other:
compiled-function function package pathname random-state stream

sequence data types

list vector all collections and strings
get docstring


(describe #'mapcar) (doc map)
define function with docstring

(defun add (x y)
"add x and y"
(+ x y))
(defn add "add x and y" [x y]
(+ x y))
apropos and documentation search

none (apropos #"^add$")
(find-doc #"add \S+ and \S+")
java interoperation

LFE Erlang common lisp clojure
new


(def rnd (new java.util.Random))
(def rnd (java.util.Random.))
method


(. rnd nextFloat)
(.nextFloat rnd)
(. rnd nextInt 10)
(.nextInt rnd 10)
class method



(Math/sqrt 2)
chain




import


(import '(java.util Random))
(def rnd (Random.))
to java array


(to-array '(1 2 3))
(into-array Integer '(1 2 3))

__________________________________________ __________________________________________ __________________________________________ __________________________________________