In the REPL
To set a variable in the LFE REPL, use the
lfe> (set answer 42) 42
In the language itself, LFE doesn't support global variables -- a valuable
feature inherited from Erlang. However, in order for a REPL experience to be
useful, an environment must be maintained in which the user may write
expressions and then refer to them later. This environment is essentially a
mechanism for global state in the context of a single user running a single
Erlang VM. If we set a variable called
answer, that variable will be available
to us as long as the REPL process continues or until we reset the environment.
Setting another value with the same variable name is allowed: it merely replaces the assignment in the current REPL environment:
lfe> (set answer "forty-two") "forty-two"
With a variable assigned with
set it may be used at any time in the REPL
environment where it was defined:
lfe> (++ "The answer is " answer) "The answer is forty-two"
Attempting to use a variable that has not been defined results in an error:
lfe> (++ "The question was " question) ** exception error: symbol question is unbound in lfe_eval:eval_error/1 (src/lfe_eval.erl, line 1292) in lists:map/2 (lists.erl, line 1243) in lists:map/2 (lists.erl, line 1243)
If you don't have any need to update the environment with data that you only
need for a specific calculation, you may use the
lfe> (let ((short-lived-value (* 2 (+ 1 2 3 4 5 6)))) lfe> (io:format "The answer is ~p~n" `(,short-lived-value))) The answer is 42 ok
Let's make sure that variable wasn't saved to our environment:
lfe> short-lived-value ** exception error: symbol short-lived-value is unbound
The lexical scope for the
short-lived-value is within the
let only and is
not available outside of that scope.
In Functions and Macros
Within functions, variables are lexically scoped and bound with
let*. One may also define lexically scoped functions inside other fucntions,
and this is done with
fletrec (the latter required for defining
recursive functions inside another function). These will be covered in detail
later in the book.
let used above in the REPL; the same applies inside functions:
(defun display-answer () (let ((answer (* 2 (+ 1 2 3 4 5 6)))) (io:format "The answer is ~p~n" `(,answer))))
This is a straight-forward case of assignment; but what if we needed to assign
a varaible that depended upon another variable. Using
let, you'd have to do
(defun display-answer () (let ((data '(1 2 3 4 5 6))) (let ((answer (* 2 (lists:sum data)))) (io:format "The answer is ~p~n" `(,answer)))))
However, as with other Lisps, LFE provides a convenience macro for this:
Here's how it's used:
(defun display-answer () (let* ((data '(1 2 3 4 5 6)) (answer (* 2 (lists:sum data)))) (io:format "The answer is ~p~n" `(,answer))))
Lexical scoping helps one isolate unrelated data or calculations, even in the
same function: multiple
let* blocks may be declared in a function
and none of the bound variables in one block will be available to another block.
Attempting to do so will result in an error.
In LFE, one cannot bind variables at the module-level, only functions and
macros. This is part of the "no global variables" philosophy (and practice!) of
Erlang and LFE. Module-level bindings are done with
defun for functions and
defmacro for macros. The creation of modules,
functions, and macros will all be
covered in detail later in the book.
One shadows a variable in one scope when, at a higher scope, that variable was also defined. Here's an annotated example:
(defun shadow-demo () (let ((a 5)) (io:format "~p~n" `(,a)) ; prints 5 (let ((a 'foo)) ; 'a' here shadows 'a' in the previous scope (io:format "~p~n" `(,a))) ; prints foo (io:format "~p~n" `(,a))) ; prints 5; the shadow binding is out of scope (let ((a 42)) (io:format "~p~n" `(,a)))) ; prints 42 - new scope, no shadowing
Shadowing also may occur at the module-level with the definition of functions, and the shadowing could be of functions at one of several levels. Here's a run-down on function shadowing in modules, from the highest or "outermost" to the lowest or "innermost":
- Predefined Erlang built-in functions (BIFs) may be shadowed by any of the following
- Predefined LFE BIFs may be shadowed by any of the following
- Module imports may shadow any of the above via aliasing
- Functions defined in a module may shadow any of the above
- Functions defined inside a function (e.g., via
fletrec) may shadow any of the above
Note that to shadow functions in LFE, functions must match both in name as well as arity (number of arguments).
hd Erlang BIF returns the "head" of a list (the first item). Here's an
example of shadowing it in the REPL. Here's the BIF at work:
lfe> (hd '(a b c d e)) a
Next, paste this into the REPL:
(defun hd (_) ;; part of the pun here is that the same function in Lisp is called 'car' "My other car is The Heart of Gold.")
hd function takes one argument (a list), so our function also needs to
take one. However, since we don't do anything with that, we use the "don't care"
Now let's call
lfe> (hd '(a b c d e)) "My other car is The Heart of Gold."
Note that, like many other Lisps, LFE has the
car function, but since this is
a core form, it can't be shadowed (see the next section).
Shadowing does not apply to the supported LFE core forms. It may appear that your code is shadowing those forms, but the compiler will always use the core meaning and never an alternative. It does this silently, without warning -- so take care and do not be surprised!
Core LFE forms are never be shadowed.