`in` as variable name leads to calling of variable

I stumbled across code that uses in as a variable name, which confused me, so I tried to see when using keywords as variables breaks. First a few examples where everything is fine:

julia> f(in, out) = in + out
f (generic function with 1 method)

julia> f(1,2)
3

julia> f(in, ∈) = in + ∈
f (generic function with 1 method)

julia> f(1,2)
3

julia> f(i, in) = i ∈ in
f (generic function with 1 method)

julia> f(1,[1,2])
true

julia> f(i, ∈) = i in ∈
f (generic function with 1 method)

julia> f(1,[1,2])
true

As long as the variable is not used as a keyword too everything works fine. But when in is used both as a variable and keyword, the variable is getting called:

julia> f(i, in) = i in in
f (generic function with 1 method)

julia> f(1,[1,2])
ERROR: MethodError: objects of type Vector{Int64} are not callable
Use square brackets [] for indexing an Array.
The object of type `Vector{Int64}` exists, but no method is defined for this combination of argument types when trying to treat it as a callable object.
Stacktrace:
 [1] f(i::Int64, in::Vector{Int64})
   @ Main ./REPL[9]:1
 [2] top-level scope
   @ REPL[10]:1

Is this the expected behaviour?

Other keywords are not allowed as variable names:

julia> f(for) = for
ERROR: ParseError:
# Error @ REPL[11]:1:6
f(for) = for
#    ╙ ── unexpected `)`
Stacktrace:
 [1] top-level scope
   @ REPL:1

julia> f(end) = end
ERROR: ParseError:
# Error @ REPL[12]:1:3
f(end) = end
# └ ── Expected `)` or `,`
Stacktrace:
 [1] top-level scope
   @ REPL:1

julia> f(if) = if
ERROR: ParseError:
# Error @ REPL[13]:1:5
f(if) = if
#   ╙ ── unexpected `)`
Stacktrace:
 [1] top-level scope
   @ REPL:1

I see that users might like to use in as a variable for an input, but it leads to weird behavior as shown above. So my question is should in be allowed as a variable name?

in is only a keyword in the syntax of for loops and comprehensions. Otherwise it is an ordinary identifier that works as a binary operator, in the same way that + is an ordinary identifier that works as a binary operator.

For example:

julia> typeof(in)  # the builtin `Base.in` is an identifier for an infix function
typeof(in) (singleton type of function in, subtype of Function)

julia> f(in) = [x^2 for x in in]  # "in" used as both a local variable and a keyword
f (generic function with 1 method)

julia> f(1:3)
3-element Vector{Int64}:
 1
 4
 9

julia> g(i, in) = i in 1  # "in" used as a binary operator, not a keyword
g (generic function with 1 method)

julia> g(2, +)
3

Of course, I wouldn’t recommend using in in this way, in the same way that I wouldn’t recommend redefining + to be 17 or atan.

7 Likes