! notation for individual modified variables

I quite like your proposal. It is already allowed by the syntax rules. So each function that changes its argument (that will be mostly functions receiving, and changing, arrays) could indicate not only in the formal parameter name that it will be changed, but even inside the function it would give helpful hints that we are changing the formal parameter.

Are you sure this works?

Try this (in a fresh REPL):

function f2(a!) a[1] = 2; end
b = [5]
f2(b)

I think in your example the global variable a is being mutated, not the argument a!

Even if it did work, I’m skeptical it’s a good idea, as your example shows.

I did not have a global a in the workspace. It did work. [Oops. I did have a defined before I called f2. I take the previous back: I need to refer to a! for the code to work.]

The naming scheme with the bang (!) has nothing to do with whether one accesses global variables from within a function. Orthogonal concepts!

So a good version of the code would be:

module mmmmmmmmmmmm
function f2!(a!) 
  a![1] = 2; 
end
end 
B=[5]
using .mmmmmmmmmmmm: f2!
f2!(B)
println("B = $(B)")

I like that a! is telling me inside the function that I’m changing something passed in as argument.

4 Likes

Well if you want to attach ! to the variable name, rather than to the formal parameter, then that’s a lot of annotation! !!! HaHa

Especially considering nearly all Julia code is in functions.

You are not seriously thinking that my proposal entailed going through all of Julia functions and changing them? I meant to use it just for myself. In my own code.

1 Like

If more people find this idea useful, where would be the place to extend the discussion? Opening an issue on the main repo?

Isn’t it rather source of confusion than help?

I think the original proposal was to make this a more general convention. I like the idea of annotating arguments like this in the abstract, but I think it would be nearly impossible to enforce / propagate through all the package developers. Making sure people use ! For mutating functions is probably hard enough.

Is it possible to write a macro, say @de!, which transforms f!(x!,y,z!,w), intended to return modified x and z and unmodified y and w, into f!(x,y,z,w) ? In this way one would write a clear function declaration with the indication of the modified return arguments but the actual body of the function would remain unchanged.
(I don’t know if macros can operate on a single line)

Sure,

strip_trailing_!(s::Symbol) = Symbol(rstrip(string(s), '!'))

macro de!(ex)
    # TODO: error handling 
    for i in 2:length(ex.args)
        @assert ex.args[i] isa Symbol
        ex.args[i] = strip_trailing_!(ex.args[i])
    end
    return :( $(esc(ex.args[1]))($(esc.(ex.args[2:end])...)) )
end
julia> @macroexpand @de! f!(x!, y!, z!)
:(f!(x, y, z))

julia> f!(x,y,z) = x + y + z;

julia> x = 1; y = 2; z = 3;

julia> @de! f!(x!,y!,z!)
6

Not saying if this is a good idea or not :stuck_out_tongue:

Not sure. The exclamation mark for function names is a soft convention; adopting a similar soft convention for formal parameter names is both non-breaking and probably not extremely hard (go about each function separately, one independent PR after the other, use tools for code refactoring).

So this entire thing is a question for the “julia style guide” only. And I like it, lots of info in the first line of docstrings, which mercifully contain the formal parameter names!

If this became adopted, one could probably even ask the linter to emit warnings for possible violations of the convention; then, type-inferred code without linter-warnings would almost have intent-like guarantees about data-flow.

If this became strongly adopted by the time julia 3.0 rolls around, then one might even think about making this a true guarantee (that the compiler is allowed to assume for optimization purposes).

1 Like

I like that you dare to dream, I did not think it was realistic to think this could be at some point implemented Julia-wide :slight_smile:

I totally agree: the information value of this annotation is substantial! And, nothing breaks at the moment, so everyone is free to use it in their own packages.

Do you mean the “!”? The added exclamation point has nothing to do with writing into global variables from within functions. (To avoid seeing global variables within the function, in my second code snippet I put the function definition into a module to prevent the sort of thing from happening.)

Nice! :slight_smile:

Another problem could be for example:

julia> a = a! = 1
1

julia> a==a!
true

julia> a!==a
false
1 Like

However, carrying the ! around inside the function does add some clutter. Here a comparison: https://gist.github.com/mauro3/b45c293d88167cd6cee622d08c05b5c0. Maybe not too bad though.

1 Like
julia> a! == a
true

It is good practice to put a space around operators anyway.

1 Like

I think it may be more than offset by the information value: anyone reading the code immediately sees that an input parameter is being modified.

If you still like the idea (I sure do!), I would file an issue on Julialang. Even if it is not made part of Julia at this point, perhaps we can get it as a suggestion into the documentation?

After a long hibernation, resurrecting the discussion :slightly_smiling_face:. What do you say in your comment would be something like what I drew quickly on a sheet of paper?

Julia function arguments follow a convention sometimes called “pass-by-sharing”, which means that values are not copied when they are passed to functions. 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. This is the same behavior found in Scheme, most Lisps, Python, Ruby and Perl, among other dynamic languages. – See: https://docs.julialang.org/en/v1/manual/functions/.

The above writing as found in the documentation of the language Julia leads me to think like the image I drew.

passing_by_sharing