Skip to content

builtin: add :ne, inequality over two evaluated constants - #92

Closed
brian-slashguard wants to merge 1 commit into
google:mainfrom
slashguard:upstream-ne
Closed

brian-slashguard wants to merge 1 commit into
google:mainfrom
slashguard:upstream-ne

Conversation

@brian-slashguard

Copy link
Copy Markdown

Mangle has :lt/:le/:gt/:ge but no not-equals, so every inequality must be written as negation over a domain the program derives first.

This adds :ne at the same four registration points :lt uses: symbol declaration, arg modes, the decision case, and the relation type.

The relation type is the non-obvious one — without it the predicate parses and reaches evaluation, then fails type inference with cannot find assignment that works for premise :ne(A,B), which reads like a broken implementation rather than a missing registration.

Verified: differs(A,B) :- colour(A), colour(B), :ne(A,B). over three colours yields the six ordered pairs of distinct values. go build ./... is clean.

Happy to adjust naming or the type bound if you'd prefer something narrower than AnyBound.

Mangle has :lt/:le/:gt/:ge but no not-equals, so every inequality must be written as
negation over a domain the program derives first. That is a recurring authoring and
review cost for a common construct.

Adds :ne at the four registration points :lt uses — symbol, arg modes, decision case,
and relation type. The relation type is the non-obvious one: without it the predicate
parses and reaches evaluation, then fails type inference with 'cannot find assignment
that works for premise :ne(A,B)', which reads like a broken implementation.

Verified: differs(A,B) :- colour(A), colour(B), :ne(A,B) over three colours yields the
six ordered pairs of distinct values.
@google-cla

google-cla Bot commented Aug 2, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@burakemir

Copy link
Copy Markdown
Contributor

Hey there! First, an administrative remark: this project lives on codeberg now. I can review it here, but I'd prefer to not depend on github.

Now, about the PR itself:

  1. why is the available inequality operation not good enough?
    You can do differs(A,B) :- colour(A), colour(B), A != B today.

  2. Equality and inquality should be polymorphic and deal with subtyping through refinement. This is implemented (kind-of) for Eq and Ineq ast nodes, which are what is backing = and !=. So comparing a number to a string will give a type error. It does not seem like a good idea to duplicate all of that for a built-in predicate.

In fact, built-in predicates are specific to types - there should be a ":float:lt" etc for floats, but that was missing which I will add now.

@brian-slashguard

Copy link
Copy Markdown
Author

Withdrawing this, and the premise it was raised on was wrong.

I claimed Mangle had no not-equals and that every inequality therefore had to be negation over a
derived domain. That is false. != is a lexer literal in the grammar beside <, <=, =, > and
>=; your README uses it three times in its introductory example; and — the part I find hardest to
explain — the project I raised this for was already using != in four shipped rules while its
own notes recorded that no such predicate existed. I did not check my own repository before
proposing a builtin to yours.

Your type objection was also right, and it is the better argument of the two. With optional type
checking enabled != refuses 1 != "1" and :ne accepts it, so this would have added a second
inequality that is weaker than the one already there, with nothing marking which was which. Two
operations that differ only in what they fail to catch is a worse outcome than the one I was trying
to fix.

We are migrating our 27 :ne call sites to != and removing the patch from our fork. Measured
before proposing that: all 107 of our checks reach identical verdicts either way.

The process failure was the same as #93's and worth stating once more plainly: "works after the
patch" was never shown to be "fails before the patch", and neither PR carried a regression test that
would have made the gap visible. Thank you for asking for a reproducible failure instead of taking
the diff at face value — twice.

I have filed #94 separately, about :list:member declaring ArgModeOutput while Decide handles a
bound first argument. That one I did reproduce before writing it up, and the shapes and error text
are in the issue. Given this PR and #93, please treat it with the scepticism the previous two earned.

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

Successfully merging this pull request may close these issues.

2 participants