The let expressions in scheme are in fact macros. They can be expressed with lambdas. A simple let might look like this:
(let ((x 1) (y 2))
(+ x y))
It will return 3 as the value of the last expression of the let body is returned. As you can see, a let-expression is actually executing something. If we translate this part of code with lambdas, we'd get something like this:
((lambda (x y) (+ x y)) 1 2)
Here we can see that we're calling the anonymous lambda with 1 and 2 directly. So the result in this case is also 3.
With that in mind, we understand that a let expression is composed of 2 parts. It has parameters and a body like a lambda has, but the difference is that let expression are called after right after their evaluation.
To explain how a let expression work from an abstract to concrete view, it would look like this.
(let params body ...)
(let (param1 param2 ...) body ...)
(let ((p1 val1) (p2 val2) ...) body ...)
The parameters are a list of pair of (name value)
to be used in the body of the let
.
Why use let expression?
Let expressions are particularly useful to store variables in a method just like initializations of variable in c like languages. It is favorable to the use of define
because out of the let expression, the variables are gone... Using a define is actually adding a variable to the current execution environment. Variables that are added to the global environment cannot be removed. Let expression are safe to use anywhere. It can also be used to ghost variables without touching the parent scopes.
For example:
(let ((x 1))
(let ((x 2) (y x))
(display x)
(display y))
(display x))
It will print:
2
1
1
In this case, x
is defined with 1, then ghosted by the x
in the second let
with the value 2
. The variable y
is initiated with the value x
of the parent scope. After the inner let
expression is executed, it display the initial value of x
with 1. The inner let
expression didn't change the value of the parent scope.
Whenever you need to initialize variables, you should be using let expressions like this:
(let (
(user (get-current-user session))
(db (get-current-db session))
(ids (get-active-ids session))
)
(mark-task-completed db user ids)
(change-last-logged db user)
(db-commit db))
Here in this example, the variables are initialized and used multiple time in the code block. And when the let expression is finished, the variables are automatically freed as they are not necessary anymore.