Function output confusion

I have a simple function that changes the first element of any matrix to the number 99. It looks like this:

function foo(a)
    a[1] = 99
end

Now when I apply the function to a matrix, here’s what I get:

x = [1 2 3 4 5]; 
y = foo(x); 

I’m confused, because applying foo to x has mutated the first element of x as expected, but the output y is just the number 99.

julia> x
1×5 Matrix{Int64}:
 99  2  3  4  5

julia> y
99

What exactly is the logic here? How is the function able to mutate x, then seemingly forget about the other elements in x before spitting out a scalar value y?

The expression a=b in Julia has a return value of b. Thus a[1]=99 (and therefore foo) returns 99.

3 Likes

Ooooohhh, interesting. That’s pretty straightforward now that I understand it. Thanks for the tip!

This is also the reason why you can do multiple assignments like a = b = 1.

2 Likes

Dang, well ain’t that fancy.

1 Like

For more consequences of this:

2 Likes

To get the function you want:

function foo!(a) # there's a convention to mark mutating functions with !
    a[1] = 99
    return a
end
    
2 Likes

Yes! Thanks; these concepts are slowly beginning to click for me…slowly.

it also means you can have snippets like this

 a = if rand() > 0.5  1 else 0 end

which assigns to a based on the condition

1 Like

Slick!

and they could be multi line

a = if rand() > 0.5
    b = 5
    c = 7
    b + c
else
    x = rand()
    v = rand()
    x * v
end

which assigns a to be b + c or x * v

1 Like

I would advise against this. It becomes super difficult to read. The oneliner is fine, but since there are no return statements in if blocks, it always takes a while to figure out what points are the final ones.

if x < 0
    a = 0
elseif x < 1
    a = 1
else
    a = 2
end

is a lot better than

a = if x < 0
        0
    elseif x < 1
        1
    else
        2
    end

. And the difference gets bigger the more complicated the statement is.

2 Likes

Why? You’ve never used lisp, have you?

No, I have not, and I’m increasingly convinced I don’t want to.

With functions it’s mostly obvious where the return value is, and if it’s not you can mark it with return. In an if clause you don’t have that option. Add to that the fact that the major action, the assignment, is all the way at the top, so if I’ve figured out what it returns I now need to scroll all the way back to remind myself where it’s stored. And some but not all if clauses are assignments. Meanwhile, “if x is small, assign 0 to a” is natural.

I recently implemented a feature in JuliaFormatter, but I ironically had to spend most of the time just figuring out heads or tails of all the if-assignments in there. It’s fixed now though.

What a pity :wink:

You can wrap the if clause in a begin block which then allows you to return from there.

For short if-clauses an assignment inside can be nice. For larger ones – as in the example above – I find the assignment outside better as it immediately makes clear where the value of a comes from. In the other case, it could well be that every branch assigns a different variable!

if x < 0
    a = 0
elseif x < 1
    b = 1  # oops
else
    a = 2
end

In any case, if I need to scroll over the if-clause it is probably time to at least move it into its own function or better yet, break the logic into smaller pieces.

3 Likes