Why are `not-equal?` and similar negated comparisons not built into Racket?
Asked Answered
R

4

13

In Racket (and other Schemes, from what I can tell), the only way I know of to check whether two things are not equal is to explicitly apply not to the test:

(not (= num1 num2)) 
(not (equal? string1 string2))

It's obviously (not (that-big-of-deal?)), but it's such a common construction that I feel like I must be overlooking a reason why it's not built in.

One possible reason, I suppose, is that you can frequently get rid of the not by using unless instead of when, or by switching the order of the true/false branches in an if statement. But sometimes that just doesn't mimic the reasoning that you're trying to convey.

Also, I know the negated functions are easy to define, but so is <=, for example, and that is built in.

What are the design decisions for not having things like not-equal?, not-eqv?, not-eq? and != in the standard library?

Robbins answered 24/8, 2016 at 4:42 Comment(4)
Such kind of decisions by language designers are just a matter of findind a delicate balance between the contrasting objectives of keeping the language simple and providing useful primitives. Where do you stop in provinding primitives that can be defined in terms of others? For instance in the strict cousin of Scheme, Common Lisp, there is a /= operator for integer equality, but not an operator like not-equal or nequal etc. The designers of Scheme were evidently more interested in purity of the language, those of Common Lisp in practicity, but they had to stop somewhere...Spermicide
I bet that if the unicode character ≠ had been available back in the day, it would have been included. The earliest discussions on Scheme available online is the rrrs mailing list archive: groups.csail.mit.edu/mac/projects/scheme/rrrs-archive.htmlYorktown
I couldn't find anything on negation, but you might find the archive interesting in its own right.Yorktown
Wow, I'm only through '84, but those emails are kind of a treasure. Thank you, @soegaard.Robbins
P
18

First, you are correct that it is (not (that-big-of-a-deal?))1

The reason Racket doesn't include it out of the box is likely just because it adds a lot of extra primitives without much benefit. I will admit that a lot of languages do have != for not equal, but even in Java, if you want to do a deep equality check using equals() (analogous to equal? in Racket), you have to manually invert the result with a ! yourself.

Having both <= and > (as well as >= and <) was almost certainly just convenient enough to cause the original designers of the language to include it.

So no, there isn't any deep reason why there is not any shortcut for having a not-eq? function built into Racket. It just adds more primitives and doesn't happen to add much benefit. Especially as you still need not to exist on its own anyway.

1I love that pun by the way. Have some imaginary internet points.

Pinko answered 24/8, 2016 at 5:13 Comment(1)
Imaginary internet points? From someone at PLT? I'll take it!Robbins
P
9

I do miss not having a not= procedure (or as mentioned in @soegaard's comment), but not for the reasons you think.

All the numeric comparison operators are variadic. For example, (< a b c d) is the same as (and (< a b) (< b c) (< c d)). In the case of =, it checks whether all arguments are numerically equal. But there is no procedure to check whether all arguments are all unequal—and that is a different question from whether not all arguments are equal (which is what (not (= a b c d)) checks).

Yes, you can simulate that procedure using a fold. But still, meh.


Edit: Actually, I just answered my own question in this regard: the reason for the lack of a variadic procedure is that you can't just implement it using n-1 pairwise comparisons, unlike all the other numeric comparison operators. The straightforward approach of doing n-1 pairwise comparisons would mean that (≠ 1 2 1 2) would return true, and that's not really helpful.

I'll leave my original musings in place for context, and for others who wonder similar things.

Precipitous answered 24/8, 2016 at 12:55 Comment(3)
(define (unique? . xs) (= (set-count (list->set xs)) (length xs)))? Can be made more effiecent but that's the idea?Shockheaded
It took me a second to realize that you meant n-1 pairwise comparisons with , in particular. Right? I appreciate your musings--I was hoping you would share some.Robbins
@Shockheaded Huh, nice. If using a hash set, that's even amortised linear time. The constant factor is bigger than the "n-1 pairwise comparisons" approach (and it's linear-space rather than constant-space), of course, but it's still technically feasible.Precipitous
S
4

Almost all of the predicates are inherited by Scheme, the standard #!racket originally followed. They kept the number of procedures to a minimum as a design principle and left it to the user to make more complex structures and code. Feel free to make the ones you'd like:

(define not-equal? (compose1 not equal?))
(define != (compose1 not =))
; and so on

You can put it in a module and require it. Keep it by convention so that people who read you code knows after a minute that everything not-<known predicate> and !-<known-predicate> are (compose not <known-predicate>)

If you want less work and you are not after using the result in filter then making a special if-not might suffice:

(define-syntax-rule (if-not p c a) (if p a c))

(define (leafs tree)
  (let aux ((tree tree) (acc 0))    
    (if-not (pair? tree)
            (+ acc 1) ; base case first
            (aux (cdr tree)
                 (aux (car tree) acc)))))

But it's micro optimizations compared to what I would have written:

(define (leafs tree)
  (let aux ((tree tree) (acc 0))    
    (if (not (pair? tree))
        (+ acc 1) ; base case first
        (aux (cdr tree)
             (aux (car tree) acc)))))

To be honest if I were trying to squeeze out a not I would just have switched them manually since then optimizing speed thrumps optimal readability.

Shockheaded answered 24/8, 2016 at 20:4 Comment(5)
I’ve noticed that you like to use the #! alias for #lang, and I haven’t said anything in the past, but I kinda want to bring up that the Racket documentation actively discourages that alias. “Use of this alias is discouraged except as needed to construct programs that conform to certain grammars, such as that of R6RS.”Despond
@AlexisKing The problem with just writing racket is that it's not obvious if it's a surface language or a compiler suit one writes about. Wikipedia messes this up really good so it's not obvious what it writes about. I would have loved the two to have completely different names (not scheme). As to using the short I often mention several and #!r6rs has no long form in other implementations.Shockheaded
@Shockheaded I think she's saying you should write #lang racket and not #!racket or just racket.Precipitous
@ChrisJester-Young Even SO get this wrong. The information on the tag racket is clearly about #lang racket so asking a question about beginning student tagged with racket is clearly an error. Unfortunately drracket seems to be an alias which clearly isn't the language but a software tool. They cannot be the same.Shockheaded
@Shockheaded I agree that the tagging is not very precise. However, I would say that the community of users who would answer questions about #lang racket isn't so far off from the community of users who would answer questions about DrRacket, Beginning Student, etc., so it's not considered broken enough to fix.Precipitous
L
1

I find that one easily can define != (for numbers) using following macro:

(define-syntax-rule (!= a b) 
  (not(= a b)))

(define x 25)
(!= x 25)
(!= x 26)

Output:

#f
#t

That may be the reason why it is not defined in the language; it can easily be created, if needed.

Leadin answered 24/8, 2016 at 17:17 Comment(2)
As @Chris mentioned, this is not as generic as =, because it only operates on two numbers. On the other hand, = is a variadic function and tests for numeric equality on all of its arguments.Pinko
It's not good to use a macro where you can use a procedure. Since you rewrite to (not (= a b)) you still get two appliactions and using it the same way as = won't work eg. (map != '(1 2 3) '(4 5 6)). In addition you have 10+ more to do.Shockheaded

© 2022 - 2024 — McMap. All rights reserved.