# ✓ Symbols, Bindings and Scopes in Clojure¶

In this section, we will present two important concepts in functional programming:

• Functions, and

• Scopes.

We will use Clojure to illustate the interplay between the two concepts, and how they should be used in functional programming.

## Scopes¶

### Symbols¶

A symbol is a name that is a reference to some data.

### Binding¶

An association between a symbol and data is called a symbol binding or simply binding.

We will write a symbol binding as:

$s \mapsto v$

where

• $$s$$ is a symbol

• $$v$$ is data

### Symbol dereference¶

Dereference of a symbol is to retrieve the data that the symbol is bound to.

### Scope¶

• A scope is a collection of symbol bindings.

• For a scope $$\sigma$$, its domain is the set of symbols defined in the scope.

• Given a symbol $$x\in\mathrm{dom}(\sigma)$$, the dereference of $$x$$ in $$\sigma$$ is written as $$\sigma(x)$$.

## Structures of Scopes¶

### Evaluating expressions¶

Every expression is inside some scope.

Evaluation of an expression involves:

1. Dereference all the symbols.

2. Convert all function invocations to their return values.

### Nested Scopes¶

Scopes are nested, and form a tree.

### Closure of a scope¶

Each scope $$\sigma$$ has a unique parent scope $$\mathrm{parent}(scope)$$. We arbitrarily define $$\mathrm{parent}(\sigma) = \mathrm{nil}$$ if $$\sigma$$ is the top-level scope.

Definition

The closure of a scope is a set of symbol bindings defined by all the ancestors of a scope.

$\mathrm{closure}(\mathrm{toplevel}) = \mathrm{toplevel}$

For $$\sigma\not=\mathrm{toplevel}$$, we have

$\begin{split} \mathrm{closure}(\sigma)(x) = \left\{ \begin{array}{ll} \sigma(x) & \mathrm{if}\ x\in\mathrm{dom}(\sigma) \\ \mathrm{closure}(\mathrm{parent}(\sigma))(x) & \mathrm{otherwise} \end{array} \right. \end{split}$

## Scopes in Clojure¶

### Top-level bindings¶

(def pi 3.1415)

#'user/pi

(println pi)

3.1415

nil

(def greeting (fn [message] (println "Hello," message)))

#'user/greeting

(greeting "how are you?")

Hello, how are you?

nil

(defn greeting [message] (println "Hello," message))

#'user/greeting

(greeting "how are you?")

Hello, how are you?

nil


### Sub-scope using let-form¶

Clojure allows us to create sub-scopes with additional local bindings using a let-form:

(let [ bindings... ] expression)

(let [instructor "Ken Pu"
title "Programming languages"
code "CSCI 3055U"]
;;
;; local bindings **and**
;; top-level bindings
;;
(greeting (str instructor " teaches " code ": " title))
(println "PI =" pi))

Hello, Ken Pu teaches CSCI 3055U: Programming languages
PI = 3.1415

nil


But note that the bindings for instructor, title and code are all just local to the scope inside the let-form.

instructor

Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: instructor in this context

  Util.java:   221 clojure.lang.Util/runtimeException
core.clj:  3214 clojure.core$eval/invokeStatic core.clj: 3210 clojure.core$eval/invoke
main.clj:   437 clojure.main$repl$read_eval_print__9086$fn__9089/invoke main.clj: 458 clojure.main$repl$fn__9095/invoke main.clj: 368 clojure.main$repl/doInvoke
RestFn.java:  1523 clojure.lang.RestFn/invoke
AFn.java:    22 clojure.lang.AFn/run
AFn.java:    22 clojure.lang.AFn/run


### A remark on let-forms¶

Something quite obvious but subtle about the let-form is that the definition and the evaluation of the let-form are the same.

This is called immediate evaluation. We will later explore other evaluation modes including: deferred, lazy and asynchronous evaluations.

### Sub-scopes in function body¶

Another way of creating a sub-scope is by function declaration.

The body of the function is a new scope, with additional bindings from the parameters of the function.

(fn [parameters...] <body>)

(defn area-of-circle [radius]
;;
;; inside the body, we have access to radius symbol binding
;;
;;
;;
(println (format "Area of a circle with radius %.2f is %.2f" radius area))))

#'user/area-of-circle

(area-of-circle 10.)

Area of a circle with radius 10.00 is 314.15

nil


### Deferred evaluation¶

Function bodies are evaluated in a different scope from its definition.

This is called deferred evaluation.

### Scope of function body¶

How should we determine the bindings available in the evaluation scope of the function body?

• Parameters override bindings in the closure.

Lexical Scoping Rule

Non-parameter symbols are taken from the closure of the declaration scope.

This is the default behaviour.

Dynamic Scoping Rule

Non-parameter symbols are taken from the closure of the evaluation scope.

## Lexical and Dynamic Scoping in Clojure¶

### Lexical scoping is the default¶

;;
;; Lexical scoping - easy and natural
;;


#'user/add

(add 10)

15