Repository navigation
Description
a != b is accepted and parses as a! = b — the factorial of a, equated to b.
"a != b".ToEntity() → a! = b [Equalsf]
"a! = b".ToEntity() → a! = b [Equalsf] the identical tree
!= is not a token, so the lexer takes ! as the postfix factorial and = as equality, and the result is well-formed. There is no diagnostic, because nothing is malformed — it is simply not what was written.
Why this is worth refusing rather than shrugging at. != is how inequality is spelled in C, C#, Java, Python, JavaScript, Rust and most of what a caller of this library writes all day. The spelling here is <>, which is fine, but somebody who reaches for the one they know does not get an error — they get a factorial equation, and every downstream answer is a correct answer to a question they did not ask.
It is the same shape as #733, where floor(x) came back as the product of an undeclared variable named floor with x and (floor(x) - 3 = 0).Solve("x") answered { 3 / floor }. That was fixed by refusing the name rather than letting the implicit-multiplication fallback swallow it, and the reasoning transfers exactly.
What I think the fix is. != cannot be given its usual meaning while ! is the postfix factorial — a != b is genuinely ambiguous between not (a = b) and (a!) = b, and picking either silently is how we got here. So it should be refused, with a message naming <>, exactly as arcsinh is refused with a message naming arsinh:
there is no operator
!=: inequality is written<>. It is refused rather than read as a factorial followed by an equality, which is whata != bwould otherwise mean.
That keeps a! = b working when it is written with the space, which is the only way to say it unambiguously anyway.
Scope. One alternative in the lexer plus a test, and a BREAKING-CHANGES.md row, since input that parses today would stop parsing. Raised out of the notation survey on #1212, but independent of the design question there and worth doing whichever way that goes.
Happy to take it if you want it — leaving it unassigned in case you would rather it be somebody's first contribution, since it is self-contained and the precedent to copy is right there in the grammar.
🤖 Generated with Claude Code
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTodo
Activity
Record the spellings that are known to read as something else (#1226)
Rafael-SOWNet commentedon Sep 9, 2026
How about "/=" as alternative? (or "<>" XD)
Happypig375 commentedon Oct 2, 2026
For v3 we may also want to introduce "not equal" as a proper node. See what syntax would be best here...
Rafael-SOWNet commentedon Oct 2, 2026
Agreed. Measured on master
94ba5ff6:Solve("x")x <> 3not (x = 3)not x = 3x \neq 3{ x : not x = 3 }a != ba! = ba! = ba! = ba ≠ b≠So the relation exists only as a negated equality. It prints as
not x = 3, not as<>, and the solver doesn't read it.A node,
NotEqualsf(a, b), would print as one relation, be solved (x ≠ 3isCC \ { 3 }, orRR \ { 3 }over the reals), and be matched by rules as a relation of its own.not (a = b)would simplify to it, so that there is one canonical form.For the syntax I'd follow Mathematica, which has the same postfix
!:≠,<>and!=, with!=read as one token.a != banda!=bare then the relation, and the factorial equation is writtena! = b, with the space.a!=bchanging meaning is why this waits for v3.<>in ASCII,≠in Unicode output, and\neqin LaTeX, as now.This would replace item 7 on #1019, which currently says to refuse
!=. I'll rewrite it there.🤖 Generated with Claude Code
Happypig375 commentedon Oct 2, 2026
Sure, approved.
Rafael-SOWNet commentedon Oct 2, 2026
Recorded as approved in item 7 on #1019.
🤖 Generated with Claude Code