Lisp Flavoured Erlang (LFE)

Личный сайт Go-разработчика из Казани

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))))))

Further Reading

Extra Info