# Introduction

Recently, I was confused by how Julia parser works and complained on Julia Slack about it (in a moment I will explain what confused me). Then I learned Miguel Raz Guzmán Macedo has a very nice post about surprising behaviors of Julia, so today I thought to promote Miguel’s blog :).

In my post, not to steal all the fun you will have when reading Miguel’s blog, I will write about Julia’s behavior that surprised me and three behaviors, related to operator precedence, that commonly lead to bugs.

The post was written under Julia 1.7.

# What surprised me

The behavior of Julia that caught me off guard is:

julia> -a = 10
- (generic function with 1 method)


As you can see, by accident instead of writing -a == 10 I have written -a = 10. In consequence instead of doing an equality test we have defined a new function for the - operator in module Main overshadowing the - definition from the Base module, as you can see here:

julia> -(50)
10

julia> 1 - 2
ERROR: MethodError: no method matching -(::Int64, ::Int64)
You may have intended to import Base.:-
Closest candidates are:
-(::Any) at REPL[1]:1


The reason of this behavior is that for Julia’s parser writing -a = 10 means the same as writing -(a) = 10, which, can be recognized as a one-line function definition syntax.

Why is this behavior problematic? Once you have defined a new function for - in Main you have two options. Either restart your REPL or do - = Base.:- to bind Base.:- with - defined in Main (I would recommend restarting REPL instead of doing the work-around).

# Operator precedence corner cases

Here are three cases of operator precedence surprises in Julia.

#### Scenario 1: & and |

When you write:

julia> 1 == 3 & 1 == 1
true


instead of expected false you get true. The reason is that you probably thought that the parser will interpret your expression as:

julia> (1 == 3) & (1 == 1)
false


While Julia interprets it as:

julia> 1 == (3 & 1) == 1
true


#### Scenario 2: ranges

When you write:

julia> 1:2 .+ 3
1:5


you might have expected:

julia> (1:2) .+ 3
4:5



but actually this is interpreted as:

julia> 1:(2 + 3)
1:5


#### Scenario 3: pairs and anonymous functions

When you write:

julia> :a => x -> x => :b
:a => var"#1#2"()


you probably expect:

julia> :a => (x -> x) => :b
:a => (var"#3#4"() => :b)


but in reality you get:

julia> :a => (x -> x => :b)
:a => var"#5#6"()


The last scenario is relevant in DataFrames.jl, where we often use syntax:

source_column => (x -> some_anonymous_function_body) => target_column_name


# Conclusions

As any programming language Julia has some syntax corner cases that can be surprising. The problems with operator precedence I have listed in this post have a simple practical solution: if you are unsure about operator precedence be explicit and use parentheses to clearly signal how you want your expression to be evaluated.