Search code examples
pythonoperator-precedencesicp

Why is the environment diagram in the following Python code inconsistent with its execution order?


When I was studying the SICP course, the execution order of the following code in the environment diagram is inconsistent with what I expected.

def add_one(x):
    y = x+1
    return y
def square(x):
    return x*x
    
square(add_one(9))

I am expecting the following evaluation order:

  1. Evaluate the operator square. A function value is returned.
  2. Evaluate the operand. Now we have to evaluate another expression add_one(9).
  3. Evaluate the operator add_one. A function value is returned.
  4. Evaluate the operand 9. Now the expression add_one(9) returns 10
  5. The whole expression square(add_one(9)) return 100.

However, I run the code on this website It told me that the add_one frame created before the square, which is opposite of what I expected. Is it right?

I asked ChatGPT about this question. It gives the answer that this is because in Python, the priority of function calls is higher than that of operators.


Solution

  • Your order is evaluation is mostly correct. However you are confusing "creation of the frame" with "evaluation of the frame".

    To call a function, Python evaluates the function and its arguments. This order appears to be left-to-right, but I cannot find anything in the documentation that guarantees this. [Other StackOverflow answers say it is guaranteed left-to-right, so maybe there's something I'm missing.]

    Only after the function and its arguments are evaluated is the function call actually made. This is when the frame is created.

    Try the following code. It will give you a clearer idea of evaluation order.

    def add_onex():
        print("Calling add_one")
        return add_one
    
    def squarex():
        print("Evaluating square")
        return square
    
    def add_one(x):
        print("Calling add one")
        y = x + 1
        return y
    
    def square(x):
        print("Calling square")
        return x * x
    
    result = squarex()(add_onex()(9))
    
    

    Obviously, in most cases, the evaluation of a function is a simple name lookup. But in cases where evaluation of the function is not trivial, you can see that this evaluation happens before the arguments are evaluated.