asc-community / asc-community/AngouriMath

Pending breakages: changes agreed or arguable that cannot ship before a major version

Open
#1,019 6 comments 0 reactions 0 assignees View on GitHub
Opinions wanted Proposal
Dominant language
C#
Stars
831
Forks
79
Avg merge
3h 23m
Merged PRs (30d)
309

Description

> *"Maybe an issue on pending breakages would be appropriate. Remember to note to yourself to follow through on the next major version. Also, maybe noting down to promote experimentals and removing obsoletions would be appropriate."*
> — @Happypig375, [#1009](https://github.com/asc-community/AngouriMath/pull/1009)

This is that issue: the list of changes that are **agreed or arguable but cannot ship in a minor**, so that 3.0 has a docket rather than a memory. Nothing here is scheduled and nothing here is a defect report.

A change belongs on this list when it moves the **value** of existing user input, or removes a published member. A changed answer *has* gone in a minor here before, on the stated principle that correctness outranks compatibility — but every one of those was a wrong answer becoming right. Everything below is a deliberate convention change, which is a different thing and should not borrow that licence.

---

## 1. `implies` becomes right-associative

Raised on #1009. The grammar folds `implies` to the **left**; mathematics, Lean, Coq, Agda, Haskell and CSharpMath.Evaluation all read `→` to the right. Ours is the outlier, and CSharpMath reading it differently from us is a live inconsistency across a boundary we do not control.

It cannot go in a minor because it re-values existing text:

```
"false implies true implies false" today False after True
```

Nothing fails to compile; answers move silently. #1009 makes the printer state the grouping the grammar actually has, so the change is a one-line flip in `ToString.Discrete.Classes.cs` (from `<=`-on-the-right to `<=`-on-the-left) plus the grammar, and the round-trip tests written there keep it honest.

**Also decide `provided` at the same time.** It folds right today, which #1009 documents and tests. If `implies` moves to the right, the two agree and `Syntax.md`'s rule becomes "`^`, `implies` and `provided` group to the right"; that is worth settling in one go rather than twice.

## 2. `FreeVariables`, `VarsAndConsts`, and a property that does not exist

Raised on #989.

> *"VarAndConsts is what it says: free variables and constants (pi and e). "Used variables" looks like another property."* — @Happypig375

Measured on `master` at `6b93b401`, against that definition. The documented example is `Lambda(x, x * 2 + sin(y * pi))`:

| | today | by the definition above |
|---|---|---|
| `Vars` | `x, y` | — |
| `VarsAndConsts` | `x, y, pi` | **`y, pi`** — `x` is bound |
| `FreeVariables` | `y` | `y` |

So `VarsAndConsts` returns bound names, and its XML example pins that. It is wrong for `lambda`, which is the **one** binder the library already honours elsewhere — and wrong for the other five (`sum`, `product`, `integral`, `limit`, `derivative`, set-builder) in the same way:

```
sum(k, k, 1, 3) Vars = k VarsAndConsts = k FreeVariables = k
```

`sum(k, k, 1, 3)` is `6`. Nothing about that value depends on `k`, and all three properties say it does.

The shape of the fix, which is three published properties moving at once:

- **`FreeVariables`** — extend "bound" from lambda to every binder. Its own doc comment currently defines bound as *"a parameter of some outer lambda"*, which was accurate when written and is not now that `CalculusOperator` and `ConditionalSet` both declare a `Var` (#986).
- **`VarsAndConsts`** — free variables and constants, per the definition above.
- **A new property for "used variables"** — every name that occurs, bound ones included. That is what `Vars`/`VarsAndConsts` do today, so the behaviour is not lost, it is renamed to something that describes it.

Part 1 of #989 — the `%1` placeholder escaping — was a defect on any definition and is already fixed (#1000). This is part 2, which was always a design call.

## 3. Promote or retire the experimental features

`MathS.ExperimentalFeatures` (`Sources/AngouriMath/Convenience/Experimental/`) is **11 public members**, documented as *"features that might become stable in the future, but are not guaranteed to do anything useful or correctly at the current moment"*:

`SolveDiophantineEquation` · `DecomposeRational` ×2 · `GetSineOfHalvedAngle` · `GetCosineOfHalvedAngle` · `ExpandSineArgumentMultiplied` · `ExpandCosineArgumentMultiplied` · `ExpandSineOfSum` · `ExpandCosineOfSum` · `SymbolicFormOfSine` · `SymbolicFormOfCosine`

Each wants one of three verdicts, and the third is why this is on a *breakage* list:

- **promote** — move to the stable surface under its proper name, and commit to the answer;
- **keep** — it is still not guaranteed, and say what would settle it;
- **remove** — it has not earned a place, and deleting a published member is a major-version act.

Worth doing as one pass rather than one member at a time, because "experimental" is a promise about the whole namespace and it decays if it is never spent.

Note also that `Experimental` is a folder **inside the kernel package**, not a separate package as #746 describes it — see #1008.

## 4. Removing obsoletions — a standing practice, not a backlog

There are currently **zero** `[Obsolete]` members in the tree: 2.0.0 removed the lot. So there is nothing to schedule, and the note to keep is the *rule*:

**Anything marked `[Obsolete]` during 2.x is removed at 3.0.** An obsoletion that survives a major stops meaning anything, and 1.x accumulated a set precisely by never spending them.

## 5. Already-known decisions that belong on the same docket

`AGENTS.md` names three under *"Decisions only a major version may take"*, all still open:

- **#204** — roots versus fractional powers. `SimplificationContract.md` calls it *"open and deliberately a major-version question"*.
- **#326** — the syntax for `piecewise`. #327's simplification half shipped; the syntax was never touched and no design is recorded.
- **#721** — unify `Codomain` with a `provided … in RR` condition. Named on #746 as a consumer of the retired expression-metadata item, and still open.

And one more that is breaking by construction:

- **#217** — complex infinity. It changes `1/0` from `NaN`, and every consumer branching on `NaN` is blast radius.

---

## 6. `|` stops meaning `or`

Raised on #1212, where the invitation was explicit:

> *"You may change the meaning of `|` on the next major version if that's more mathematically appropriate. Consider this for all the other parsed inputs as well."* — @Happypig375

`|` is an alias for `or` today. It is the only spelling in the grammar that already means something **else** in mathematics — divides (`a | b`), "such that" (`{x | P(x)}`), "given" (`P(A | B)`), and the delimiter in `|x|` — so input written by a mathematician is read as a disjunction and answered as one. Measured on `281e0d0c`:

| written | reads today as |
|---|---|
| `2 \| 6` | `2 or 6` — a disjunction of two numbers |
| `{ x \| x > 0 }` | `{ x or x > 0 }` — a `FiniteSet` of **one** element, that element a disjunction |

The second is what decided it: `{x | x > 0}` is ordinary set-builder notation, and we read it as a one-element set. Well-formed, silent, and nothing like what was written.

Nothing is lost by retiring the alias, since `or` is the primary spelling and is what everything prints as. **`&` is deliberately not on this list**: it is equally a programming convention, but no mathematical meaning competes for it, and the test applied throughout was "does this symbol already mean something else to a mathematician", not "would a mathematician have chosen it".

It cannot go in a minor because it re-values existing text, and the proposal is that it should not go in **one** major either:

```
"x > 0 | x < -1" today a disjunction after a divisibility statement
```

Nothing fails to compile; the answer moves silently. So two steps rather than one:

1. **First major:** `|` becomes a parse error, with a message naming `or`. Loud rather than silent, and it costs a release of waiting to remove the entire class of quietly-wrong results.
2. **The major after:** `|` means **divides**, and **"given"** inside an expectation or probability — the scoping @Happypig375 proposed on #1212, where `E`/`P` introduce the meaning and their closing bracket ends it. `divides` (#1220) then becomes the alias and `|` the canonical spelling.

If one step is preferred, the refusal step is the half I would keep regardless of how long it lasts.

## 7. `!=` is refused rather than silently read as a factorial

Raised on #1212's survey, filed as #1225.

`a != b` is accepted today and parses as `a! = b` — the factorial of `a`, equated to `b` — because `!=` is not a token, so the lexer takes `!` as the postfix factorial and `=` as equality. Measured on `281e0d0c`:

| written | reads today as |
|---|---|
| `a != b` | `a! = b`, an `Equalsf` |
| `a! = b` | `a! = b` — the identical tree |

`!=` is how inequality is spelled in most of what a caller writes all day, so somebody reaching for the familiar one gets a factorial equation and no diagnostic. It is the shape of #733, where `floor(x)` came back as a product with an undeclared variable named `floor`, and which was fixed by refusing the name rather than letting a fallback swallow it.

`!=` cannot be *given* its usual meaning while `!` is the postfix factorial, since `a != b` is genuinely ambiguous between the two readings. So it should be refused, with a message naming `<>`, exactly as `arcsinh` is refused with a message naming `arsinh` — which leaves `a! = b` working when written with the space, the only unambiguous way to say it.

Strictly this is a defect rather than a design question and could ship sooner; it is here because it removes input that parses today, which is the same bar as everything else on this list.

---

## What this issue is for

To be **read before the next major version is named**, in the same way #746 is read before any version is named. It is not a plan and it does not commit anyone.

Anything added here should say what moves, what the old and new values are, and why a minor cannot carry it.

Contributor guide

Open the contributing guide

Research direction

Start with AGENTS.md and the linked issues to understand which major-version decisions are still open. Then inspect ToString.Discrete.Classes.cs, Syntax.md, and Sources/AngouriMath/Convenience/Experimental/ alongside the referenced round-trip tests. Done means each proposed breakage has a recorded decision, scope, and validation plan before the next major version is named.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend-api-design, compilers, release
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.