Tazinho / Advanced-R-Solutions

Set of solutions for the Advanced R programming book
https://advanced-r-solutions.rbind.io/
290 stars 118 forks source link

Recheck Diagram for Function Environment Exercise (7.4.5.2) #244

Closed arashHaratian closed 3 years ago

arashHaratian commented 4 years ago

I think the diagram in answer to the question 7.4.5.2 should shows the execution environments of functions. so f2 name must be in execution environment of function f1() and binds f2(), f3 name must be in execution environment of function f2() and binds f3(). Is that right?

henningsway commented 3 years ago

Hey @arashHaratian, thank you for raising this issue.

It is my understanding, that functions capture their environments at creation time ("closure"). From the logic of the computation one could say that f3 is contained in f2 and f2 is contained in f1 and I think this is expressed in the "binding scheme". I did just try to clarify the explanation in https://github.com/Tazinho/Advanced-R-Solutions/commit/c28a859d8d51ba72e63404736a327c41f3de3838.

I based the diagram on the first diagram on https://adv-r.hadley.nz/environments.html#function-environments where f binds the global environment in a similar way.

@hadley could you check this exercise (https://advanced-r-solutions.rbind.io/environments.html#special-environments, Q2) once again?

arashHaratian commented 3 years ago

thanks for your response to this issue. my problem is that f2 is not an argument for the f1(). (if your scheme follows the conventions of the original book)

image

so, I think the diagram is something like this:

image

(sorry for the awful photo 😅)

henningsway commented 3 years ago

I see your point: x1 is an argument to f1, while f2 is part of the functions body (which is not drawn in Adv R). In the diagram both look the same. Also the value for x1 is not captured when the function is created, but passed at execution time.

For this exercise it's probably necessary to draw the function body's content and I would argue that both function body and function arguments are ultimately part of the same environment.

Thank you so much for the helpful graphic 😍. I believe in this are too many environments as seen by the number of dots (I believe it should be the global environment and 3 function environments in total) and also the values of x1, x2, x3 are not represented (which could be easily added, I guess).

arashHaratian commented 3 years ago

yes, I did not add x1, x2, and x3 values and bindings. my point is f2() function binds to the variable name f2 inside the execution env of the f1(), f3() function binds to the variable name f3 inside the execution env of the f2(). check the plus example in the book. this question is similar to that.

henningsway commented 3 years ago

Hey @arashHaratian,

I just had another look at the exercise and also the plus-example in the Execution Environments-Subsubchapter.

I still believe the diagram currently presented is correct:

f1 <- function(x1) {
  f2 <- function(x2) {
    f3 <- function(x3) {
      x1 + x2 + x3

      print(rlang::env_print())
    }
    f3(3)

    print(rlang::env_print())
  }
  f2(2)

  print(rlang::env_print())
}
f1(1)
#> <environment: 0x5589c1864c60>
#>   parent: <environment: 0x5589c1864e20>
#>   bindings:
#>   * x3: <dbl>
#> <environment: 0x5589c1864e20>
#>   parent: <environment: 0x5589c1864fe0>
#>   bindings:
#>   * f3: <fn>
#>   * x2: <dbl>
#> <environment: 0x5589c1864fe0>
#>   parent: <environment: global>
#>   bindings:
#>   * f2: <fn>
#>   * x1: <dbl>

What do you think? :)

arashHaratian commented 3 years ago

yes, I had done this experimentation and I found the same result, but these environments are the execution environments. this is the example of the book ( above the plus_one example )

h2 <- function(x) {
  a <- x * 2
  rlang::current_env()
}

e <- h2(x = 10)
rlang::env_print(e)
print(e)

and so the bindings that are printed are in those environments ( like a in the above example).

am I right?

henningsway commented 3 years ago

I'm already quite sure you are correct. Now, we just have to wait until I get it too. 😂

I agree with the execution environments...

I'll take another look shortly, thank you for your patience. :D

henningsway commented 3 years ago

So, I've updated the text again.

I also made a sketch of how the diagram could look if we include the distinction between function environments at creation time and execution time.

image

@arashHaratian please let me know what you think of it now. :)

arashHaratian commented 3 years ago

I think it's great and it's more complete than what I drew. 👌👌

henningsway commented 3 years ago

That's good to hear! :)

(The only thing that still confuses me a little, is that the rlang::env_print() outputs only seem to show the execution environments and that these have each other as parent environments. In the diagram each execution environment seems to bind to it's function environment - do you understand what I mean?)

arashHaratian commented 3 years ago

I think the answer is they bind to the environment that function binds to. (a quote from the book: "the parent of the execution environment is the function environment."

the book uses this convention to make the graph easier, instead of drawing the arrow from the execution environment to the function environment.

so, in fact, you can draw the arrow from the execution env of the f1 to the global_env.

I hope I answered your question correctly.