Reputation: 18552
In Lisp (I am still learning Lisp with SBCL), local variables are declared with let
, and the scope is only within that expression. Why is that? Unlike in other imperative languages like C/C++/Java..., where we can freely use local variables anywhere in its function scope.
Upvotes: 1
Views: 258
Reputation: 58578
The difference between let
and that other style you are referring to is purely one of syntactic sugar. In a language where you can introduce a new variable "anywhere", there are still nested scopes, only they are flattened in the surface syntax. For instance
{
x = 3;
x++;
y = 4;
print "hi";
z = 42;
}
There isn't just one scope here, but different scopes. There is a scope for x
which begins with x = 3
. Then there is a scope for y
which begins with y = 4
. If you refer to y
before that scope, you have an error.
This kind of thing is left out of Lisp because it adds a lot of effort to the development of code which processes code. The compilers for such a language have to analyze that code and recover the abstract syntax tree structure which shows the nested let
structure, so that the remaining passes of the compiler don't have to keep re-analyzing the code to rediscover that information.
In Lisp, we write directly in that abstract syntax tree structure and that is that. This makes the language uniform. Everything is a form, and if it's a compound form it has an operator in the leftmost position which determines the meaning of the rest of that form, in a context-free way.
For example see this recent question: find free variables in lambda expression . Finding the free variables in a lambda expression is a lot easier if they are set off by binding special forms such as let
, or by macros which expand to a small repertoire of such special forms.
Also note that the functional style, though not enforced by Lisp in anyway, is more prevalent than in those kinds of languages (note how I have some imperative statements among the variable definitions in the fictitious "{} language".) If you write code in which there are no imperative statements, then you don't really run into this.
In functional code, you can have successive binding in the form of let*
:
(let* ((x 1) x = 1;
(y (1+ x)) y = x + 1;
(z (/ x y)) z = x / y;
(sqrt z)) return sqrt(z);
The later initializations in the let*
can refer to the earlier bindings.
Upvotes: 3
Reputation:
Just another little insight into what let is. It is basically an application of an anonymous function "spelled backwards".
I will use JavaScript for the illustration because it is more C-like language and it illustrates the concept quite well.
(function(variableA, variableB){
console.log("variableA = " + variableA);
console.log("variableA * variableB = " + variableA * variableB);})(6, 7);
Now, let's name the parts: from function
to ;}
is the function definition. (definition)(arguments)
is the application. Let expression does essentially the same thing, i.e. it invokes an anonymous function with arguments, which you use inside that function as variables. So, if you consider the previous example, rewriting it in let form would make something like:
(let(variableA = 6, variableB = 7){
console.log("variableA = " + variableA);
console.log("variableA * variableB = " + variableA * variableB);});
(JavaScript doens't support let, yet, so above isn't a working code example, but it should be an illustration)
You should also note that it is not exactly that simple. Because in more complex cases you may want to reference one of the arguments when constructing another - and then you would use (let* ...)
, or you would want to use functions as arguments of this expression, and then you would use (flet ...)
or (labels ...)
. But the general idea is the same.
Upvotes: 6
Reputation: 139261
Lisp has a construct for introducing local variables. It sets up a scope. You can use it wherever a form is allowed. The two main constructs for that are LET and LET*. Ait allows us to have local variables defined independently from functions. We don't need to introduce a function when we want local variables and we use it to have local variables a minimal scope.
Note also that DEFUN allows you to declare local variables: it allows &AUX in the parameter list to introduce them.
Upvotes: 4