Variable binding: (re-) assignment, argument passing, `let`, scope

documentation

#1

I’d like to make sure I understand “variable binding”: what happens when
using x =... in different scope constructs, particularly when the name
x remains the same.
I picked examples that seemed more unintuitive for me; I could have split
them in many posts, but they are very inter-connected.

So here is an exercise for you to see where I’m getting it wrong :wink:
EDIT: if you also explain in a line WHY it’s wrong, it will be awesome!

But a single person not have to answer all of them: just please mark
which you are answering, and I will edit to remove the questions from
here.
to correct from here.

I hope will be helpful for other users too.
Thanks!



EDIT: NOTE TO THE READER
The strike-outs below reflect corrections in my own understanding, most of them due to the discussions with @yuyichao (thanks again! ) in the replies below, to the extent I understood him. Where I still have a doubt, I’ll leave a (?).



In a new module, or fresh REPL:

1. Assignment

x=1
Creates a “variable(name) binding”: a new name (or variable) name-location (associated with
“x”) , pointing to a new value-location (storing 1)
associated with /pointing to / bound to value (or object) 1.

CML: ( by “CML” I will mean At computer memory level, at “RUN-TIME” (after compilation) ) :
in case this instruction is inserted as is, by itself, at REPL, then some location in memory will be occupied by the (binary repr. of) value 1, linked through the address of that location to the name “x”.
However, in other contexts (for ex: not at REPL, or when this instruction is surrounded by other instructions in a block…), due to complex compiling optimizations, the above statement about CML may not be true. (Read replies in the thread to understand why).

For this reason, and because I know too little about compilation, I will leave out any specific interpretations at CML in the next parts, except for a comment on it.

2. Re-assignment

x=2
? Keeps the same binding (name-location and value-location), just
overwrites the value-location with 2

Re-binds the same name/variable “x” to value 2.

CML: cannot say whether the original value location (storing 1) is or not overwritten with value 2: if x=1 was enter-ed separately, then followed by x=2, then it might (?), but if both entered as x=1; x=2 then an “original” value location storing 1 may not be produced at all !
Bottom line: it’s compiler’s business, as long as it produces the final result, compliant with the semantics-level interpretation I wrote above this paragraph.

3. Argument passing

After reading this
https://docs.julialang.org/en/v1.0/manual/functions/#Argument-Passing-Behavior-1
f(a) = print(a)
f(x)
A local var. binding: new name a name-location (associated with string
“a”), but pointing to the same value-location of x as before
seen only from within body of f, but referring to the “same” value/object as of x (“pass by sharing”)
What “same” means here:

  • in case the value is immutable (like in our case, or say a tupple), then it just means “equal” value, and the semantics is equivalent to “pass by copy”.
  • in case the value is mutable (like if x=[0 0]), then it means both a and x share the same “reference” to the value/object . This “reference” doesn’t have to be simply an address of the memory location holding the value: is like a common “handle” that just guarantees that modifications to value of a within body of f will reflect in x too (say with a[1]=1)

CML:

  • with immutable values, compiler is free to either copy the value of x to new mem. location or keep it in same – we don’t know and should not care, because neither violates the semantics of the language.
  • with mutable value/object - I think the compiler better keeps the object in same location in memory, and “reference” simply be the memory address of that location (?) – but hey, as long as the specification above holds, it is also allowed to keep multiple copies of same object, cross-linked with direct or indirect pointers/references, eventually linked to that common “reference” in the semantics.

4. Local assignment

g(a) = (x=a; x = 3; print(x))
g(x)
Initially happens same as in #3. above.
Then, with x=a in the body of g, another local name-binding is
produced: new name x name-location (but with same name string “x”), and , new
value-location as well, though holding
bound to same value as before (2).
Because of that, next x=3 does not change the outer (global) x, but just re-binds local x to value 3.

5. global vs local within a function’s local scope block

The interpretation/question is in comments:

function h()
   global x=2 # ?? same thing as in 2. above
   x = 5      # ??  strikeout(same thing as in 4. above)
  function j()
     x=10     # ?? strikeout(keeps the same variable binding, just overwrites the value)
  end
end
h()
# ?? strikeout(so global x=2 now)

EDIT: my interpretation of line x=5 was wrong: x was made global by line above, thus stays so within current
block. Hence comment for x=10 is also wrong, and so is the last comment:
thus, finally, after h() call, x is 5.
EDIT: comment for x=10 is not wrong only b/c of scope, but just overwrites the value : see above CML notes…

6. in let, nested in global vs in local scope block

x=2
let x=1
end

function k()
  x = 0
  let x=1
    print(x)
  end
  print(x)
end
k()
# so global x=2 still

In both cases of let, new var binding is formed, local to let: new name
name-location (associated with “x”), and new value-location (with 1). with value 1.
Hence k() prints 1 then 0 , while global x is still 0. 2

7. inside while

x=2
 while true
   # print(x)
   x=x
   print(x)
  break
end

Will not print anything, even if uncomment Ist print(x), but just
give:
ERROR: UndefVarError: x not defined
because:
x=... is detected first, syntactically, and makes x local( creating
new name name-location, and (?) allocating an empty value-location for it ) but not bound to any value).
Then evaluation happens, top-down, but Julia does not have any value to
replace right-hand side of x=x or in print(x), b/c making x local has applied to the RHS of x=x as well, and thus bans it from using the global x value.
See also this discourse post.

8. in let : next to it vs inside its body

x=2
let
  x=x # gives error just like 7. above.
  print(x)
end

let x=x # works
   print(x)
end

In the last let, in contrast to the first let and to while loop in 7. above, let also introduces a local scope, however if assignments are written right next to let:
then the right-side of assignment x=… is evaluated first (thus getting the value from the outer scope), then it creates new variable binding: name-location, and value-location, to store that 2 value.
then the names in the LHS of assignments are made local, while the names in the RHS are made global. At evaluation, the right-side of assignment x=.. is evaluated in the outside scope, hence x in the local scope get’s bound to 2 (2 is written in its value-location)
See also
https://docs.julialang.org/en/v1.0/manual/variables-and-scoping/#Let-Blocks-1
, below the Ist code block.
EDITS: scopes of names in both LHS and RHS of the assignments are determined syntactically( at “compile” time), before the execution/run time.

CML: The (2 is written in its value-location) : because is not true in general (i.e, with any such assignment, in any let block) – as explained in 1., 2., 3. …


Compile-time vs run-time actions
Assignment and argument passing semantics
#2

Wrong. There is no “value-location”. There’s an object 1, bound to a name x. That’s it.

No. It changes the binding to point the name x to 2 instead.

No. It’s a new name x points to the same object 1. Again, there’s nothing called “value-location”.

No. It’s just a local name bound to the object 3. The local name is a different name from global name of course. (If you want to call this two name-location that’s fine, there’s just no a value location that’s distinct from this.)

No. x is global in this scope.

No. This creates a new local x in j since x is a global in h.

No. x = 5.

Yes.

Again, no such difference.

No, x = 2, as you said above in the code comment.

New empty variable/name. Again, no value-location anywhere.

It’s not correct to think of it this way. The scope is determined BEFORE the code execute. The RHS of the let are evaluated in the outside scope. The scope determines the evaluation, not the other way around.


#3

Perhaps this may clarify things.

In a given scope a variable can only mean one thing: it either means the same thing as it does in the surrounding scope, or it’s a new local variable.

What does it mean for a variable to be “the same” as another? Two variables are different if you can change the name of all instances of one but not the other without affecting the meaning of the program.


#4

First off, thank you for your time to review my interpretations!
I understood & agree with, and made the corresponding edits above for, 3 of your “wrong”-s and “no”-s: one serious in 5.., a typo in 6., and one serious in 8. (although if one follows https://docs.julialang.org/en/v1.0/manual/variables-and-scoping/#Let-Blocks-1 , sentences below the Ist code block, I think it’s easy to get it partially wrong).

With respect to “value” vs “object”: I will take it as your preference; otherwise, see that the documentation uses former in most instances in the relevant sections:
https://docs.julialang.org/en/v1.0/manual/variables/
https://docs.julialang.org/en/v1.0/manual/variables-and-scoping/

At your reply to 3.:

It actually points to 2.

At your reply to 4.:

It actually points to 2 (I was referring explicitely to x=a)

About rest of “wrongs” you found: they concern my “value-location”, and, indirectly, “name-location”

1. Value-location.
It’s not my invention: https://docs.julialang.org/en/v1.0/manual/variables-and-scoping/#Let-Blocks-1 :

An assignment modifies an existing value location, and let creates new locations.

I think it’s common knowledge that “values” don’t exist in the sky or just in the programmer’s imagination, but are represented by bits at some computer memory location. That’s what my “value-location” means.
Maybe in Julia one cannot retrieve the address of a value-location if the value (or object if you like) is immutable (or at least I don’t know how , yet) – but that does not mean the location does not exist.

2. Name-location
I don’t understand it well myself, I simply took it from the documentation:
https://docs.julialang.org/en/v1.0/manual/functions/#Argument-Passing-Behavior-1 (2nd emph. mine):

Function arguments themselves act as new variable bindings (new locations that can refer to values), but the values they refer to are identical to the passed values. Modifications to mutable values (such as Array s) made within a function will be visible to the caller.

Is the above fragment wrong then @yuyichao ?
What do you think @StefanKarpinski ?

EDIT: I had interpreted “locations” in above quote to stand for “name-locations”, so as to distinguish from “value-location”.


#5

I said nothing about value vs object. Either is fine. What I was saying though, is that there’s only x the name and 1 the object/value. Your,

Implies that there’s x -> name location -> value location -> 1 which is not right. While you can say the name x is a property of the name location, there’s abosolutely no “value location” in between the “name location” and the object itself. There’s also no

No they do not exist in the semantics and may not exist at the runtime so it doesn’t make sense to talk about it at any level based on your code. According to your definition, it’s purely a low level compiler concept and there’s absolutely nothing you can say about them just based on your code. The location that stores any object you write may or may not exist and may or may not be unique. They also may or may not be allocated any time unrelated to any specific line of code you write.

I don’t see what’s wrong with that. (Edit: and I don’t have much issue interpreting your “name location” simply as variables apart from that it’s not the standard name for variables)

I still don’t understand what you are trying to figure out/illustrate though. It still seems to me that you are trying to find the execution that give rise to the scope rules (i.e. you go from code to how bindings are handled and then to what variables are visible as what values) and that line of throught is just wrong. I’m not sure what background you have but just in case I would add that your description sounds very similar to dynamic scope, which does not exist in julia.

If this line of thought is not what you are following, you should probably explain more explicitly what you want to learn at a higher level.


#6

A.

I don’t see what’s wrong with that

I had better asked: how do you interpret the word “location” in the in that quote? https://docs.julialang.org/en/v1.0/manual/functions/#Argument-Passing-Behavior-1

Function arguments themselves act as new variable bindings (new locations that can refer to values), but the values they refer to are identical to the passed values. Modifications to mutable values (such as Array s) made within a function will be visible to the caller.

I had interpreted (and maybe erroneously) that “location” to stand for “name-location”: some kind of memory location that stores the name of a variable, and is linked to the value of that variable , but different from the memory location of storing that value.
How do you interpret that word?

B. Why don’t you please explain the quote I mentioned from https://docs.julialang.org/en/v1.0/manual/variables-and-scoping/#Let-Blocks-1 :

An assignment modifies an existing value location, and let creates new locations.

After these 2 clarifications, we can see what’s redundant and what not in the chain " x -> name location -> value location -> 1 "

If you mean with my overall post, then:

  1. A consistent language and a minimum but sufficient level of details needed to explain assignment (name binding) in different block scopes in different circumstances.
    And not just in case of immutable values (as my code works with) but mutable too
  2. Make sure I understood the scoping rules
  3. As by-product, help other users too

P.S. I don’t have a formal programming background (it’s rather physics and math, and on my own time – some programming/computer science), but I understood that Julia has lexical scoping.


#7

The location just means variable. There’s no memory that stores the name of the variable. And there’s no generic semantics of memory location of stored value.

The locations still just means variable here. There’s no distinction between “value” location or “name” location. They are just variables. (i.e. names that can refer to values)

Assignment in every blocks in every circumstances are binding an object to a variable determined by the scope rules. (i.e. the scope rules tells you the, well, scope of the variable or which variable the name in your code refers to and the semantics of assignment takes it from there.)

And this is not the right way to understand scoping rules. Again, the scoping rule are above everything you’ve said above. None of the descussions you have about assignment can be used to explain the scoping rules. You apply the scoping rule first, and then you figure out how the assignment works, not the other way around. Trying to figure out how scoping rule works by looking at the semantics of assignment will only confuse yourself and confuse other readers.


#8

You linked the manual chapter on scoping multiple times, so presumably you have read it. It is now very nicely written and full of great examples, which should help you with your goals 1. and 2.

In case some corner case is unclear, ask about that, and then once you understand it, perhaps make a PR. contributing to goal 3.


#9

BTW, just in case it was ambiguous: by “value location” or “location of a value” I always meant: space/region/box in computer memory that would hold that value, as distinct from the “address” of that space/region/box. And “computer memory” as addressable memory as described in the introduction here https://en.wikipedia.org/wiki/Computer_memory .

OK, thanks for clarifying: in that sentence, location means variable.
It makes sense to me, as “names” are just a programmer’s convenience, that get converted by the computer to some references/addresses (direct or indirect) to the referred values.
So that sentence should just read:

Function arguments themselves act as new variable bindings (new names that can refer to values), but the values they refer to are identical to the passed values.

(Maybe the writer’s background was a progr. lang. where “variable” means a box/region in memory, which refers to value by just holding the (binary representation of) the value, or maybe the writer wanted to appeal to people with such background)

My concept of “name-location” is unnecessary and (most likely) just wrong – settled. Thank you.

Thanks; I had interpreted “value location” there as memory location of the value, which is why, in example 2. above, after:
x=1; x=2
, I thought the second = overwrites the same value-location with 2.
But I had my doubts, because I knew that for example, with
x = [0 0] ; x = [1 0]
the address (or reference, as found with pointer_from_objref(x)) of x's value/object is changed.
And hence it means the new object resides at a different memory location. So value-location, in this sense, is part of “language semantics” (IMO).

Back to the above quote, if we modify it to:

An assignment modifies an existing variable (name that can refer to values) , and let creates new variables.

Then it does sound clearer: let introduces new local scope, so the variables (name-value bindings) there, by default, are different from the variables in the outer region.
I applied the new version to most examples in my post, and indeed, to explain them, I don’t need to talk about the value location ( memory location of a variable’s value ).

Where I find it necessary, though, is in argument passing (examples 3. and 4.). In https://docs.julialang.org/en/v1.0/manual/functions/#Argument-Passing-Behavior-1 , by

[…] which means that values are not copied when they are passed to functions. Function arguments themselves act as new variable bindings […], but the values they refer to are identical to the passed values. Modifications to mutable values (such as Array s) made within a function will be visible to the caller.

The way I understand it, is that, with:

x = ... # some object
f(a) = do_something_with(a) 
f(x)

now a gets bound to the same object/value as x is, which imply the 2 variables’ objects share the same value/object location in memory – regardless of whether x is bound to mutable or immutable value.
Next:
if do_something_with(a) is just a=4 then re-binds “a” to 4 stored in a different memory location, because assignment is not mutation
but if do_something_with(a) is a[1]=4 and x=[0 0] in the first line, then this modifies a “in place”: without allocating (moving to) new memory location.

I think you wanted to say that, in that case, one doesn’t need to talk about value location - which is a different thing.

I never argued about uniqueness, or whether and when they are allocated, and other such details; but such a location exists, always – where else the computer stores 3 when I enter x=3 at REPL?? :slight_smile:

I’m sure you understand that – but simply the blunt way you have put it, from the beginning of your replies, doesn’t help understanding things, IMHO.

I think it’s one thing to explain that, according to the semantics of the language, in a particular code example, it’s not necessary to invoke the concept of “storing location”, and totally different thing to claim that “storing locations” don’t exist, and therefore it does not make sense to talk about them.

I never in this post said that the way I used to understand or explain scoping rules is by looking at how assignments work; here it is–I read the docs, I asked on forums.
The title of my post, and the introduction to it, say what is the main goal of this post.

Rather, indirectly, I tested my scope understanding , through the various examples I gave (and you helped me, as I acknowledged earlier).


#10

I did not mean to criticize the documentation at all (and if I sounded like criticizing a bit, I think it was constructive criticism, well intended).
I linked to it as I think it’s more reliable than anything else: because of the review process, it should represent the opinion of more than 1 specialist.

In case some corner case is unclear, ask about that, and then once you understand it, perhaps make a PR. contributing to goal 3.

That’s exactly what I’m doing here.


#11

Correct, I’ve already acknowledged that above and this is exactly what I said that may not exist. To be more precise, I’m telling you that when the assignment your wrote is executed, the bit pattern corresponding to the RHS (or LHS fwiw) may or may not exist anywhere (memory, register, harddrive you name it) on your computer. If this blews your mind, that’s fine, low level compiler detail has not been intuitive since even before I was born. You can just forget about the whole concept for this discussion since it’s unhelpful for discussions on this (semantics) level. (And I should also add that you should not mix implementation level concept with semantics level concept. That’s what I mean by unhelpful)

That line of document is very old. IMHO the reference to “location” rather than “variable” doesn’t add much/anything.

(Just to repeat myself, if you just use it to mean variable or binding, that’s fine, albeit non-standard.)

No. Only reference to object are. x = 1; x = 2; semantically first bind x to the object (value) 1 and then bind it again to the object (value) 2. Absolutely no values are overwritten. The compiler has complete freedom to do whatever it want when actually executing the code, including giving [0 0] and [1 0] in the second case the same address in certain cases. The different addresses you see is absolutely not part of the language semantics. And, again, talking about what the compiler can do in either cases is unhelpful. All what you should care for now is that assignment bind a variable to an object (making the variable reference that object, or semantically storing an object reference in that variable, whichever language you prefer.)

No. It does not imply anything about “location in memory”. It just mean the two variable reference the same object, period, no implication on memory.

And you are just trying very hard to confuse yourself here. If you remove everything about location in the above two sentenses then it’ll be correct and clear.

No. I mean you cannot talk about it here. If a concept exists and is valid but irrelevant, that’s when you don’t “need to talk” about it. Here not only the concept is irrelevant it may not exist and is not valid in this context, that’s why there should be no such thing in this discussion.

Nop.

In REPL, yes, there will be a place to store it (even though it doesn’t behave anywhere close to what you described above) but in many of the other cases you have, no it may not exist.

The only location related to a variable stores the reference to an object, not for the value (see above). And yes, semantically, storing locations (for the value) don’t exist.

That procedure is correct and should be encouraged. It just seems that you have a lot of misunderstanding and what I said is just my best guess how you got here. If that isn’t the case it’s fine and you can disregard that guess. I’ll just repeat that your understanding of scope shouldn’t involve understanding assignment. They are completely unrelated semantically. (They are of course coupled in the implemetation but don’t mix that in before you understand everything about the semantics of assignment and scope first).


#12

And I should add that immutable and mutable values (objects) have identical semantics (well, other than the obvious difference of one allow mutation while the other do not). More specifically they have identical assignment semantics. All the other differences between them come from implementation/optimizations.

So if you truely want an understanding that works for both immutables and mutables, forget about memory, forget about implementation and only use the semantics, i.e. object and reference to object. This should be all what you need to understand assignment after you understand scope and there will be no issue covering both mutability (mutability isn’t even included in the two ingredients I mentioned above).


#13

Unfortunately, the variables and scope chapter of the manual has, over the years, become rife with misleading and somewhat confusing material. It should probably be rewritten by one of the few people who completely understands how scoping works (it’s not that complicated but somehow it’s just a constant source of confusion for people—in all languages, not just Julia) and then be considered “locked” in the sense that all edits must be approved by one of those same set of people.


#14

Also, you may want to read the Wikipedia page on scope in general for some helpful background material:

Julia has very standard lexical (aka static) scoping. It works exactly the same as it does in Scheme, Python, C, Java—pretty much all languages with lexical scoping. The lexical closure behavior is identical to Scheme—this is widely considered “the right way” for lexical closures to work.

Implicit declaration of local variables seems to cause an undue amount of confusion, but it is really very simple: if you see x = in a local scope and x is not already a local variable that’s visible in that scope, then it is implicitly declared to be a new local variable in the scope where the assignment appears. This can only occur two ways: in the surrounding scope, x is a global variable or x is not defined at all.


#15

That may be, but I think in this topic one source of confusion is the search for a mental model that involves things happening to bits in memory — while modern languages like Julia deliberately avoid committing to these things in general, as it would preclude a lot of optimizations.


#16

Thanks for the link.
Aside from a typo, the only scope related mistakes I had in this topic refer to my examples 5. and 8. (and I fully understand them)
Rest of discussions mostly revolve around other things: what’s happening with assignment, under the hood, and whether “under the hood” should be talked about or not.
I should find a way to round it all up…


#17

I think I’m starting to see where you’re coming from. You’re talking from the perspective of so-called “language semantics”, which (in my simplistic understanding) describes what the computer should and should not do with this or that input string taken by a conforming implementation of the language. Like a specification.
And from this perspective, Julia’s language semantics does not involve “memory locations”, or doesn’t even mention them. – I have no problem with that!
By consequence, the language semantics does not guarantee or imply one or another thing about memory locations - including existence or nonexistence.

No, it doesn’t blow my mind; it’s just your opinion.
If you’re saying this from the perspective of lang. semantics as I explained above, then all fine…skip 1 paragraph.
But if not from that perspective, then it seems like either:

  • you’re not sure whether that bit pattern exists anywhere (or even think it doesn’t exist)
    I am sure that it does exist somewhere (regardless of any number of layers of compilations and other software between what I type on screen, and the result I get back).
    And if you’re repeating that over and over, it doesn’t convince me and should not convince anyone who can think for themselves :slight_smile: (and have some elementary computer science brackground )
  • or , we have a language / philosophy barrier here. Maybe your notion of “exists” is different from mine. Arguments often happens due to different meanings for different people. Peace!

With that out of the way.
Unlike yourself apparently, in this post, I did not set out to adhere strictly to that language semantics.
So I don’t see the value of “you must not talk about memory locations here” or “you should that and should not that” (You sound dogmatic or even authoritarian ):
of course I can talk about anything that helps my understanding.
And one such thing is memory location for me, even if lang semantics does not mention it, and even if it’s not helpful to you.
Some people learn/understand things better when they are grounded, not just abstract, and mem. location is the missing grounding thing (to me at least) between name and value.

x = 1; x = 2; semantically first bind x to the object (value) 1 and then bind it again to the object (value) 2 . Absolutely no values are overwritten.

Maybe they are overwritten. It’s compiler’s business. Language semantics doesn’t tell you that, so you can’t know it :wink:

The different addresses you see is absolutely not part of the language semantics.

In that case, I definitely don’t care that much about “language semantics” , if it doesn’t include things that are build in Julia language to use/explore.

No. It does not imply anything about “location in memory”. It just mean the two variable reference the same object, period, no implication on memory.

That’s from your “language semantics” perspective. But in general, if 2 vars reference the same object, and if the object is mutable, then I’m pretty sure that object is in 1 place, not 2 places in memory

Nop.

Yep.

The only location related to a variable stores the reference to an object, not for the value (see above).

Don’t mention location ! it’s not helpful in this discussion! :wink:

And yes, semantically, storing locations (for the value) don’t exist.

Correct! I fully agree with this sentence! (provided “semantically” means from that (theoretical) lang. semantics point of view)

I’ll just repeat that your understanding of scope shouldn’t involve understanding assignment.[…]

Right, you’re (just) repeating yourself. I’ve addressed this concern of yours in my prev. reply.

My conclusion:
There’s more than one way to understand things, what works for you may not be the best for others. You don’t have to agree with me, and I respect your personal opinions.


#18

It’s not an opinion, it’s the truth…

No. I’m sure it may not exist.

I’m not sure if you are implying anything about my education level here. But ignoring that, what you believe is simply not ture. The simplest example is that if you write x = 1; x = 2; in a local scope I can guarantee you that the 1 will not appear in the compiled code. (If it does, report a bug. And my use of “may not” simply mean that the bit pattern can of course accidentally exist due to other reasons, e.g. a y = 1 that comes after). There are more complicated examples that will cause object youve created and use to not appear anywhere in the final result.

You can talk about anything that you think are helping your understanding. I’m just pointing out that you are completely on the wrong track though, which you are of course free to ignore or interprete differently but my advise is that it won’t help your understanding. I, having failing in the same track a few years ago, knows for sure that compilcating the discussion by adding unnecessary ingredient, especially in a handwavy and incorrect way, didn’t help my understanding.

Do note that my original objection to your memory location concept is not that it can be optimized out completely it’s that there really isn’t anything that behave like what you describe it to be, doesn’t matter what you name it.

NO. I said no “values” are overwritten. That’s the language semantics and the compiler has no business violating that. Registers and memories, both irrelavant, could be overwriten.

Well, you can’t understand language semantics (i.e. what assignment and scope do/mean) without caring much about it.

And that’s absolutely wrong. It’s actually the key to the julia compiler design/implementation. The same object can and do live in completely different pieces of memories. And this is not even limited to immutables.

Not sure what you are trying to say here. I’m just acknowledging that your notion of a location storing a reference is correct. If you don’t like that now, it’s fine.

I agree so I’m pointing out things that’s just wrong in your understanding. By telling you to not talk/think about certain concept I’m just pointing you a shortcut that will completely avoid those wrong understandings in the first place. If you really want to confuse yourself, which I have to assume you are given the statements that I have to correct in the last reply (edit: I mean I assume you are still confused, not that you are looking forward to be confused), it’s of course fine to keep mixing things together. I have no authority to stop that.


#19

FWIW, I agree with your approach to understanding: Mentally compile your code into unoptimized assembly; then allow llvm to work its magic to give you the assembly you should have written in the first place. The meaning of assembly is sufficiently unambiguous that all these questions evaporate (replace assembly by llvm-IR as appropriate).

Always asking “how would I compile that” also allows you to guess how things work: They work in a way that can be compiled in a reasonable way. @yuyichao is right that “low level compiler detail has not been intuitive since even before I was born” (he is always right, even though it is sometimes necessary to meditate on his words).

Luckily, compilers go to great effort to preserve the illusion that they are simple; i.e. complicated optimizing compilers emit code that has the same observable behavior as naive “platonic” compilers.

Assembly is a high-level abstraction as well: Whatever you wrote, the CPU will probably do something else (much of it at the same time). Whenever you care to look, it will then go to great pains to retcon an “architected state” that is compatible with the high-level abstraction that people can understand (i.e. assembly). Sometimes this abstraction leaks, and hilarity ensues (spectre, meltdown, etc). Sometimes the key to fast code is to play “if I were a superscalar processor, how should I execute this assembly?” (i.e. not one instruction after the other). It is turtles all the way down.


#20

I’m not sure if you are implying anything about my education level here.

Of course not: it’s about the education level of “anyone” in that sentence.

what you believe is simply not ture. The simplest example is that if you write x = 1; x = 2; in a local scope I can guarantee you that the 1 will not appear in the compiled code.

Then you misunderstood what I believe true and what not. What I said is that at x=1, when that get’s “taken” by the computer, value 1 will be stored at some memory location. I never said that value will stay there forever, or until in the compiled code, or untill the so-called “run-time” (which I don’t have a simple picture for due to JIT) .
I only said what I said, and meant what I said! It’s that simple.
What the compiler does in your example – it still works with values 1 and 2 both stored initially in memory. That’s it.
I see the big picture, and in that big picture , the work of compiler is part of the overall work of the computer to convert my program into the final results. That work happens with memory, and cache, and registers (as you said “you name it”)

LATE EDIT (back from the future): Although the above striked portion still deserves to be criticized , I strike it now because it is, unlike most of other things I wrote in all this thread, mostly non-sense that I should have realized before writing it, even without yuyichao’s next replies. I was confused for a moment by the idea that, when you run any source code (at Repl or not), it first gets loaded in memory – so in this trivial sense, anything you write in code get’s a place in memory. Silly, of course.

NO. I said no “values” are overwritten. That’s the language semantics and the compiler has no business violating that. Registers and memories, both irrelavant, could be overwriten.

(you’re referring to x = 1; x = 2; )
This again sounds like “proof by authority” to me: because you neither explain, nor link to a reference that explains.
By overwriting memory location, you overwrite the (representation of ) a value there, and viceversa (as values are stored in memory - at some point or another).
So you again talk strictly from lang. sem. regard, while I talk about the general scheme of things.

The same object can and do live in completely different pieces of memories.

Which “same object”? I was talking about 2 ojbects for which Julia gives the same reference, say as in: a=b=[0 0]

Not sure what you are trying to say here. I’m just acknowledging that your notion of a location storing a reference is correct.

Because you’ve been saying (or at least implying) all along that memory locations are not part of Julia language semantics, and you’ve been preaching to discuss strictly only what’s part of language semantics.

By telling you to not talk/think about certain concept I’m just pointing you a shortcut that will completely avoid those wrong understandings in the first place.

Thanks, sounds good. I also like the principle of separation of concerns :+1:
Except that there’s only so much you (or anyone else) can extrapolate from your learning experience to me (or someone else).