1

I'm looking for the technical answer answer here. How is Clojure interpreting these symbols? My current working understanding is that the opening paren '(' is a kind of call that calls the succeeding operator on the operands while the closing paren ')' is a terminate that wraps up the previous evaluation and returns the final value generated (whether function or value).

Any and all details on the truth here would be appreciated. I'm looking to go deep here as well as seeing/knowing every level of abstraction along the way. It bugs me to know that I may have some imaginal thinking going on currently.

Sirgeorge
  • 127
  • 7

3 Answers3

4
(foo x1 x2)

is the syntax for calling a special form or var (function or macro).

So the compiler will analyze the form (foo x1 x2) and will check if foo is a special form (if, try, let*, etc.) and if not, the symbol will be resolved to a var in the context of the current namespace. If that var is macro, then macroexpansion will happen, else the call will be treated as a normal function call.

To prevent treating (foo x1 x2) as a function call you can quote the expression: '(foo x1 x2) and then it will just remain a list of symbols.

More info:

Michiel Borkent
  • 34,228
  • 15
  • 86
  • 149
4

You are trying to do too much at once when you say

'(' is a kind of call that calls the succeeding operator on the operands while the closing paren ')' is a terminate that wraps up the previous evaluation and returns the final value generated

The Clojure evaluation model does not assign semantics to characters directly. Instead, evaluation of a Clojure program goes through two broad phases:

  • First, read the characters in the source file according to the language's lexical rules, yielding a Clojure data structure
  • Second, evaluate that data structure, according to the language's evaluation rules, yielding a value

So when we write an expression like (+ (* 4 5) 2), what happens? The reader matches up parentheses to create lists, and yields as its result a list of three elements: the symbol +, another list (containing the symbol * and the numbers 4 and 5), and the number 2.

Next we move to evaluate that expression. Notice, crucially, that at this point there is no trace of parentheses. The textual source of the program is no longer material. We're evaluating a list. Of course, if we printed that list, conventionally we would surround it with parentheses, but that does not concern the evaluator. How do we evaluate this list? Well, the evaluation rule for lists is to, first, evaluate each of the components, and then invoke the first component as a function, passing the remaining components as arguments1. So our list of pending tasks is:

  1. Evaluate +
  2. Evaluate (* 4 5)
  3. Evaluate 2
  4. Invoke the result of (1), passing the results of (2) and (3) as arguments

(1), of course, evaluates to the addition function, (2) evaluates (in a similar manner) to the number 20, and (3) evaluates to the number 2 (since numbers evaluate to themselves). Thus, (4) becomes "Invoke the addition function, passing the numbers 20 and 2 as arguments". Of course, the final result is 22.


1 The rule is actually more complicated than this, because of macros, but for functions this suffices.

amalloy
  • 89,153
  • 8
  • 140
  • 205
0

The other 2 answers are great. The simple summary is:

foo( x1, x2 )  // Java function call

(foo x1  x2)   // Clojure function call

In both cases, the compiler will evaluate any nested function calls in x1 or x2 before calling foo on the resulting values.

Alan Thompson
  • 29,276
  • 6
  • 41
  • 48