Using the + function on a tuple of two Int64s returns the sum:
julia> +((1, 2))
3
However, using the + function on a variable that references a tuple gives the following error:
julia> a = (1, 2)
(1,2)
julia> +(a)
ERROR: MethodError: no method matching +(::Tuple{Int64, Int64})
I'm having trouble understanding why it behaves like this, especially when the following code returns true.
julia> typeof(a) == typeof((1, 2))
Note that, contrary to what you might think,
This is a single function call equivalent to
(+)(1, 2)
. There is no tuple, although the syntax may look like there is a tuple. (The+
function, as you noted, does not work on tuples.) Is this behavior desirable? Well it was reported as a bug #12755, but then fixed. But the fix caused bug #12771 which resulted in the fix being reverted by pull #12772.The solution to this mess is to avoid calling operators as functions without explicitly writing parentheses. That is, always write
(+)(1, 2)
instead of+(1, 2)
. You can verify that(+)((1, 2))
throws the error that you expect.(This problem only occurs with unary operators, hence why
|
and*
are not subject to it.)If you're interested, the heart of this problem is a fundamental ambiguity between
+(x, y)
function call syntax and unary operator syntax. Here are a few situations that motivate parsing+
and-
as unary operators, even when followed by(
:-(x+y)^2
, it is highly likely that(-)((x+y)^2)
was meant, not((-)(x+y))^2
. So we cannot simply unconditionally parse-(
as a function call.-
parsed up to a certain precedence, so that-x * y
is parsed as(-x) * y
,-x + y
as(-x) + y
, but-x^y
as-(x^y)
.-(1, 2)
parse as(-)((1, 2))
, that is, a function called on a tuple. For whatever reason or another, it was decided to add an exception for when the thing after-
looks like a function call tuple. This is so that+(1, 2)
would work, but this is really mostly just a hack.((1, 2))
looks exactly like(1, 2)
; just the former is wrapped in parentheses.My personal opinion is that the
-(1, 2)
notation is silly (and doesn't work in all cases anyway; e.g. in-(1, 2)^2
). If that exception weren't around, and-(1, 2)
consistently parsed as a unary function call on a tuple, then more consistency could be had without (I think) much loss. It's not too bad to just write1 - 2
or(-)(1, 2)
when a binary function call is desired.