It’s not any difference between Python and Julia that’s making things difficult here, but the fact that the SymPy library is sloppy and loses type information. For example, I was expecting Eq
would be a constructor that returns an object of type Eq
; we would’ve been able overload the arithmetic operators (e.g. addition, Base.:+(a::Eq, b) = Eq(lhs(a)+b, rhs(a)+b)
) as the article suggested. If this were any other Julia library, we could (and actually easier than Python since we wouldn’t need to make a wrapper class; we would just overload the methods directly).
But instead Eq
is just a function that returns a Sym
object, which can’t be told apart from any other SymPy symbolic expression. This a) is not Julian (because idiomatic Julia requests that CamelCase be reserved for types and constructors, and that function and object names should be written in snake_case), and b) means that we can’t overload the arithmetic operators like the article was suggesting.
All to say: Most of your surprise is due to SymPy, not Julia.
This is actually not a result of the size of the expression, but a result of how you use the -
minus operator.
When calling macros, we have two options: a) use parentheses and delimit expressions with commas (e.g., @my_macro(expr1, expr2, expr3)
—just like a regular function call but on expressions) or b) use spaces to delimit the arguments (e.g., @my_macro expr1 expr2 expr3
).
A consequence of that is: What happens when you happen upon an operator which can be considered either a binary operator or a unary operator? (namely: minus!)
You get this weirdness:
julia> macro what_did_I_do(exprs...)
for expr ∈ exprs
show(expr); println()
end
end
@what_did_I_do (macro with 1 method)
julia> @what_did_I_do 1-2 3
:(1 - 2)
3
julia> @what_did_I_do 1 -2 3
1
-2
3
julia> @what_did_I_do 1 - 2 3
:(1 - 2)
3
julia> @what_did_I_do(1 - 2, 3)
:(1 - 2)
3
julia> @what_did_I_do(1 -2, 3)
:(1 - 2)
3
julia> @what_did_I_do(x^2 + 3x -4 = 0)
:((x ^ 2 + 3x) - 4 = begin
#= REPL[215]:1 =#
0
end)
julia> @what_did_I_do x^2 + 3x -4 = 0
:(x ^ 2 + 3x)
:(-4 = 0)
julia> @what_did_I_do x^2 + 3x - 4 = 0
:((x ^ 2 + 3x) - 4 = begin
#= REPL[217]:1 =#
0
end)
Nominally, Julia treats 1 -2
as 1 - 2
. But in contexts where expressions will be space-delimited, they result in different expressions.
The other context in which we will space-delimit expressions is in building matrices, and we have the same behavior:
julia> [1-2 3]
1×2 Matrix{Int64}:
-1 3
julia> [1 -2 3]
1×3 Matrix{Int64}:
1 -2 3
julia> [1 - 2 3]
1×2 Matrix{Int64}:
-1 3
julia> [1-2, 3]
2-element Vector{Int64}:
-1
3
julia> [1 -2, 3]
ERROR: syntax: unexpected comma in array expression
This behavior is necessary so that we can build matrices with negative entries without writing a bajillion parentheses.
All to say: when you’re hoping to use -
as a binary operator, you should adopt a habit of writing it symmetrically spaced so it’s clear you wish to call the binary operator, not the unary operator. If that constraint cannot be enforced, then use the parenthesized version of macro calls so that expressions are comma-delimited,
eqn = @eq(x^2 + 3x -4 = 0)