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:
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 ;)
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.
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.
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.
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.
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
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]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With