Assignment in function arguments

One of the references I found for this were in an old discussion about keyword arguments, where it seems the conclusion was that they prevented the possibility of assigments in function arguments. That makes sense for assignments that are indistinguishable from keyword arguments, but shouldn’t it be okay otherwise? E.g., if one wraps the assignment in parentheses? (Python, for one, makes this distinction – f(x=1) is fine, but f((x=1)) is a syntax error, and thus this syntax would have been free.)

My use-case is in a loop where I keep calling a function until (yes, I could use other iteration tools for this ;-)) such as

while (x = func(y)) ≢ nothing
    …
end

This, of course, works fine. However, if I need a function rather than an operator, it doesn’t work:

while !isa((x = func(y)), Z)
    …
end

Here Julia thinks I’m trying to use a keyword argument x. Is it necessary to permit calling keyword arguments in parentheses like this? Wouldn’t it be more useful to permit assignments? Or is there perhaps some straightforward workaround I’m missing (other than defining an operator for the function, which is an option in the binary case)?

Okay, isa was a bad example, as it’s already a binary operator xD But you get the idea!

This is very subjective, but I think this leads to code that is difficult to read. Sometimes I also do similar things, but I regret it later most of the time.

1 Like

Adding a semi-colon does what you want.

julia> isa((x = 1;), Int)
true

julia> x
1

3 Likes

Indeed it is subjective. The vitriol over the introduction of assignment expressions is (as I understand it) what drove Guido van Rossum to step back as figurehead for Python a couple of weeks ago. So some people really hate them, I guess.

One alternative would be to use somthing like Python’s iter(callable, sentinel) (which I can’t find in the standard library or in IterTools.jl), though that would require introducing () -> func(y), and would only work for equality. And a more specialized solution (e.g., including the argument to the callable, and having a predicate rather than a sentinel value) would perhaps be better handled by individual projects. (Though Python at least has takewhile, which is related. Maybe worth a PR.)

You can also just do:

x = func(y)
while !isa(x, Z)
    ....
    x = func(y)
end

Sure. The motivation was trying to avoid the redundancy. (A common Python pattern is to use an infinite loop with a conditional break – which sort of seems to indicate a missing looping construct. Or, indeed, assignment expressions, which have now been introduced.)