Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Semantics of == not good for generic programming #520

Open
jlapeyre opened this issue Apr 11, 2023 · 7 comments
Open

Semantics of == not good for generic programming #520

jlapeyre opened this issue Apr 11, 2023 · 7 comments

Comments

@jlapeyre
Copy link

jlapeyre commented Apr 11, 2023

Having == construct an equation might be a good idea if this package were a standalone CAS, like Mathematica. But it's meant to work with the larger Julia ecosystem. The semantics of == in Julia at large is consistent. The semantics here is completely different, which means you using Sym in generic code will often do the wrong thing, in fact throw an error.

I can't find a single instance in Julia where a == b does not return Bool. Maybe there is one, but I can't find it. This line in Base.jl is largely responsible:

==(x, y) = ===(x,y)

You have...

julia> @code_typed ["aa"] == 3
CodeInfo(
1return false
) => Bool

julia> @code_typed Base == 3
CodeInfo(
1return false
) => Bool

As a result, this assumption is made everywhere in the Julia ecosystem.

This issue is similar to #15. I did not check this against the version of SymbolicUtils in that issue, but I think the problem there was also that == does not return a Bool, not simplification.

I noticed this a couple of years ago, and may have posted something somewhere. But I want to state the issue clearly. I can't think of a function in Julia that is more widespread and with more consistent semantics than Base.==. And the method defined for ::Sym breaks this consistency for the sake of giving the frontend experience of Mathematica. This makes SymbolicUtils more like a DSL and less like a library for use in other Julia projects.

A response could be "just use isequal". But that would miss my point. Julia is about composability an genericness. (And isequal has a different, specified, semantics.)

(btw. +100 on performance of rules and of load time of SymbolicUtils! This makes it easier to argue for Julia.)

@lairez
Copy link

lairez commented Apr 19, 2023

Same problem here. For example, we cannot use Polynomials.jl with symbolic expression coefficients.

@shashi
Copy link
Member

shashi commented Apr 24, 2023

What are you proposing == do?

If there is a reasonable answer, we can implement it. But a==b is meant to be a value check in most code, so just returning false would be wrong.

I can't find a single instance in Julia where a == b does not return Bool. Maybe there is one, but I can't find it.

julia> missing == 2
missing

that would miss my point. Julia is about composability an genericness.

I hope it's also about correctness.

@lairez
Copy link

lairez commented Apr 24, 2023

What are you proposing == do?

That it does what isequal do?

@shashi
Copy link
Member

shashi commented Apr 24, 2023

The current behavior of == is actually used in packages. (MTK for example).

That it does what isequal do?

This will result in wrong code as I mentioned.

@jlapeyre
Copy link
Author

jlapeyre commented May 5, 2023

What are you proposing == do?
If there is a reasonable answer, we can implement it. But a==b is meant to be a value check in most code, so just returning false would be wrong.

I mean that == always returns a Bool and everyone expects it. Not returning a Bool is more wrong. An exception is missing which is a continual source of headaches and forces everyone to deal with itself.

Because == falls back to ===, you can always call == and expect a Bool. So people rely on it. A lot of people wish it were not this way. I've seen Stefan opine that it was a mistake.

It looks like 1.0 was released a couple of months ago. That's unfortunate. This limits the usability of SymbolicUtils.jl. I'm not sure how to fix it. Maybe deprecate it's use and use a different symbol, like Symbolics.jl does.

Here is a related problem shield Base.require from invalidations when loading Symbolics.jl. SymbolicUtils extends Base.isequal for no reason to do something it is not documented to do. So far, this has required at least this commit to Julia itself. If there is some way to take this back or fix it in a breaking release, I suggest doing it.

I think this kind of thing is quite common. I have some packages that should be released at v1 and this is a good reminder to vet them for gratuitous extensions, which I know I have made in the past. Like everyone else, I was over-enamored with multiple dispatch. It's probably the relatively recent focus on invalidations that has brought this problem to light.

@ericphanson
Copy link

btw Convex.jl also works this way, where == returns a constraint object, and has since ~2014 or so when the package was first developed. I don't have a strong opinion about whether or not it should, but just want to point that out. I think it hasn't caused many issues there because Convex's objects don't really work in generic code so they tend to only be passed into Convex.jl-aware functions.

@shashi
Copy link
Member

shashi commented May 8, 2023

If there is a strong desire for this, I think one can make a number type which behaves in the way need, just as we have LiteralReal and SafeReal.

@syms x::LiteralReal makes it so that x-x remains as-is and is not represented as 0. @syms x::SafeReal makes it so that (x*y)/x is not simplified but x-x is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants