Walrus operator

I came across the walrus/assignment operator in Python 3.8.

The idea is that you can make an assignment in the if-expression and the variable is only visible in the if and elseif blocks. Just wondering if there’s any discussion before whether it’s a good idea for Julia… or not.

Example if it were to exist in Julia:

x = [1,2,3]
if (n := length(x)) > 0
    println("There are $n elements in `x`")
else
    println("Sorry, nothing in `x`")
end

So, the variable n is not available outside of the if-block nor in the else-branch.

Currently, we could use a let-block to limit the scope of n but it would be available in all branches of the if-block. Maybe that’s good enough for most people…

2 Likes

AFAICT the main difference from a let is indention and that’s easily solvable with a macro. And python can’t do that since AFAICT they have neither let or macros.

I believe it is good to separate additional variable scope from the expression that uses it. (i.e. it’s a bad idea to encourage if .... long expression ... (n := sth) ..... long expression). The variable assignment should just come before where it is used. Note that the example you give is one of this case since n is not a Bool, you have to write sth like if (n := length(x)) > 0 and it’s much cleaner to just write @let n = length(x) if ... else ... end instead.

1 Like

Oh yeah… my example is flawed regarding the condition. I have just updated the post.

I think a more appealing example is the walrus operator used with the comprehension expressions. Something like [y for x in range(3) if (y := 2 * x) > 1] in Python.

Ref:

https://github.com/JuliaLang/julia/issues/32905

3 Likes

That case is similarly easy to handle with a let around the generator though.

It’s slow due to boxing and also not parallelizable (e.g., when reducing a lazy iterator constructed by the comprehension).

I’m not opposed to us getting this doo-hicky, but I don’t think it really helps with anything. It makes code shorter, but often more confusing! Is there any way a variable like this could be treated differently by the compiler for some kind of speed boost or something?

1 Like

The main reason Python needs this operator isn’t scoping but because x = ... doesn’t have a return value.

It’s kinda crazy that if statements don’t generally introduce scopes but this does?

2 Likes

So it the difference between the walrus operator in Python and our assignment operator is that the walrus operator makes a local binding. We can do that already with the local keyword:

julia> my_list = [1,2,3,4];

julia> if (local m = length(my_list)) > 3
           println(m)
       end
4

julia> m
ERROR: UndefVarError: m not defined
...
9 Likes

Those are mostly implementation details (iteration itself isn’t parallizable either, without change in semantics) but I do agree that it is somewhat useful in comprehension. Comprehension/generator is already a compact syntax so it should be OK to introduce something along the same style to allow the condition and the return value to share a state. However, for all the normal code blocks, having a normal let would still be better.

1 Like

I think it’s nice to come up with the surface syntax that is easily handled by the compiler.

It is actually very useful to consider that the iterator comprehension is defining iterator transformation rather than the iterator itself. It makes parallel algorithms work naturally with the collections created by the iterator comprehensions, e.g., using the conversion of the iterator transformations to transducers (Transducer as an optimization: map, filter and flatten by tkf · Pull Request #33526 · JuliaLang/julia · GitHub). But yes, it’s in a way a change in semantics.

1 Like

Ooops… I made a mistake.

Somehow I was led to believe that it would introduce a local scope from reading a random blog post but I just checked with a real example and that’s not true. So, it seems that this operator is merely used for making code a little shorter. Sorry about the confusion :sweat_smile: