**> On Wed, 29 Jun 94 10:52:53 MET DST, mulet@info.emn.fr (Mulet Philippe 40448387) said:
[ how syntax can make code more readable ]
jane goTo: theBallgame With: yourLittleBrother.
A minor syntactic change, and you have an English sentence:
Jane, go to the ballgame with your little brother.
A cute example, just meant to show the possibilities of writing the mythical and proverbial "self-documenting" code.
Mulet> It is true, but remember that historically the syntax of Self is derived Mulet> from the one of Smalltalk.
Of course -- this works for Smalltalk as well. I think it stops working for both Self and Smalltalk when you have to start bringing in strange characters to define things: parens, pipes, colons, and brackets come to mind in Self -- the definition of an object isn't terribly readable (meaning, try to read it as English instead of code).
What I'm trying to say is that this:
jane goTo: theBallgame With: yourLittleBrother.
is more readable than its C++ equivalent:
jane->goTo(theBallgame,yourLittleBrother);
The purpose of the parameters is completely unidentified in C++, but at least _can_ be identified in a keyword kind of message send.
On the other side: Sure, Self has lots of potential for malicious obfuscation, such as swapping the "ifTrue:Else:" slots on the true and false oddballs. But some conversations here at Brown indicate that Self's design enables a highly reflexive mode of computing without recourse to the additional mechanisms provided for reflexive computation in other systems (e.g., the macro systems found in Lisp, Scheme, etc. provide the syntax-creation facilities that arise naturally out of how Self works). I think this is a big plus.
Mulet> I am working on reflective object-oriented languages, and I wonder what Mulet> kind of behavioral reflection you can envisage in Self.
A while back, a brief discussion started here when I asked "What do you really need a macro system for?" in the context of both the C preprocessor and Lisp-like macro systems. The most common useful purpose seemed to be "Creating new syntax." I brought up the point that, in a language like Self, the equivalent of new syntax can be created at the linguistic level. I didn't mean you could create the equivalent of reader macros and make Self look like Algol (for example). More, that the more common defmacro construct wasn't necessary: if-then-else is definable at the user level, as are various kinds of loops, iterators, etc.
The point in the end was that macro-like systems are not per se necessary to accomplish what they are most often used for. Macros are approximately baby forms of full reflective capabilities. I haven't put enough time into figuring out what full reflection adds that you can't get with the builtin stuff. The only thing that comes to mind is implementation information, which you can get with mirrors. And of course, in general, such information is something you _don't_ want to see.
Mulet> Mirrors are not causally connected to the structures they represent.
Hmm? How do you mean? What reflective stuff do you want to do that can't be done with mirrors? This isn't a challenge: I'm trying to figure out how much extra junk you have to add to something like the base Self model to get full reflection. (Part of my research is in stripping down existing object models until you get something that you can use to build exciting new object models. TR available any day now).
[...]
a bad thing per se, but then you must ask: why have message lookup at all? Why not just get rid of the implicit inheritance mechanism and make everyone forward messages on themselves (explicitly (using the Tready of Orlando verbiage here))?
Mulet> I like very much the idea of being able to compute dynamically the parent Mulet> object, this provides the same kind of flexibility as assignable parent slots, Mulet> with dynamicity in addition.
If it provides the _same_ kind of flexibility, it isn't necessary. So what do you mean by "dynamicity?" Could you accomplish what you want by removing actual parent slots and adding a method to handle unresolved messages, resending the message to whatever slot (possibly computed) you wanted to be "the parent?"
Mulet> In this way, you could easily implement a network with delegation link, Mulet> the routage algorithm could be handled by parent methods.
I can't quite follow what you're looking for here. Can you give an example?
[...]
Yes, but, at least in Self, it's infinite recursion introduced by the programmer, not by the system (as it is in the infinite recursion in the classes and metaclasses of Smalltalk and Dylan).
Mulet> First the infinite regression of metalink in Smalltalk is not infinite at all, Mulet> it is closed in a very elegant way by admitting a circularity around Metaclass.
You're right, it isn't infinite. But I see the recursion as being broken, not in an elegant way, but by admitting an arbitrary rule: "This single special-case object doesn't follow the rule that applies to every other object in the system." I don't like rules like that. I find it nicer and more elegant to set up the system without the recursion that requires a special rule just to break out of it (i.e., Self).
Mulet> Then you can easily introduce programmer's infinite recursion in Smalltalk, Mulet> much harder is to stop them.
If you couldn't recurse, it would be a poorer language for it. I feel that a system is easier to understand the fewer special rules it has governing the way it works. Recursion as part of the system's rules is one thing: recursion I write down myself is another. The first is a roadblock to understanding the system. The second is something I have a much greater chance of understanding, because I wrote it.
Mulet> Self contains potential infinite regressions through mirrors and slot descriptors Mulet> that are dealed with lazy creation, but nevertheless it exists.
How do you mean? Last time I checked, any time you asked for a mirror on an object, you always got the same mirror. I.e., mirrors were in a one-to-one correspondence with object identity (hmm -- another way to think of mirrors? The object's "identity"?).
Perhaps some thinking from the land of lazy evaluation is in order. I was impressed by how elegant it is to define an infinite sequence in languages like Haskell. E.g., the list of all even numbers looks like this (excuse the syntax -- this is just the idea):
def allEvens = [ 2, 2 * allEvens]
Mulet> Lisp/Scheme programmers are used for years to use such constructions, called streams. Mulet> They are easily implemented with closures, and probably inspired Haskell. Mulet> In Smalltalk and Self thanks to blocks you can achieve the same result to Mulet> create such infinite series.
Yes, of course. Although I suspect lazy evaluation inspired streams, not the other way around (Lisp programmers implemented Prolog in a few pages of Lisp code, but not until after Prolog had been described). But I don't have any references to verify this.
The point in Haskell, though, is not that you can get the same kind of functionality somewhere else (this is not a Turing-equivalence argument), but that the mechanisms of the language intrinsically provide streams without extra garbage. You can't simply take the next element of the list if you're using closures in something like Scheme In Lisp, (car (cdr allEvens)) is a closure, not a number. In Haskell, it's a number, not a closure. Haskell hides the implementation. Lisp doesn't. In Self, you can do better: an object with code and no argument slots is indistinguishable from an object (analogous to the equivalence in functional programming of literal constants and functions that return constants).
Brook
self-interest@lists.selflanguage.org