 In the simple core evaluation rules, symbols and lists are given special treatment. When a symbol is evaluated, it is resolved to the value of the var associated with that symbol in a namespace. A symbol containing a slash in the middle is fully qualified, meaning the part before the slash specifies the namespace. Here, dog slash cat refers to the var mapped to the symbol cat in the dog namespace. When a symbol contains no slash in the middle, it is resolved to a var of the current namespace. When the closure interpreter starts, closure.core is the current namespace, but a few functions in closure.core can switch the current namespace to something else, such that subsequent evaluation will use that namespace instead. As you might expect, when a symbol doesn't resolve to a matching name in a namespace, the evaluator throws an error. As for lists, the evaluator will first try to treat a list as a function call. This list represents a function call with three arguments. The function that gets invoked is stored in the var resolved from the symbol foo. The arguments are the number 51, the string hello, and the object stored in the var resolved from the symbol bar. There are a few things to be clear about this. When the function call is executed, if the var resolved from the symbol foo does not store a function, an exception will be thrown, because of course we can't invoke something which isn't a function. Also, closure requires a function to be invoked with its expected number of arguments. If the function of foo expects a number of arguments other than three, this call will also trigger an exception. A macro is a special kind of function that is invoked at compile time, not during execution. When the evaluator sees a list beginning with a symbol that maps to a var storing a macro, the list represents a macro call, not a regular function call. And so the evaluator will pass the arguments un-evaluated, i.e. it will pass the reader data itself. The macro call then should return reader data, which gets inserted in place of the macro call list, and then that new reader data is itself evaluated. So in this example, if foo resolves to a macro, then the symbol bar would not be resolved, but instead just itself passed as a third argument to the function. If the macro then, say, returns a list with three symbols, baz, x, y, this list gets inserted in place of the macro call, and then evaluated just like any other list. Assuming baz resolves to a regular function, that function would be called with resolved values of x and y as arguments. It's perfectly possible, however, for a macro to return a list which is itself another macro call, in which case the process repeats until a non-macro form is returned. When the symbol at the start of a list doesn't resolve to a var, before throwing an exception, the evaluator checks if it matches one of the 16 special form names. If so, the list is evaluated with the rules unique to that special form. The special form def creates and modifies var mappings in the current namespace. For this example, if the current namespace has no var mapped to the symbol Alice, this def form will create the var and give it the value 3. Otherwise, this def modifies the existing var to have the value 3. Either way, Alice in the current namespace will henceforth resolve to 3. Here, we store the result of a function call. This call to plus returns the value 12, which is then stored by def in the var mapped to the symbol James in the current namespace. Do be clear about evaluation order. The evaluator always works outside in, so the def form is evaluated first before the call to plus. However, when evaluated, a def form in turn evaluates its enclosed expression because it needs the return value. So the call to plus here finishes evaluating before the def form here finishes evaluating. The fn special form creates and returns a function object. The general form is fn parameters body, where parameters is zero or more symbols, naming the parameters of the function and body is one or more expressions that run when the function is invoked. Be clear that the function form encloses the parameters in a vector simply to distinguish its parameters from its body. Once the function object is created, there is no vector associated with the parameters. You'll see vectors use this way in a number of special forms and macros because vectors are visually distinct from lists. Closure has no equivalent of a return statement. Instead, a function call always implicitly returns the value of the last expression executed. When the fn form is evaluated, the body expressions get evaluated along with it, but the body expressions are not immediately executed because, of course, a function body should only run when the function is invoked, not when it is created. If a symbol in a function body has the same name as one of the parameters, then it resolves to that parameter. So this example fn form returns a function object which, when invoked, takes three arguments. The parameters x, y, and z are locally bound to the function, and so occurrences of those symbols in the function body resolve to the parameters. The body here calls two functions, foo with the arguments y and z, then bar with the arguments z and x. Neither foo nor bar are locally bound in the function, so those symbols must resolve to vars in the current namespace. When symbols in a function body are resolved in the current namespace, you should think of them as resolving to the vars themselves instead of to the values of those vars. When the vars change their value, the values used in the function change too. So in our example, if we modified the var of symbol foo to reference a different function, future invocations of the function created by this fn form will execute that new function instead of whatever the foo var referenced previously. Arguably, this capacity of functions to effectively change their behavior after definition undermines functional purity. The reason closure works this way, however, and the reason namespace is mapped symbols to vars instead of mapped symbols directly to other values is so that your code can always be updated at runtime. This capacity for so-called monkey patching comes in handy when debugging and also for live patching running systems. Now, if the last parameter of a function is preceded by the symbol ampersand, then the function takes a variable number of arguments. So this example here is a function that takes two or more arguments. The last parameter, the so-called rest parameter, receives every argument past the second bundled into an ordered collection. Here ampersand precedes the only parameter, so the function takes zero or more arguments. Closure is lexically scoped, so when functions are nested inside each other, the symbols are resolved inside out. Here, the interior function has its own parameter called x, so x in the interior function resolves to that parameter, but z in the interior function resolves to the z parameter of the enclosing function. Because enclosing fn forms in a def form is such a common thing to do, closure provides a macro defn that does the same thing in a more compact form. So these two expressions are equivalent. An if form takes three expressions starting with a condition expression. When an if is evaluated, the condition is evaluated, and when the condition returns any value other than false or nil, the second expression is evaluated and its value returned from the if. However, when the condition does return false or nil, the third expression is evaluated and its value returned from the if. So here, when Alice returns false or nil, the if evaluates carol and returns its value. When Alice returns anything else other than false or nil, the if evaluates bob and returns its value. As a convenience, you can omit the third expression of an if, in which case it will default to nil.