Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does eq? in Racket work?

At my university we had to work with Racket and since I kind of liked it, I bought the recently published book "Realm Of Racket" from No Starch.

It's great so far, however, I cannot figure out what they mean in Chapter 4 when they try to explain how eq? works:

  1. At first, they explain how equal? compares whether two values consist of identical pieces. OK, no problem, I got that: equal? does pretty much the same thing as Java's equals(someObject) method. If two objects/structs/whatever are the same contentwise, #t is being returned.
  2. Then, I figured, eq? must be the equivalent to Java's == operator, which doesn't compare contentwise but based on references.
  3. This thought seemed to be confirmed by the following sentence in the book: "eq? compares whether changing one structure changes the other structure..." Great! Let's compare it to the following piece of Java code:

    Point p1 = new Point(5, 5);
    Point p2 = p1;
    System.out.println(p1 == p2);   // true, since the reference has been copied.
    System.out.println(p1.x);       // 5
    System.out.println(p2.x);       // 5
    p1.x = 42;
    System.out.println(p1.x);       // 42
    System.out.println(p2.x);       // Accordingly, 42
    

    Let's try this in Racket:

    (define cons1 (cons 1 empty))
    (define cons2 cons1)
    (eq? cons1 cons2)           ;; #t, since the refernce has been copied.
    (set! cons1 (cons 2 empty))
    cons1                       ;; Returns '(2) - as expected.
    cons2                       ;; Still returns '(1).
    

    Why? cons2 points to cons1, which itself points to '(2). Additionally, didn't they just say that they are equal as soon as one changes the other?

Obviously, right now I don't get why this doesn't behave as expected and because of that, I don't see what eq? is doing. Maybe I am wrong and it does not have anything to do with references...

If someone knows about this, please share your wisdom ;)

like image 200
lkbaerenfaenger Avatar asked Jul 10 '13 13:07

lkbaerenfaenger


People also ask

What does EQ mean in racket?

The eq? predicate follows the Racket semantics for opaque or mutable datatypes, such as procedures or vectors, but not for transparent immutable datatypes, such as lists, or transparent solvable types, such as reals. Rosette treats these transparent types as value types, while Racket does not.

What is eqeq?

EQ is the the tool you use to manipulate the frequency content of your mix so that everything is balanced and clear. Here’s a rough break down of where the common mix areas sit on the spectrum.

What is the equal predicate in racket?

The equal? predicate follows the Racket semantics, extended to work with symbolic values. In particular, two values are equal? only when they are eq?, unless a more permissive notion of equal? is specified for a particular datatype.

What is EQing and how does it work?

Think of EQing as sculpting… You’re working with raw material—the existing frequencies of your sound. EQ is essentially a special application of a filter. The qualities of the filter that your EQ uses for its cuts or boost determines a lot about its sound—and the best way to use it.


2 Answers

For a technical explanation of how eq? works, take a look at the current specification, you won't find a more detailed reference. Or simply check Racket's documentation on the subject, in particular the procedures eq?, eqv? and equal?. Regarding your question - the result is as expected and correct in the Scheme code, let's see why. Notice that in this line in Java:

p1.x = 42;

You're modifying the same object that's being pointed at by both p1 and p2. Whereas in this line:

(set! cons1 (cons 2 empty))

You're creating a new, different object and setting cons1 to point to it, but cons2 is still pointing to the old object. You can confirm this, after the previous line, the comparison (eq? cons1 cons2) will return #f.

The point is: the examples are not equivalent. The Java example deals with a single object that's being pointed at by two different references, whereas the Scheme example deals with two objects and two references.

For comparison purposes, here's a Scheme example that's similar to the Java code, and works as you expected because in here we're modifying a single mutable object that's being pointed at by two references:

#lang racket
(require scheme/mpair) ;; `m` stands for "mutable"

(define p1 (mlist 5 5))
(define p2 p1)

(eq? p1 p2)       ;; #t
(mcar p1)         ;;  5
(mcar p2)         ;;  5

(set-mcar! p1 42)
(eq? p1 p2)       ;; #t
(mcar p1)         ;; 42
(mcar p2)         ;; 42
like image 121
Óscar López Avatar answered Sep 23 '22 12:09

Óscar López


Ah: you want to make sure you're changing structure. In your example, it is actually not changing the structure of an existing value, but rather constructing a whole new value and directing cons1 to it. You have the concept right: eq? is pretty much Java's ==.

Just to analogize, here's the mistake in Java form just so you see what went wrong:

int[] lst1 = new int[] { 1 };       // (define cons1 (cons 1 empty))
int[] lst2 = lst1;                  // (define cons2 cons1)
System.out.println(lst1 == lst2);   // (eq? cons1 cons2)
lst1 = new int[] { 2 };             // (set! cons1 (cons 2 empty))
System.out.println(lst1[0]);        // (list-ref cons1 0)
System.out.println(lst2[0]);        // (list-ref cons2 0)

In fact, at this point, you'll want to check for eq?-ness at the end of this: you'll see that the two are no longer eq?: they're two distinct values.

What you really want is to do mutation on the structure, rather than a variable rebinding. In Racket, since lists are immutable, you'll want to use a different data structure that allows for mutation. Vectors are one example datatype that can demonstrate. Let's "Rosetta" this up again so you see the analogy:

(define vec1 (vector 1))          ;; int[] vec1 = new int[] { 1 };
(define vec2 vec1)                ;; int[] vec2 = vec1;
(eq? vec1 vec2)                   ;; System.out.println(vec1 == vec2);
(vector-set! vec1 0 2)            ;; vec1[0] = 2;
(vector-ref vec1 0)               ;; System.out.println(vec1[0]);
(vector-ref vec2 0)               ;; System.out.println(vec2[0]);
like image 35
dyoo Avatar answered Sep 21 '22 12:09

dyoo