summaryrefslogtreecommitdiffhomepage
path: root/lfe.html.markdown
blob: 071181824abeb9654351cd2b4772124c18224dc7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
---
language: "Lisp Flavoured Erlang (LFE)"
filename: lispflavourederlang.lfe
contributors:
  - ["Pratik Karki", "https://github.com/prertik"]
---

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](https://github.com/rvirding/lfe).
The classic starting point is the [LFE docs](http://docs.lfe.io).

```lisp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 0. Syntax
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; General form.

;; Lisp is comprised of two syntaxes, the ATOM and the S-expression.
;; `forms` are known as grouped S-expressions.

8  ; an atom; it evaluates to itself

:ERLANG ;Atom; evaluates to the symbol :ERLANG.

t  ; another atom which denotes true.

(* 2 21) ; an S- expression

'(8 :foo t)  ;another one


;;; Comments

;; Single line comments start with a semicolon; use two for normal
;; comments, three for section comments, and four fo file-level
;; comments.

;; Block Comment

   #| comment text |#

;;; Environment

;; LFE is the de-facto standard.

;; Libraries can be used directly from the Erlang ecosystem. Rebar3 is the build tool.

;; LFE is usually developed with a text editor(preferably Emacs) and a REPL
;; (Read Evaluate Print Loop) running at the same time. The REPL
;; allows for interactive exploration of the program as it is "live"
;; in the system.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 1. Literals and Special Syntactic Rules
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Integers

1234 -123           ; Regular decimal notation
#b0 #b10101         ; Binary notation
#0 #10101           ; Binary notation (alternative form)
#o377 #o-111        ; Octal notation
#d123456789 #d+123  ; Explicitly decimal notation
#xc0ffe 0x-01       ; Hexadecimal notation
#2r1010 #8r377      ;Notation with explicit base (up to 36)
#\a #$ #\ä #\🐭     ;Character notation (the value is the Unicode code point of the character)
#\x1f42d;           ;Character notation with the value in hexadecimal

;;; Floating point numbers
1.0 +2.0 -1.5 1.0e10 1.111e-10

;;; Strings

"any text between double quotes where \" and other special characters like \n can be escaped".
; List String
"Cat: \x1f639;" ; writing unicode in string for regular font ending with semicolon.

#"This is a binary string \n with some \"escaped\" and quoted (\x1f639;) characters"
; Binary strings are just strings but function different in the VM.
; Other ways of writing it are:  #B("a"), #"a", and #B(97).


;;; Character escaping

\b  ; => Backspace
\t  ; => Tab
\n  ; => Newline
\v  ; => Vertical tab
\f  ; => Form Feed
\r  ; => Carriage Return
\e  ; => Escape
\s  ; => Space
\d  ; => Delete

;;; Binaries
;; It is used to create binaries with any contents.
#B((#"a" binary) (#"b" binary))                 ; #"ab" (Evaluated form)

;;; Lists are: () or (foo bar baz)

;;; Tuples are written in: #(value1 value2 ...). Empty tuple #() is also valid.

;;; Maps are written as: #M(key1 value1 key2 value2 ...). Empty map #M() is also valid.

;;; Symbols: Things that cannot be parsed. Eg: foo, Foo, foo-bar, :foo
| foo | ; explicit construction of symbol by wrapping vertical bars.

;;; Evaluation

;; #.(... some expression ...). E.g. '#.(+ 1 1) will evaluate the (+ 1 1) while it            ;; reads the expression and then be effectively '2.

;; List comprehension in LFE REPL

lfe> (list-comp
          ((<- x '(0 1 2 3)))
          (trunc (math:pow 3 x)))
       (1 3 9 27)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 2. Core forms
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; These forms are the same as those found in Common Lisp and Scheme.

(quote e)
(cons head tail)
(car e)
(cdr e)
(list e ... )
(tuple e ... )
(binary seg ... )
(map key val ...), (map-get m k), (map-set m k v ...), (map-update m k v ...)

(lambda (arg ...) ...)
  (match-lambda
    ((arg ... ) {{(when e ...)}} ...) ; Matches clauses
    ... )
(let ((pat {{(when e ...)}} e)
      ...)
  ... )
(let-function ((name lambda|match-lambda) ; Only define local
               ... )                      ; functions
  ... )
(letrec-function ((name lambda|match-lambda) ; Only define local
                  ... )                      ; functions
  ... )
(let-macro ((name lambda-match-lambda) ; Only define local
            ...)                       ; macros
  ...)
(progn ... )
(if test true-expr {{false-expr}})
(case e
  (pat {{(when e ...)}} ...)
   ... ))
(receive
  (pat {{(when e ...)}} ... )
  ...
  (after timeout ... ))
(catch ... )
(try
  e
  {{(case ((pat {{(when e ...)}} ... )
          ... ))}}
  {{(catch
     ; Next must be tuple of length 3!
     (((tuple type value ignore) {{(when e ...)}}
      ... )
     ... )}}
  {{(after ... )}})

(funcall func arg ... )
(call mod func arg ... ) - Call to Erlang Mod:Func(Arg, ... )
(define-module name declaration ... )
(extend-module declaration ... ) - Define/extend module and declarations.
(define-function name lambda|match-lambda)
(define-macro name lambda|match-lambda) - Define functions/macros at top-level.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. Macros
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Macros are part of the language and allow you to create abstractions
;; on top of the core language and standard library that move you closer
;; toward being able to directly express the things you want to express.

;; Top-level function

(defun name (arg ...) ...)

;; Adding comments in functions

(defun name
  "Toplevel function with pattern-matching arguments"
  ((argpat ...) ...)
  ...)

;; Top-level macro

(defmacro name (arg ...) ...)
(defmacro name arg ...)

;; Top-level macro with pattern matching arguments

(defmacro name
  ((argpat ...) ...)
  ...)

;; Top-level macro using Scheme inspired syntax-rules format

(defsyntax name
  (pat exp)
  ...)

;;; Local macros in macro or syntax-rule format

(macrolet ((name (arg ... ) ... )
            ... )
    ... )

(syntaxlet ((name (pat exp) ...)
             ...)
 ...)

;; Like CLISP

(prog1 ...)
(prog2 ...)

;; Erlang LFE module

(defmodule name ...)

;; Erlang LFE record

(defrecord name ...)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 4. Patterns and Guards
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Using patterns in LFE compared to that of Erlang

;; Erlang                     ;; LFE
;; {ok, X}                       (tuple 'ok x)
;; error                         'error
;; {yes, [X|Xs]}                 (tuple 'yes (cons x xs))
;; <<34,F/float>>                (binary 34 (f float))
;; [P|Ps]=All                    (= (cons p ps) all)

  _    ; => is don't care while pattern matching

  (= pattern1 pattern2)     ; => easier, better version of pattern matching

;; Guards

;; Whenever pattern occurs (let, case, receive, lc, etc) it can be followed by an optional
;; guard which has the form (when test ...).

(progn gtest ...)             ;; => Sequence of guard tests
(if gexpr gexpr gexpr)
(type-test e)
(guard-bif ...)               ;; => Guard BIFs, arithmetic, boolean and comparison operators

;;; REPL

lfe>(set (tuple len status msg) #(8 ok "Trillian"))
    #(8 ok "Trillian")
lfe>msg
    "Trillian"

;;; Program illustrating use of Guards

(defun right-number?
        ((x) (when (orelse (== x 42) (== x 276709)))
          'true)
        ((_) 'false))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 5. Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; A simple function using if.

(defun max (x y)
  "The max function."
  (if (>= x y) x y))

;; Same function using more clause

(defun max
  "The max function."
  ((x y) (when (>= x y)) x)
  ((x y) y))

;; Same function using similar style but using local functions defined by flet or fletrec

(defun foo (x y)
  "The max function."
  (flet ((m (a b) "Local comment."
            (if (>= a b) a b)))
    (m x y)))

;; LFE being Lisp-2 has separate namespaces for variables and functions
;; Both variables and function/macros are lexically scoped.
;; Variables are bound by lambda, match-lambda and let.
;; Functions are bound by top-level defun, flet and fletrec.
;; Macros are bound by top-level defmacro/defsyntax and by macrolet/syntaxlet.

;; (funcall func arg ...) like CL to call lambdas/match-lambdas
;; (funs) bound to variables are used.

;; separate bindings and special for apply.
apply _F (...),
apply _F/3 ( a1, a2, a3 )

;; Cons'ing in function heads
(defun sum (l) (sum l 0))
  (defun sum
    (('() total) total)
    (((cons h t) total) (sum t (+ h total))))

;; ``cons`` literal instead of constructor form
      (defun sum (l) (sum l 0))
      (defun sum
        (('() total) total)
        ((`(,h . ,t) total) (sum t (+ h total))))

;; Matching records in function heads

(defun handle_info
  (('ping (= (match-state remote-pid 'undefined) state))
    (gen_server:cast (self) 'ping)
    `#(noreply ,state))
  (('ping state)
   `#(noreply ,state)))

;; Receiving Messages
      (defun universal-server ()
        (receive
          ((tuple 'become func)
           (funcall func))))

;; another way for receiving messages

 (defun universal-server ()
        (receive
          (`#(become ,func)
            (funcall func))))

;; Composing a complete function for specific tasks

(defun compose (f g)
  (lambda (x)
   (funcall f
     (funcall g x))))

(defun check ()
  (let* ((sin-asin (compose #'sin/1 #'asin/1))
         (expected (sin (asin 0.5)))
         (compose-result (funcall sin-asin 0.5)))
    (io:format "Expected answer: ~p~n" (list expected))
    (io:format "Answer with compose: ~p~n" (list compose-result))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 6. Concurrency
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Message passing as done by Erlang's light-weight "processes".

(defmodule messenger-back
 (export (print-result 0) (send-message 2)))

(defun print-result ()
  (receive
    ((tuple pid msg)
      (io:format "Received message: '~s'~n" (list msg))
      (io:format "Sending message to process ~p ...~n" (list pid))
      (! pid (tuple msg))
      (print-result))))

(defun send-message (calling-pid msg)
  (let ((spawned-pid (spawn 'messenger-back 'print-result ())))
    (! spawned-pid (tuple calling-pid msg))))

;; Multiple simultaneous HTTP Requests:

(defun parse-args (flag)
  "Given one or more command-line arguments, extract the passed values.

  For example, if the following was passed via the command line:

    $ erl -my-flag my-value-1 -my-flag my-value-2

  One could then extract it in an LFE program by calling this function:

    (let ((args (parse-args 'my-flag)))
      ...
      )
  In this example, the value assigned to the arg variable would be a list
  containing the values my-value-1 and my-value-2."
  (let ((`#(ok ,data) (init:get_argument flag)))
    (lists:merge data)))

(defun get-pages ()
  "With no argument, assume 'url parameter was passed via command line."
  (let ((urls (parse-args 'url)))
    (get-pages urls)))

(defun get-pages (urls)
  "Start inets and make (potentially many) HTTP requests."
  (inets:start)
  (plists:map
    (lambda (x)
      (get-page x)) urls))

(defun get-page (url)
  "Make a single HTTP request."
  (let* ((method 'get)
         (headers '())
         (request-data `#(,url ,headers))
         (http-options ())
         (request-options '(#(sync false))))
    (httpc:request method request-data http-options request-options)
    (receive
      (`#(http #(,request-id #(error ,reason)))
       (io:format "Error: ~p~n" `(,reason)))
      (`#(http #(,request-id ,result))
       (io:format "Result: ~p~n" `(,result))))))
```

## Further Reading

* [LFE DOCS](http://docs.lfe.io)
* [LFE GitBook](https://lfe.gitbooks.io/reference-guide/index.html)
* [LFE Wiki](https://en.wikipedia.org/wiki/LFE_(programming_language))

## Extra Info

* [LFE PDF](http://www.erlang-factory.com/upload/presentations/61/Robertvirding-LispFlavouredErlang.pdf)
* [LFE mail](https://groups.google.com/d/msg/lisp-flavoured-erlang/XA5HeLbQQDk/TUHabZCHXB0J)