Paradigm | Multi-paradigm: concurrent, functional |
---|---|
Family | Erlang, Lisp |
Designed by | Robert Virding |
Developer | Robert Virding |
First appeared | 2008 |
Stable release | 2.1.1 / 6 January 2023 |
Typing discipline | dynamic, strong |
Implementation language | Erlang |
OS | Cross-platform |
License | Apache 2.0 |
Filename extensions | .lfe .hrl |
Website | lfe |
Influenced by | |
Erlang, Common Lisp, Maclisp, Scheme, Elixir, Clojure, Hy | |
Influenced | |
Joxa, Concurrent Schemer |
Lisp Flavored Erlang (LFE) is a functional, concurrent, garbage collected, general-purpose programming language and Lisp dialect built on Core Erlang and the Erlang virtual machine (BEAM). LFE builds on Erlang to provide a Lisp syntax for writing distributed, fault-tolerant, soft real-time, non-stop applications. LFE also extends Erlang to support metaprogramming with Lisp macros and an improved developer experience with a feature-rich read–eval–print loop (REPL). [1] LFE is actively supported on all recent releases of Erlang; the oldest version of Erlang supported is R14.
Initial work on LFE began in 2007, when Robert Virding started creating a prototype of Lisp running on Erlang. [2] This work was focused primarily on parsing and exploring what an implementation might look like. No version control system was being used at the time, so tracking exact initial dates is somewhat problematic. [2]
Virding announced the first release of LFE on the Erlang Questions mail list in March 2008. [3] This release of LFE was very limited: it did not handle recursive letrec
s, binary
s, receive
, or try
; it also did not support a Lisp shell. [4]
Initial development of LFE was done with version R12B-0 of Erlang [5] on a Dell XPS laptop. [4]
1958 | 1960 | 1965 | 1970 | 1975 | 1980 | 1985 | 1990 | 1995 | 2000 | 2005 | 2010 | 2015 | 2020 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
LISP 1, 1.5, LISP 2(abandoned) | ||||||||||||||
Maclisp | ||||||||||||||
Interlisp | ||||||||||||||
MDL | ||||||||||||||
Lisp Machine Lisp | ||||||||||||||
Scheme | R5RS | R6RS | R7RS small | |||||||||||
NIL | ||||||||||||||
ZIL (Zork Implementation Language) | ||||||||||||||
Franz Lisp | ||||||||||||||
Common Lisp | ANSI standard | |||||||||||||
Le Lisp | ||||||||||||||
MIT Scheme | ||||||||||||||
T | ||||||||||||||
Chez Scheme | ||||||||||||||
Emacs Lisp | ||||||||||||||
AutoLISP | ||||||||||||||
PicoLisp | ||||||||||||||
Gambit | ||||||||||||||
EuLisp | ||||||||||||||
ISLISP | ||||||||||||||
OpenLisp | ||||||||||||||
PLT Scheme | Racket | |||||||||||||
GNU Guile | ||||||||||||||
Visual LISP | ||||||||||||||
Clojure | ||||||||||||||
Arc | ||||||||||||||
LFE | ||||||||||||||
Hy |
Robert Virding has stated that there were several reasons why he started the LFE programming language: [2]
lfe
and lfescript
Like Lisp, LFE is an expression-oriented language. Unlike non-homoiconic programming languages, Lisps make no or little syntactic distinction between expressions and statements : all code and data are written as expressions. LFE brought homoiconicity to the Erlang VM.
In LFE, the list data type is written with its elements separated by whitespace, and surrounded by parentheses. For example, (list12'foo)
is a list whose elements are the integers 1
and 2
, and the atom foo
. These values are implicitly typed: they are respectively two integers and a Lisp-specific data type called a symbolic atom, and need not be declared as such.
As seen in the example above, LFE expressions are written as lists, using prefix notation. The first element in the list is the name of a form, i.e., a function, operator, or macro. The remainder of the list are the arguments.
The LFE-Erlang operators are used in the same way. The expression
(*(+123456)2)
evaluates to 42. Unlike functions in Erlang and LFE, arithmetic operators in Lisp are variadic (or n-ary), able to take any number of arguments.
LFE has lambda, just like Common Lisp. It also, however, has lambda-match to account for Erlang's pattern-matching abilities in anonymous function calls.
This section does not represent a complete comparison between Erlang and LFE, but should give a taste.
Erlang:
1>{Len,Status,Msg}={8,ok,"Trillian"}.{8,ok,"Trillian"}2>Msg."Trillian"
LFE:
lfe>(set(tuplelenstatusmsg)#(8ok"Trillian"))lfe>;; or with LFE literal tuple syntax:lfe>(set`#(,len,status,msg)#(8ok"Trillian"))#(8ok"Trillian")lfe>msg"Trillian"
Erlang:
1>[trunc(math:pow(3,X))||X<-[0,1,2,3]].[1,3,9,27]
LFE:
lfe>(list-comp((<-x'(0123)))(trunc(math:pow3x)))(13927)
Or idiomatic functional style:
lfe>(lists:map(lambda(x)(trunc(math:pow3x)))'(0123))(13927)
Erlang:
right_number(X)whenX==42;X==276709->true;right_number(_)->false.
LFE:
(defunright-number?((x)(when(orelse(==x42)(==x276709)))'true)((_)'false))
Erlang:
sum(L)->sum(L,0).sum([],Total)->Total;sum([H|T],Total)->sum(T,H+Total).
LFE:
(defunsum(l)(suml0))(defunsum(('()total)total)(((consht)total)(sumt(+htotal))))
or using a ``cons`` literal instead of the constructor form:
(defunsum(l)(suml0))(defunsum(('()total)total)((`(,h.,t)total)(sumt(+htotal))))
Erlang:
handle_info(ping,#state{remote_pid=undefined}=State)->gen_server:cast(self(),ping),{noreply,State};handle_info(ping,State)->{noreply,State};
LFE:
(defunhandle_info(('ping(=(match-stateremote-pid'undefined)state))(gen_server:cast(self)'ping)`#(noreply,state))(('pingstate)`#(noreply,state)))
Erlang:
universal_server()->receive{become,Func}->Func()end.
LFE:
(defununiversal-server()(receive((tuple'becomefunc)(funcallfunc))))
or:
(defununiversal-server()(receive(`#(become,func)(funcallfunc))))
Calls to Erlang functions take the form (<module>:<function> <arg1> ... <argn>):
(io:format"Hello, World!")
Using recursion to define the Ackermann function:
(defunackermann((0n)(+n1))((m0)(ackermann(-m1)1))((mn)(ackermann(-m1)(ackermannm(-n1)))))
Composing functions:
(defuncompose(fg)(lambda(x)(funcallf(funcallgx))))(defuncheck()(let*((sin-asin(compose#'sin/1#'asin/1))(expected(sin(asin0.5)))(compose-result(funcallsin-asin0.5)))(io:format"Expected answer: ~p~n"(listexpected))(io:format"Answer with compose: ~p~n"(listcompose-result))))
Message-passing with Erlang's light-weight "processes":
(defmodulemessenger-back(export(print-result0)(send-message2)))(defunprint-result()(receive((tuplepidmsg)(io:format"Received message: '~s'~n"(listmsg))(io:format"Sending message to process ~p ...~n"(listpid))(!pid(tuplemsg))(print-result))))(defunsend-message(calling-pidmsg)(let((spawned-pid(spawn'messenger-back'print-result())))(!spawned-pid(tuplecalling-pidmsg))))
Multiple simultaneous HTTP requests:
(defunparse-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_argumentflag)))(lists:mergedata)))(defunget-pages()"With no argument, assume 'url parameter was passed via command line."(let((urls(parse-args'url)))(get-pagesurls)))(defunget-pages(urls)"Start inets and make (potentially many) HTTP requests."(inets:start)(plists:map(lambda(x)(get-pagex))urls))(defunget-page(url)"Make a single HTTP request."(let*((method'get)(headers'())(request-data`#(,url,headers))(http-options())(request-options'(#(syncfalse))))(httpc:requestmethodrequest-datahttp-optionsrequest-options)(receive(`#(http#(,request-id#(error,reason)))(io:format"Error: ~p~n"`(,reason)))(`#(http#(,request-id,result))(io:format"Result: ~p~n"`(,result))))))
Common Lisp (CL) is a dialect of the Lisp programming language, published in American National Standards Institute (ANSI) standard document ANSI INCITS 226-1994 (S20018). The Common Lisp HyperSpec, a hyperlinked HTML version, has been derived from the ANSI Common Lisp standard.
Erlang is a general-purpose, concurrent, functional high-level programming language, and a garbage-collected runtime system. The term Erlang is used interchangeably with Erlang/OTP, or Open Telecom Platform (OTP), which consists of the Erlang runtime system, several ready-to-use components (OTP) mainly written in Erlang, and a set of design principles for Erlang programs.
Lisp is a family of programming languages with a long history and a distinctive, fully parenthesized prefix notation. Originally specified in 1960, Lisp is the second-oldest high-level programming language still in common use, after Fortran. Lisp has changed since its early days, and many dialects have existed over its history. Today, the best-known general-purpose Lisp dialects are Common Lisp, Scheme, Racket and Clojure.
Scheme is a dialect of the Lisp family of programming languages. Scheme was created during the 1970s at the MIT Computer Science and Artificial Intelligence Laboratory and released by its developers, Guy L. Steele and Gerald Jay Sussman, via a series of memos now known as the Lambda Papers. It was the first dialect of Lisp to choose lexical scope and the first to require implementations to perform tail-call optimization, giving stronger support for functional programming and associated techniques such as recursive algorithms. It was also one of the first programming languages to support first-class continuations. It had a significant influence on the effort that led to the development of Common Lisp.
In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function together with an environment. The environment is a mapping associating each free variable of the function with the value or reference to which the name was bound when the closure was created. Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.
F# is a functional-first, general-purpose, strongly typed, multi-paradigm programming language that encompasses functional, imperative, and object-oriented programming methods. It is most often used as a cross-platform Common Language Infrastructure (CLI) language on .NET, but can also generate JavaScript and graphics processing unit (GPU) code.
In mathematics and computer science, a higher-order function (HOF) is a function that does at least one of the following:
In computer programming, a function object is a construct allowing an object to be invoked or called as if it were an ordinary function, usually with the same syntax. Function objects are often called functors.
In computer programming, a callback or callback function is any reference to executable code that is passed as an argument to another piece of code; that code is expected to call back (execute) the callback function as part of its job. This execution may be immediate as in a synchronous callback, or it might happen at a later point in time as in an asynchronous callback. They are also called blocking and non-blocking.
In computer science, a programming language is said to have first-class functions if it treats functions as first-class citizens. This means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures. Some programming language theorists require support for anonymous functions as well. In languages with first-class functions, the names of functions do not have any special status; they are treated like ordinary variables with a function type. The term was coined by Christopher Strachey in the context of "functions as first-class citizens" in the mid-1960s.
In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments. Support for variadic functions differs widely among programming languages.
In computer science, function composition is an act or mechanism to combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole.
In computer programming, homoiconicity is a property of some programming languages. A language is homoiconic if a program written in it can be manipulated as data using the language, and thus the program's internal representation can be inferred just by reading the program itself. This property is often summarized by saying that the language treats code as data.
The syntax of the Python programming language is the set of rules that defines how a Python program will be written and interpreted. The Python language has many similarities to Perl, C, and Java. However, there are some definite differences between the languages.
C++11 is a version of the ISO/IEC 14882 standard for the C++ programming language. C++11 replaced the prior version of the C++ standard, called C++03, and was later replaced by C++14. The name follows the tradition of naming language versions by the publication year of the specification, though it was formerly named C++0x because it was expected to be published before 2010.
In computer programming, an anonymous function is a function definition that is not bound to an identifier. Anonymous functions are often arguments being passed to higher-order functions or used for constructing the result of a higher-order function that needs to return a function. If the function is only used once, or a limited number of times, an anonymous function may be syntactically lighter than using a named function. Anonymous functions are ubiquitous in functional programming languages and other languages with first-class functions, where they fulfil the same role for the function type as literals do for other data types.
In mathematics and computer science, apply is a function that applies a function to arguments. It is central to programming languages derived from lambda calculus, such as LISP and Scheme, and also in functional languages. It has a role in the study of the denotational semantics of computer programs, because it is a continuous function on complete partial orders. Apply is also a continuous function in homotopy theory, and, indeed underpins the entire theory: it allows a homotopy deformation to be viewed as a continuous path in the space of functions. Likewise, valid mutations (refactorings) of computer programs can be seen as those that are "continuous" in the Scott topology.
Clojure is a dynamic and functional dialect of the Lisp programming language on the Java platform.
In computer programming, variadic templates are templates that take a variable number of arguments.
In mathematics, Boole's rule, named after George Boole, is a method of numerical integration. It is often known as Bode's rule, due to a typographical error that propagated from Abramowitz and Stegun.