# Guarantees on order of evaluation?

**URL:** <https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259>\
**Category:** General Usage\
**Created:** [March 17, 2023, 7:55pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259 "2023-03-17T19:55:29Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![lmtzx9h4qqnt](https://avatars.discourse-cdn.com/v4/letter/l/74df32/32.png) [@lmtzx9h4qqnt](https://discourse.julialang.org/u/lmtzx9h4qqnt)\
**Post date:** [March 17, 2023, 7:55pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/1 "2023-03-17T19:55:29Z")

</div>

Are there any guarantees on the order in which sub-parts of expressions (e. g. function arguments) are evaluated? It seems like Julia evaluates from left to right, but there doesn’t actually seem to be anything in the documentation about this? The only thing I have been able to find is that [the order of evaluations in a chained comparison is _not_ defined](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Chaining-comparisons).

* * *

Background: I have some code that looks like this

```julia
sum(
	factor1(i, j, k) * (
		factor2 = A(i, j, k) * B(i, j, k) * C(i, j, k)
	) *
	(factor2 == 0 ? 0 : expensive_function(i, j, k))
	for i, j, k in Iterators.product(gen(args)...)
)

```

and would like to make sure that the assignment and use of `factor2` in the same expression will always work properly.

In other words, I’m trying to optimize a calculation to stop early when some factor is 0. So what I really want is [short-circuit evaluation for multiplication](https://groups.google.com/g/julia-users/c/H5JEGdTClGA), which doesn’t seem to exist? If there is a better way to achieve this, I’d be interested as well! (And yes, I realize that I could rewrite the `sum(…)` as a normal `for` loop, but I prefer the “functional” form.)

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [March 18, 2023, 4:05am UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/2 "2023-03-18T04:05:17Z")

</div>

```julia
function f(i, j, k)
    factor2 = A(i, j, k)*B(i, j, k)*C(i, j, k)
    iszero(factor2) && return factor2
    return factor1(i, j, k)*factor2*expensive_function(i, j k)
end
sum(splat(f), Iterators.product(gen(args...)))

```

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [March 18, 2023, 11:48am UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/3 "2023-03-18T11:48:43Z")

</div>

> [@lmtzx9h4qqnt](#):
>
> ```julia
> sum(
> factor1(i, j, k) * (
> factor2 = A(i, j, k) * B(i, j, k) * C(i, j, k)
> ) *
> (factor2 == 0 ? 0 : expensive_function(i, j, k))
> for i, j, k in Iterators.product(gen(args)...)
> )
> 
> ```

Why not something more like this?

```julia
sum(
    let factor2 = A(i,j,k) * B(i,j,k) * C(i,j,k)
        if iszero(factor2); factor2
        else factor1(i,j,k) * factor2 * expensive_function(i,j,k)
        end
    end
    for (i,j,k) in Iterators.product(gen(args)...)
)

```

Another possibility:

```julia
sum(Iterators.product(gen(args)...)) do (i,j,k)
    factor2 = A(i,j,k) * B(i,j,k) * C(i,j,k)
    iszero(factor2) && return factor2
    factor1(i,j,k) * factor2 * expensive_function(i,j,k)
end

```

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [March 18, 2023, 1:49pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/4 "2023-03-18T13:49:52Z")

</div>

> [@lmtzx9h4qqnt](#):
>
> Are there any guarantees on the order in which sub-parts of expressions (e. g. function arguments) are evaluated?

To answer your original question:

[https://docs.julialang.org/en/v1/manual/mathematical-operations/#Operator-Precedence-and-Associativity](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Operator-Precedence-and-Associativity)

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 18, 2023, 4:56pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/5 "2023-03-18T16:56:25Z")

</div>

Positional and keyword arguments are evaluated left to right. I’m not sure where this is clearly documented, though — see the discussion in [Document function argument evaluation order by yurivish · Pull Request #24603 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/24603), which was never completed.

---

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [March 18, 2023, 5:04pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/6 "2023-03-18T17:04:01Z")

</div>

Although, I think it should be said that the order of evaluation of function arguments seems like a leaky abstraction at best, and (subjectively, open to disagreement) I think it would usually be bad style to design code that depends on this behavior for performance. I think it will be more clear to the reader, and possibly less susceptible to future confusing performance regressions during a refactor, if you build the inputs as separate objects (possibly in a `let` block) in the order you want before passing them into the function.

Slick one-liners are fun, but so is obviousness and durability 🙂

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [March 18, 2023, 7:50pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/7 "2023-03-18T19:50:01Z")

</div>

I believe everything is evaluated left-to-right (that’s a guarantee except if you control with parenthesis, and also precedence rules apply), except for, yes (there’s a good reason for this, to allow it undefined, but I think in practice for now, if may also be left-to-right):

> The only thing I have been able to find is that the order of evaluations in a chained comparison is not defined.

I recall however one suggestion to break that rule for 2.0 (may never be implemented, meant for matrices if I recall, the title is misleading, doesn’t justify this issue):

> <https://github.com/JuliaLang/julia/issues/34999>
>
> @ChrisRackauckas and I was just talking and we were wondering why we are parsin…g \`1 + 2 + 3\` as \`Expr(:+, 1, 2, 3)\`.
> While I can see how that makes the parsers job easier it seems to make it harder for type-inference since instead of just
> having on \`+\` method for integers we now have ~16, added to that is Chris infamous tendencies to create very long expressions
> which causes things to break down one you reach \*\*a\*\* limit. (and as demonstrated before no matter how large \`N\` at some point it will be to small).
> 
> Is there a strong reason that I am missing for why we keep parsing it as a single expression? Macro-writers already need to program defensively
> against both forms and it seems to me normalizing earlier would be better.

You can also change to more optimal way of evaluating with a macro (for matrix multiplies, then data dependent, I forget the package that implements it, maybe those in the following discussion, except they are outdated).

> [@Why is multiplication A\*B\*C left-associative (\`foldl\`) not right-associative (\`foldr\`)?](https://discourse.julialang.org/t/why-is-multiplication-a-b-c-left-associative-foldl-not-right-associative-foldr/17552):
>
> This behaviour is a bit surprising: julia\> A = randn(50,50); x = randn(50); julia\> @btime A\*A\*x; 10.064 μs (3 allocations: 20.17 KiB) julia\> @btime A\*(A\*x); 736.336 ns (2 allocations: 1.06 KiB) julia\> @btime (A\*A)\*x; 11.066 μs (3 allocations: 20.17 KiB) I’m curious on the design choice, as matrix \* vector is much more common than say vector’ \* matrix.

> So what I really want is short-circuit evaluation for multiplication 3, which doesn’t seem to exist?

Seems would be doable with a macro.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [March 18, 2023, 8:05pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/8 "2023-03-18T20:05:45Z")

</div>

There is [documentation of the evaluation order for keyword arguments](https://docs.julialang.org/en/v1/manual/functions/#Keyword-Arguments):

> Keyword argument default values are evaluated only when necessary (when a corresponding keyword argument is not passed), and in left-to-right order. Therefore default expressions may refer to prior keyword arguments.

---

<div class="post-metadata">

**Author:** ![lmtzx9h4qqnt](https://avatars.discourse-cdn.com/v4/letter/l/74df32/32.png) [@lmtzx9h4qqnt](https://discourse.julialang.org/u/lmtzx9h4qqnt)\
**Post date:** [March 18, 2023, 8:47pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/9 "2023-03-18T20:47:22Z")

</div>

> [@CameronBieganek](#):
>
> To answer your original question:
> 
> [https://docs.julialang.org/en/v1/manual/mathematical-operations/#Operator-Precedence-and-Associativity](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Operator-Precedence-and-Associativity)

Thanks, but that only explains how things are parsed when different operators are mixed, not the order in which the arguments to each individual operator (or function) are evaluated. Note that the example I posted contained only multiplications, so the question of operator precedence doesn’t apply.

> [@stevengj](#):
>
> Positional and keyword arguments are evaluated left to right. I’m not sure where this is clearly documented, though — see the discussion in [Document function argument evaluation order by yurivish · Pull Request #24603 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/24603), which was never completed.

That’s very useful! Indeed it seems like this was meant to be documented, but has somehow fallen by the wayside.

> [@adienes](#):
>
> Although, I think it should be said that the order of evaluation of function arguments seems like a leaky abstraction at best, and (subjectively, open to disagreement) I think it would usually be bad style to design code that depends on this behavior for performance. I think it will be more clear to the reader, and possibly less susceptible to future confusing performance regressions during a refactor, if you build the inputs as separate objects (possibly in a `let` block) in the order you want before passing them into the function.

I’m not sure what you mean by it being a leaky abstraction – things have to be done in _some_ order (although this doesn’t have to be stable or consistent – my question is just whether it is or not), and for expressions with side effects, the order does matter (as in my example). I don’t really see an abstraction here beyond the basic “expressions” and “function calls”. I think the code I posted is perfectly clear; I’m just asking whether it can be expected to work 😉

I’ll think about a `let` (or even `begin`) block à la @uniment or a macro (@Palli), though.

> [@Palli](#):
>
> I recall however one suggestion to break that rule for 2.0 (may never be implemented, meant for matrices if I recall, the title is misleading, doesn’t justify this issue):

I don’t think this would’ve changed the order of evaluation of _function arguments_, considering that `a * b * c` is (currently) parsed as `*(a, b, c)`. The `*` function can do to its arguments whatever it wants; I’m asking about what happens if `a`, `b` and `c` are expressions with side effects.

> [@Palli](#):
>
> Seems would be doable with a macro.

Indeed – since nobody seems to have done it so far, I might just write one 😃

> [@CameronBieganek](#):
>
> There is [documentation of the evaluation order for keyword arguments](https://docs.julialang.org/en/v1/manual/functions/#Keyword-Arguments):

Technically, this only describes the order for evaluation for keyword argument _defaults_ 😉

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [March 18, 2023, 9:14pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/10 "2023-03-18T21:14:14Z")

</div>

> [@lmtzx9h4qqnt](#):
>
> that only explains how things are parsed when different operators are mixed, not the order in which the arguments to each individual operator (or function) are evaluated.

E.g. `a(b(c), d(e)) + f(g)` would evaluate b then d, then a (still understood as left-to-right, but not strictly as in e.g. SmallTalk), then f, but with the 2.0 braking change I mentioned, then first f, if I understood things.

---

<div class="post-metadata">

**Author:** ![lmtzx9h4qqnt](https://avatars.discourse-cdn.com/v4/letter/l/74df32/32.png) [@lmtzx9h4qqnt](https://discourse.julialang.org/u/lmtzx9h4qqnt)\
**Post date:** [March 20, 2023, 5:59pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/11 "2023-03-20T17:59:53Z")

</div>

OK, now I’m confused.

1. The part you’re quoting was a reply to CameronBieganek.
2. Your example `a(b(c), d(e)) + f(g)` should be identical regardless of the current parsing or the one proposed in the issue you linked, since there is only one `+`. In both cases, it would parse to `+(a(b(c), d(e)), f(g))` and be evaluated like any function with 2 arguments.

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [April 12, 2023, 9:14pm UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/12 "2023-04-12T21:14:19Z")

</div>

Sorry, I think I meant to write `a(b(c), d(e) + f(g))`

---

<div class="post-metadata">

**Author:** ![lmtzx9h4qqnt](https://avatars.discourse-cdn.com/v4/letter/l/74df32/32.png) [@lmtzx9h4qqnt](https://discourse.julialang.org/u/lmtzx9h4qqnt)\
**Post date:** [April 13, 2023, 4:52am UTC](https://discourse.julialang.org/t/guarantees-on-order-of-evaluation/96259/13 "2023-04-13T04:52:18Z")

</div>

As long as there is only one `+`, there’s no difference either way?
