Function argument, binding and name

I reviewed the very first lesson people learned from doc Functions · The Julia Language

julia> function f(x, y)
           x[1] = 42    # mutates x
           y = 7 + y    # new binding for y, no mutation
           return y
       end;

julia> @code_warntype f([1], 5.0)
MethodInstance for f(::Vector{Int64}, ::Float64)
Arguments
  y@_3::Float64
Locals
  y@_4::Float64
Body::Float64
1 ─      (y@_4 = y@_3)
│   %3 = Main.:+::Core.Const(+)
│   %4 = y@_4::Float64
│        (y@_4 = (%3)(7, %4))
│   %6 = y@_4::Float64
└──      return %6

According to my knowledge

  • A binding is an association / a relationship / a fact that a name refers to an object
  • A name is merely a name (some other people also call it a “variable”)

So what does the operation y = 7 + y do?
It changes the old binding y@_4 = 5.0 to the new binding y@_4 = 12.0.
But in both bindings, the name is y@_4 at both places.

I don’t think the existing doc reads coherent—y itself is not a binding; and y is not changed

My intuition suggests (do not constitute an advice):

  • Had best not reuse a name from an argument. Do not do the y = somethingElse above, since y is already a name of an argument.
  • If intend to, had best add a let:

Instead of doing

function f(x, y)
    x[1] = 43
    y = y * 1.0
    y = y * 1.0
    () -> y
end

, had best do

function f(x, y)
    let y = y
        x[1] = 43
        y = y * 1.0
        y = y * 1.0
        () -> y
    end
end

I have no idea which one has better performance. But the latter one looks a lot neater.
The former has y@_7::Union{Int64, Core.Box},
whereas the latter only has y@_5::Core.Box.

I’m not sure it makes sense to mix the lowered code with the semantics of julia code in this way. A lot of transformations are done on the way to native code. In particular, one intermediate representation is SSA (Static Single Assignment), the one you see with @code_typed. Then «bindings» are something completely different.

I see it as when you call a function, you pass it objects, not names.
So, in this example, the function initially bind the name y to the object with which it is called, and then it rebinds it to a totally new object.
There isn’t anything wrong in doing that..

Don’t all names always refer to some object, though? In this case there’s no difference between a binding and a name…