3 ms·
> No, it doesn't. If you truly have a single equality operator, no pointer casting (like C or Rust have to solve this problem), and mutability, than you can't h
by catnaroek 10y ago
> No, it doesn't. If you truly have a single equality operator, no pointer casting (like C or Rust have to solve this problem), and mutability, than you can't have object identity as needed.
In Haskell and ML, object identities are first-class values just like everything else, and using the single equality operator on them has exactly the same effect as using eq in Lisp.
> And I think Haskell actually does have generic pointer types, so you can effectively do pointer casting, so it does, in effect, have eq.
Given a reference cell, you can check its contents. But, given a value, you can't get the abstract location where it resides. And this is a good thing!
> Values are purely abstract.
Values are as concrete as it gets in my day-to-day programming. Just like numbers and characters are concrete in Lisp, only with many more values than those.
> We can compare them, create them, mutate them, etc., but they're all objects under the surface [emphasis mine].
In a computer's memory, yes. But the semantics of a programming language isn't a computer's memory. Inability to see past possible implementations is the distinguishing trait of the abstraction-challenged!
> Everywhere else, it's expensive, impractical, and annoying (sometimes you want eq without jumping through hoops to get it).
Using reference cells doesn't seem like “jumping through hoops” to me. More like “explicitly stating my intent”. The language conveniently distinguishes the cell from its contents, even when I forget to do so myself.
> You seem to think that this has nothing to do with mutability, but if you're data's mut, your proposed semantics require deep copy [emphasis mine] on funcall by default, which is unacceptable. So it has everything to do with mutability.
Not necessarily. Another alternative is requiring values to be used at most once. Which is exactly what Rust does with things that don't support shallow copying.
---
To see why equality is completely broken in Lisp, consider the following situation: You have two “lists” of “lists” (in the Lisp sense), and you want to compare the outer “lists” by value (`equal`), but the inner “lists” by object identity (`eq`). What would you do?
(0) In Standard ML, I'd just use the type `foo list ref list`, where `foo` is the element type of the inner lists. Then the equality operator `op=` automagically does the right thing.
(1) In Haskell, it's exactly the same thing as in ML, only with different syntax: `[IORef [Foo]]` and `(==)`.
(2) I don't know what the most convenient solution would be in Lisp, but I was told in #lisp at Freenode that the idiomatic way involves using a `loop` plus some tricky thing to abort the outer “list” comparison as soon as you find a pair of non-`eq` inner “lists”. Whoa.
- qwertyuiop924 10y ago>In Haskell and ML, object identities are first-class values just like everything else, and using the single equality operator on them has exactly the same effect as using eq in Lisp Yes, but you seemed to have an objection to eq existing at all. >Given a reference cell, you can check its contents. But, given a value, you can't get the abstract location where it resides. And this is a good thing! No, it isn't. Because sometimes you need that information. Which is why refcells exist in the first place. >In a computer's memory, yes. But the semantics of a programming language isn't a computer's memory. Inability to see past possible implementations is the distinguishing trait of the abstraction-challenged! Saying that an abstraction needs an escape valve isn't the same as being abstraction-challenged. The value abstraction may be useful in some contexts, but in others, you need to test object identity. Every language, even your beloved ML, has this valve in some way. >Using reference cells doesn't seem like “jumping through hoops” to me. More like “explicitly stating my intent” As is eq an explicit statement of my intent. A different way of stating the intent, but still a statement of intent. >To see why equality is completely broken in Lisp, consider the following situation: You have two “lists” of “lists” (in the Lisp sense), and you want to compare the outer “lists” by value (`equal`), but the inner “lists” by object identity (`eq`). What would you do? First off, why are you quoting lists? ML lists are the same under the hood. Second off, that just seems like an argument for typing, dynamic dispatch, and a ref type. Which is doable, but I don't know if any of the typing systems have it. I mean, we have a pointer type, but I don't think the equality semantics for it are well-defined. As for the most convenient solution in lisp, do you want convenient, or efficent? Once again in scheme, convenient looks like this: (define (test-lists a b) (if (not (= (length a) (length b))) #f (fold and #t (map (lambda (x y) (eq? x y)) a b)))) But doing it fast requires a simple change to abort the outer list comparison: (define (test-lists a b) (if (not (= (length a) (length b))) #f (call/cc (lambda (return) (map (lambda (x y) (or (eq? x y) (return #f))) a b)))) It took one extra line of code and wasn't "tricky" at all. Sure, we don't have types, so it's not automatic, but it's still pretty simple.
- catnaroek 10y ago> Yes, but you seemed to have an objection to eq existing at all. I have an objection to equality (and, more generally, value manipulation) being broken. If you can't define it correctly, then don't define it at all. > No, it isn't. Because sometimes you need that information. Which is why refcells exist in the first place. You still misunderstand. Reference cells only let me manipulate the physical identity of objects. Normal values don't have a physical identity. Just like numbers don't have a physical identity in Lisp - except this applies to a much larger class of values in ML. > Every language, even your beloved ML, has this valve in some way. There is no abstraction violation in ML: You can't query the physical location of a list in ML. You can only query the physical location of an object whose state happens to be a list. A `foo list` and a `foo list ref` are very different things, and the type checker will let you know if you accidentally conflate them. > Second off, that just seems like an argument for typing, Yes. (Just to be clear: whether it's static or dynamic typing matters very little.) > dynamic dispatch, Dynamic dispatch is only one possible implementation strategy. Another is monomorphization, which removes all second-class polymorphism at compile time, but requires whole program analysis. Standard ML doesn't have first-class polymorphism. In any case, as a language user, I absolutely don't care how this is implemented. That's a problem for compiler writers. As Dijkstra said, the specification acts as a contract between users and implementors, defining precisely the rights and responsibilities of each part. > and a ref type. Yes. > As for the most convenient solution in lisp, do you want convenient, or efficent? Both. This dichotomy is an argument by lack of imagination. > It took one extra line of code and wasn't "tricky" at all. Have you ever tried actually proving anything about code that uses call/cc? Effectively, you need to first CPS-convert your code, and only then prove whatever you want to prove about the result of such CPS conversion. If testing two data structures for equality requires call/cc, the language is broken. > Sure, we don't have types, so it's not automatic, Let me repeat: This isn't an argument over static types. What I'm asking for can be done correctly in a dynamically typed setting too. Lisp, for some reason that's hard to understand, chooses not to. > but it's still pretty simple. Writing code that's generic over a comparator (more generally, generic over the realization of some algebraic or relational structure) is tedious and easy to do wrong in Lisp.