6 ms·
> So by "object identity," you mean doing equality comparisons with pointers instead of actually value equality. Just don't use eq. Use equal instead. The prob
by catnaroek 10y ago
> So by "object identity," you mean doing equality comparisons with pointers instead of actually value equality. Just don't use eq. Use equal instead.
The problem goes further than that. “equal” doesn't really mean equal - it only means equal until the next mutation. Not very helpful in a language with unrestricted mutation.
> Yeah, that's true of every programming language in existance.
It isn't true in ML or Haskell. In those languages, mutation is strictly opt-in, on a per-type basis. When you don't use mutation, equality is by value. When you use mutation, equality is by object identity. You can't “accidentally distinguish” between two copies of the same immutable value.
> What, do you want every equal object to be mutated, along with the one you mutated? That's just crazy talk.
I want to distinguish between objects meant to be mutated and values not meant to.
> Well, if you're doing FP, than you aren't mutating values anyway. So just don't use eq.
If I'm doing functional programming, I want to mutate as little as possible, but certain things require mutation to be done efficiently. What I want is the language to help me distinguish between functional values and imperative objects.
(0) ML: If you attempt to mutate a value, you get a compile-time error.
(1) Racket: If you attempt to mutate a value, you get a runtime error. Pretty sensible in a dynamic language.
(2) Common Lisp: Only primitives are values, and mutating anything else is permitted.
- lispm 10y ago> Pretty sensible in a dynamic language. For a dynamic language I would expect to be able to change objects, in many ways. Otherwise it would not be 'dynamic', but 'static'. > (2) Common Lisp: Only primitives are values, and mutating anything else is permitted. But not necessarily possible. In this Common Lisp example there is no way to change the closure bindings. CL-USER 107 > (let ((f (let ((a 10) (b 10)) (lambda (x) (+ x a b))))) (* (funcall f 1) (funcall f 2))) 462
- catnaroek 10y ago> For a dynamic language I would expect to be able to change objects, in many ways. That objects must support mutation was never in question. All general-purpose languages have mutable objects. The point is whether you also want to have compound values. I think the answer should be “yes”. > In this Common Lisp example there is no way to change the closure bindings. Point taken. But I hope you aren't suggesting me to Church-encode compound values. Even ignoring the usability issues of Church encoding, there's still the problem that Common Lisp will treat two otherwise extensionally equal functions as different objects. :-p
- qwertyuiop924 10y ago...but, the functions are side-effectless, and guaranteed not to change, so you can just equal? the results. Basically, you can use function as const.
- deleted 10y ago[deleted]
- catnaroek 10y ago> ...but, the functions are side-effectless, and guaranteed not to change, so you can just equal? the results. Just because (f 1) and (g 1) are equal, it doesn't automatically follow that f and g are equal. Functions shouldn't be comparable for equality at runtime, because extensional equality of functions is undecidable.
- qwertyuiop924 10y agosigh not what I meant. I MEANT that you can model immutable variables using functions.
- catnaroek 10y agoWill `equal` work correctly on immutable values modeled as functions? No? I guessed just as much.
- qwertyuiop924 10y agowell, you'd have to unwrap the values first. The call would be: (equal variable (constant)) whether you think that's acceptable or not is up to you.
- catnaroek 10y agoThe encoding of compound values as functions is actually more complicated than that: ;; pair constructor (defun make-pair (x y) (lambda (f) (funcall f x y))) ;; projection from pairs (defun former (x y) x) (defun latter (x y) y) ;; test pairs (defvar foo (make-pair 1 2)) (defvar bar (make-pair 1 2)) ;; equality test (defun equal-pairs (x y) (and (equal (funcall x #'former) (funcall y #'former)) (equal (funcall x #'latter) (funcall y #'latter)))) ;; try it (equal-pairs foo bar) And this was just for pairs of primitive values. You don't even want to imagine how painful it is to represent arbitrarily complex compound values this way.
- tome 10y ago> For a dynamic language I would expect to be able to change objects, in many ways. Otherwise it would not be 'dynamic', but 'static'. That's not what "dynamic" vs "static" means. Are you suggesting that "static" languages don't allow you to change objects?!
- qwertyuiop924 10y ago>The problem goes further than that. “equal” doesn't really mean equal - it only means equal until the next mutation. Not very helpful in a language with unrestricted mutation. Yeah, once again, true in all programming languages that allow any mutation. >It isn't true in ML or Haskell. In those languages, mutation is strictly opt-in, on a per-type basis. When you don't use mutation, equality is by value. When you use mutation, equality is by object identity. You can't “accidentally distinguish” between two copies of the same immutable value. So you want eq, unless the object hasn't been mutated, in which case you want equal. Easy enough. The following example is in CHICKEN, a lisp I am more familiar with, but translation to Common Lisp should be simple: (define (fn-equal? a b) (if (or (get a 'mut) (get b 'mut)) (eq? a b) (equal? a b))) (define-syntax fn-set! (ir-macro-transformer (lambda (exp inject compare) (let ((var (cadr exp) (val (caddr exp)) `(begin (set! ,var ,val) (put! ,var 'mut #t)))))) now, just use fn-eq? and fn-set! instead of the included, and it should keep track of what vars have been mutated. This doesn't protect against other setters: They would also have to be wrapped. >I want to distinguish between objects meant to be mutated and values not meant to. I don't know if Common Lisp has const. If it doesn't, you could always just create functions that return the immuatable. It's not fast or pretty, but it works. >Racket: If you attempt to mutate a value, you get a runtime error. Pretty sensible in a dynamic language. No, it's not. Scheme does the best by having mutation separate from single assignment, but pushing immutability on everybody by default is crazy. Which is why, save cons cells (which are bad enough), Racket doesn't do so. Observe: (define x 7) (set! x "oh noes!") ;no error (display x);prints "oh noes!"
- catnaroek 10y ago> So you want eq, unless the object hasn't been mutated, in which case you want equal. Easy enough. The following example is in CHICKEN, a lisp I am more familiar with, but translation to Common Lisp should be simple: I want the language to make it impossible to use eq on values. When I'm manipulating values, I don't care about their location in memory, and I don't want it to be possible to care. > If it doesn't, you could always just create functions that return the immuatable. It's not fast or pretty, but it works. This is actually even worse, because Common Lisp will gladly tell me two function objects are different, even if they are otherwise extensionally equal. > No, it's not. Scheme does the best by having mutation separate from single assignment, but pushing immutability on everybody by default is crazy. I said Racket for a reason. > Which is why, save cons cells (which are bad enough), Racket doesn't do so. Observe: `x` is a variable, not a value. The value 7 isn't being mutated, 7 is always 7, of course! What has changed is that the variable `x` now stands for a different value.