Lisp Flavoured Erlang (LFE) is a functional, concurrent, general-purpose programming language and Lisp dialect (Lisp-2) built on top of Core Erlang and the Erlang Virtual Machine (BEAM).
LFE can be obtained from LFE. The classic starting point is the LFE docs.
1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2;;; 0. Syntax
3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4
5;;; General form.
6
7;; Lisp is comprised of two syntaxes, the ATOM and the S-expression.
8;; `forms` are known as grouped S-expressions.
9
108 ; an atom; it evaluates to itself
11
12:ERLANG ;Atom; evaluates to the symbol :ERLANG.
13
14t ; another atom which denotes true.
15
16(* 2 21) ; an S- expression
17
18'(8 :foo t) ;another one
19
20
21;;; Comments
22
23;; Single line comments start with a semicolon; use two for normal
24;; comments, three for section comments, and four fo file-level
25;; comments.
26
27;; Block Comment
28
29 #| comment text |#
30
31;;; Environment
32
33;; LFE is the de-facto standard.
34
35;; Libraries can be used directly from the Erlang ecosystem. Rebar3 is the build tool.
36
37;; LFE is usually developed with a text editor(preferably Emacs) and a REPL
38;; (Read Evaluate Print Loop) running at the same time. The REPL
39;; allows for interactive exploration of the program as it is "live"
40;; in the system.
41
42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
43;;; 1. Literals and Special Syntactic Rules
44;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
45
46;;; Integers
47
481234 -123 ; Regular decimal notation
49#b0 #b10101 ; Binary notation
50#0 #10101 ; Binary notation (alternative form)
51#o377 #o-111 ; Octal notation
52#d123456789 #d+123 ; Explicitly decimal notation
53#xc0ffe 0x-01 ; Hexadecimal notation
54#2r1010 #8r377 ;Notation with explicit base (up to 36)
55#\a #$ #\ä #\🐭 ;Character notation (the value is the Unicode code point of the character)
56#\x1f42d; ;Character notation with the value in hexadecimal
57
58;;; Floating point numbers
591.0 +2.0 -1.5 1.0e10 1.111e-10
60
61;;; Strings
62
63"any text between double quotes where \" and other special characters like \n can be escaped".
64; List String
65"Cat: \x1f639;" ; writing unicode in string for regular font ending with semicolon.
66
67#"This is a binary string \n with some \"escaped\" and quoted (\x1f639;) characters"
68; Binary strings are just strings but function different in the VM.
69; Other ways of writing it are: #B("a"), #"a", and #B(97).
70
71
72;;; Character escaping
73
74\b ; => Backspace
75\t ; => Tab
76\n ; => Newline
77\v ; => Vertical tab
78\f ; => Form Feed
79\r ; => Carriage Return
80\e ; => Escape
81\s ; => Space
82\d ; => Delete
83
84;;; Binaries
85;; It is used to create binaries with any contents.
86#B((#"a" binary) (#"b" binary)) ; #"ab" (Evaluated form)
87
88;;; Lists are: () or (foo bar baz)
89
90;;; Tuples are written in: #(value1 value2 ...). Empty tuple #() is also valid.
91
92;;; Maps are written as: #M(key1 value1 key2 value2 ...). Empty map #M() is also valid.
93
94;;; Symbols: Things that cannot be parsed. Eg: foo, Foo, foo-bar, :foo
95| foo | ; explicit construction of symbol by wrapping vertical bars.
96
97;;; Evaluation
98
99;; #.(... some expression ...). E.g. '#.(+ 1 1) will evaluate the (+ 1 1) while it ;; reads the expression and then be effectively '2.
100
101;; List comprehension in LFE REPL
102
103lfe> (list-comp
104 ((<- x '(0 1 2 3)))
105 (trunc (math:pow 3 x)))
106 (1 3 9 27)
107
108
109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
110;; 2. Core forms
111;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
112
113;; These forms are the same as those found in Common Lisp and Scheme.
114
115(quote e)
116(cons head tail)
117(car e)
118(cdr e)
119(list e ... )
120(tuple e ... )
121(binary seg ... )
122(map key val ...), (map-get m k), (map-set m k v ...), (map-update m k v ...)
123
124(lambda (arg ...) ...)
125 (match-lambda
126 ((arg ... ) {{(when e ...)}} ...) ; Matches clauses
127 ... )
128(let ((pat {{(when e ...)}} e)
129 ...)
130 ... )
131(let-function ((name lambda|match-lambda) ; Only define local
132 ... ) ; functions
133 ... )
134(letrec-function ((name lambda|match-lambda) ; Only define local
135 ... ) ; functions
136 ... )
137(let-macro ((name lambda-match-lambda) ; Only define local
138 ...) ; macros
139 ...)
140(progn ... )
141(if test true-expr {{false-expr}})
142(case e
143 (pat {{(when e ...)}} ...)
144 ... ))
145(receive
146 (pat {{(when e ...)}} ... )
147 ...
148 (after timeout ... ))
149(catch ... )
150(try
151 e
152 {{(case ((pat {{(when e ...)}} ... )
153 ... ))}}
154 {{(catch
155 ; Next must be tuple of length 3!
156 (((tuple type value ignore) {{(when e ...)}}
157 ... )
158 ... )}}
159 {{(after ... )}})
160
161(funcall func arg ... )
162(call mod func arg ... ) - Call to Erlang Mod:Func(Arg, ... )
163(define-module name declaration ... )
164(extend-module declaration ... ) - Define/extend module and declarations.
165(define-function name lambda|match-lambda)
166(define-macro name lambda|match-lambda) - Define functions/macros at top-level.
167
168;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
169;; 3. Macros
170;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
171
172;; Macros are part of the language and allow you to create abstractions
173;; on top of the core language and standard library that move you closer
174;; toward being able to directly express the things you want to express.
175
176;; Top-level function
177
178(defun name (arg ...) ...)
179
180;; Adding comments in functions
181
182(defun name
183 "Toplevel function with pattern-matching arguments"
184 ((argpat ...) ...)
185 ...)
186
187;; Top-level macro
188
189(defmacro name (arg ...) ...)
190(defmacro name arg ...)
191
192;; Top-level macro with pattern matching arguments
193
194(defmacro name
195 ((argpat ...) ...)
196 ...)
197
198;; Top-level macro using Scheme inspired syntax-rules format
199
200(defsyntax name
201 (pat exp)
202 ...)
203
204;;; Local macros in macro or syntax-rule format
205
206(macrolet ((name (arg ... ) ... )
207 ... )
208 ... )
209
210(syntaxlet ((name (pat exp) ...)
211 ...)
212 ...)
213
214;; Like CLISP
215
216(prog1 ...)
217(prog2 ...)
218
219;; Erlang LFE module
220
221(defmodule name ...)
222
223;; Erlang LFE record
224
225(defrecord name ...)
226
227;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
228;; 4. Patterns and Guards
229;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
230
231;; Using patterns in LFE compared to that of Erlang
232
233;; Erlang ;; LFE
234;; {ok, X} (tuple 'ok x)
235;; error 'error
236;; {yes, [X|Xs]} (tuple 'yes (cons x xs))
237;; <<34,F/float>> (binary 34 (f float))
238;; [P|Ps]=All (= (cons p ps) all)
239
240 _ ; => is don't care while pattern matching
241
242 (= pattern1 pattern2) ; => easier, better version of pattern matching
243
244;; Guards
245
246;; Whenever pattern occurs (let, case, receive, lc, etc) it can be followed by an optional
247;; guard which has the form (when test ...).
248
249(progn gtest ...) ;; => Sequence of guard tests
250(if gexpr gexpr gexpr)
251(type-test e)
252(guard-bif ...) ;; => Guard BIFs, arithmetic, boolean and comparison operators
253
254;;; REPL
255
256lfe>(set (tuple len status msg) #(8 ok "Trillian"))
257 #(8 ok "Trillian")
258lfe>msg
259 "Trillian"
260
261;;; Program illustrating use of Guards
262
263(defun right-number?
264 ((x) (when (orelse (== x 42) (== x 276709)))
265 'true)
266 ((_) 'false))
267
268;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
269;; 5. Functions
270;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
271
272;; A simple function using if.
273
274(defun max (x y)
275 "The max function."
276 (if (>= x y) x y))
277
278;; Same function using more clause
279
280(defun max
281 "The max function."
282 ((x y) (when (>= x y)) x)
283 ((x y) y))
284
285;; Same function using similar style but using local functions defined by flet or fletrec
286
287(defun foo (x y)
288 "The max function."
289 (flet ((m (a b) "Local comment."
290 (if (>= a b) a b)))
291 (m x y)))
292
293;; LFE being Lisp-2 has separate namespaces for variables and functions
294;; Both variables and function/macros are lexically scoped.
295;; Variables are bound by lambda, match-lambda and let.
296;; Functions are bound by top-level defun, flet and fletrec.
297;; Macros are bound by top-level defmacro/defsyntax and by macrolet/syntaxlet.
298
299;; (funcall func arg ...) like CL to call lambdas/match-lambdas
300;; (funs) bound to variables are used.
301
302;; separate bindings and special for apply.
303apply _F (...),
304apply _F/3 ( a1, a2, a3 )
305
306;; Cons'ing in function heads
307(defun sum (l) (sum l 0))
308 (defun sum
309 (('() total) total)
310 (((cons h t) total) (sum t (+ h total))))
311
312;; ``cons`` literal instead of constructor form
313 (defun sum (l) (sum l 0))
314 (defun sum
315 (('() total) total)
316 ((`(,h . ,t) total) (sum t (+ h total))))
317
318;; Matching records in function heads
319
320(defun handle_info
321 (('ping (= (match-state remote-pid 'undefined) state))
322 (gen_server:cast (self) 'ping)
323 `#(noreply ,state))
324 (('ping state)
325 `#(noreply ,state)))
326
327;; Receiving Messages
328 (defun universal-server ()
329 (receive
330 ((tuple 'become func)
331 (funcall func))))
332
333;; another way for receiving messages
334
335 (defun universal-server ()
336 (receive
337 (`#(become ,func)
338 (funcall func))))
339
340;; Composing a complete function for specific tasks
341
342(defun compose (f g)
343 (lambda (x)
344 (funcall f
345 (funcall g x))))
346
347(defun check ()
348 (let* ((sin-asin (compose #'sin/1 #'asin/1))
349 (expected (sin (asin 0.5)))
350 (compose-result (funcall sin-asin 0.5)))
351 (io:format "Expected answer: ~p~n" (list expected))
352 (io:format "Answer with compose: ~p~n" (list compose-result))))
353
354
355;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
356;; 6. Concurrency
357;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
358
359;; Message passing as done by Erlang's light-weight "processes".
360
361(defmodule messenger-back
362 (export (print-result 0) (send-message 2)))
363
364(defun print-result ()
365 (receive
366 ((tuple pid msg)
367 (io:format "Received message: '~s'~n" (list msg))
368 (io:format "Sending message to process ~p ...~n" (list pid))
369 (! pid (tuple msg))
370 (print-result))))
371
372(defun send-message (calling-pid msg)
373 (let ((spawned-pid (spawn 'messenger-back 'print-result ())))
374 (! spawned-pid (tuple calling-pid msg))))
375
376;; Multiple simultaneous HTTP Requests:
377
378(defun parse-args (flag)
379 "Given one or more command-line arguments, extract the passed values.
380
381 For example, if the following was passed via the command line:
382
383 $ erl -my-flag my-value-1 -my-flag my-value-2
384
385 One could then extract it in an LFE program by calling this function:
386
387 (let ((args (parse-args 'my-flag)))
388 ...
389 )
390 In this example, the value assigned to the arg variable would be a list
391 containing the values my-value-1 and my-value-2."
392 (let ((`#(ok ,data) (init:get_argument flag)))
393 (lists:merge data)))
394
395(defun get-pages ()
396 "With no argument, assume 'url parameter was passed via command line."
397 (let ((urls (parse-args 'url)))
398 (get-pages urls)))
399
400(defun get-pages (urls)
401 "Start inets and make (potentially many) HTTP requests."
402 (inets:start)
403 (plists:map
404 (lambda (x)
405 (get-page x)) urls))
406
407(defun get-page (url)
408 "Make a single HTTP request."
409 (let* ((method 'get)
410 (headers '())
411 (request-data `#(,url ,headers))
412 (http-options ())
413 (request-options '(#(sync false))))
414 (httpc:request method request-data http-options request-options)
415 (receive
416 (`#(http #(,request-id #(error ,reason)))
417 (io:format "Error: ~p~n" `(,reason)))
418 (`#(http #(,request-id ,result))
419 (io:format "Result: ~p~n" `(,result))))))