1.3.5.2 Am I a backwards thinker, with my brain stuck in C mode?
Jason
Am I a backwards thinker, with my brain stuck in C mode?
Mark Craig Chu-Carroll:
That's pretty much what's going on.
I don't mean that to be insulting. The problem is, you're having a very hard time grasping the ideas of Dylan, because you're trying to think about them in terms of the concepts of more traditional languages like C and C++.
In my various attempts to explain Dylan to non-Lisp people, I've discovered that the biggest hurdle is getting people to see past the ideas of pointers, and call-by- reference versus call-by-value parameter passing. Those concepts are all borrowed from the Algol family languages, which Dylan isn't part of.
In Dylan, you pass *objects* around as parameters. Down in the implementation, that's probably implemented as pointers, but it doesn't have to be. The key thing is that when you pass a parameter, you're actually passing an *object*, which has identity. If you insist on thinking of it as a pointer, you're going to get confused. You're not passing a *value*, and you're not passing a *pointer*; you're passing an *object*.
An object comes with a set of methods, exported from its home module, that allow you to perform operations on that object. The implementor of the object's class may give you access to operations that alter the object in some way, if that makes sense for the object. That's fine - those operations still preserve the object's *identity*.
In C++, you worry about passing a pointer to an object because that gives you the ability to alter the objects identity, by writing a new object into the memory that was formerly used by the original object. You have the ability to alter its *identity* through it's pointer. In Dylan, you can't do that. Object identity is guaranteed to be preserved. The attributes of the object might be altered, but the identity remains the same.
Without the ability to alter an objects identity, all that you can do to it are perform the operations that the implementor of that object gave you access to. That is, anything that you can do to alter an object from outside of its defining class is something that the implementor decided was OK for you to do! You can't "damage" the object, because the only things that you can do to it are the things that are "safe" for the definition of the object.
For example, if you perform a function on a mutable set, you can add items to that set. That's because it makes sense to be able to perform alterations on a mutable set, so its mutation operations are accessible to you. But if you get a non-mutable set, you won't be able to add elements to it, because the implementor won't export any mutation operators that act on it.
So what it all really comes down to is you end up with a different model of safety. In languages like Dylan, guarantees of safety are provided by the implementor of the class in terms of the operations provided for acting on instances of the class. That's sufficient for making a strong guarantee of safety in Dylan, because of its preservation of identity. If you're using to a language like C++, it seems like that's not enough - because in C++, to make a program safe, you need to worry about preserving identity *as well as* the kinds of safety that you need in Dylan.
This all means that the style of programming in Dylan is radically different. Safe programming styles in C++ are very value oriented - the operate more on values than on objects. They copy things all over the place, and use value equality rather than object equality for comparison. In C++, you design objects to be used that way - you expect things to be copied like crazy. You write operations on objects in ways that don't make any real effort to preserve identity, because you know that no one is going to rely on it. In Dylan, you expect copying to be very rare, and write your programs in a fashion that is strongly reliant on the preservation of identity.