Archive for August 2013
I remember reading about Einstein. A source of his genius was how he re-evaluated criteria.
It follows that if everybody looks at the same criteria, and they look at it in the same way, and they all think the same way, then people will reach the same conclusions. And because being different in this world can be painful, most people are not only in incapable of thinking differently, they aspire not to. Einstein looked at criteria differently and found something he liked.
He decided that the speed of light was a constant. That’s it. Many things follow from that one decision. Everybody else looked at the criteria of light and saw the same thing. The conventional wisdom saw nothing new where light was concerned. In his conflict with (eval), I think Alan Kay did something similar. He took something axiomatic, unpacked new criteria, and solved the problem he faced.
Appendix II of the article The Early History Of Smalltalk describes in detail how Kay created the interpreter for Smalltalk-72. Whatever people might say about the origin of Smalltalk, those four pages demonstrate it was hugely influenced by Lisp. Colin Putney once told me that many languages are the combination of two others. “C was invented because Assembly wasn’t enough like Fortran. Smalltalk was invented because Simula wasn’t enough like Lisp”. In the Smalltalk-72 era, Kay had glommed onto Simula. But how could it be synthesized with Lisp?
The battle to be waged was against (eval). Page 92 of Land Of Lisp has:
(defparameter *foo* ‘(+ 1 2))
The problem was that he couldn’t just use eval. As I understand it, the half page of syntax he had (as a part of a bet) left him almost without resources. He just couldn’t add eval willy-nilly. This problem led him to re-evaluate what the criteria of eval were and find a solution. He had two tools at his disposal: time, and, space.
Of course he didn’t say it that way. He said: “When I set out to win the bet, I realized that many of the details that have to be stated explicitly in McCarthy’s elegant scheme can be finessed.”
I like that. Finessed. To me that means he realized he could push the details out to the context and away from the syntax. Instead of locating all the information required in the syntax, many of the details would be ambient. They’d be around. He’d change space by changing the scope. And he’d change time by pushing the processing to late, late, late in the cycle. Dynamic binding is a choice about time and nothing else. First class objects are objects that exist at runtime. They have to be fully formed at runtime. If they were processed earlier, then they wouldn’t have to be.
He said he needed: “a protected way to access and change the relationships of the static and dynamic environment”. No problem. We will change space by confining the scope to that inside an object. Put the processing inside an object, a context object, and then you have complete control over what happens. “I extend(ed) the eval by distributing it”
If you evaluate the (eval foo) as above, then it will happen. Period. No buts. A message in Smalltalk is the location of a letter and not the actual letter. It’s a request for something to happen. And before it happens there are options: the object can ignore it, throw an exception, delegate, or do anything it likes with the request.
Appendix II also talks about the process counter (PC) in both objects and how they have to be synchronized. “Delayed receipt requires the PC counter of the receiver and the sender to be manipulated at the same time.” I’m not quite under that yet. As there is a PC counter in every context object, it shouldn’t take too long.
Interestingly, Matz didn’t have this same kind of resistance when adding eval to Ruby. Avi Bryant says that it can be used for doing Lisp-like programming in the Ruby language:
Since I often see the programming language world (at least, the small enlightened part of it) as being divided into the LISP camp and the Smalltalk camp, it's nice to see something like Ruby strike a decent balance between the two. My particular style of writing Ruby is an interesting example of this: Ruby has the peculiarity that code inside a class definition, but outside a method definition, is evaluated in the context of the class object. In other words, this code class Foo def some_method(x) x * 2 end print self end will print "Foo". This means that it is possible to write class methods that use eval to act more or less like macros, but macros that can be overridden by subclasses, etc.
Sometimes what looks whole and incontestable is made up of smaller parts. Sometimes there are new criteria waiting to be found. Like muons.
I took a crack at drawing the object model for Squeak. Feedback welcome.
For contrast, here are some object model diagrams of Ruby provided from Paolo Perotta’s excellent book Metaprogramming Ruby.
I read “Metaprogramming Ruby” asking myself why Rubyists didn’t have a debugger like we have. What was different about our object model that allowed us to have our debugger?
It’s not just a deeper object model. It’s a more rigorous implementation of the language. Ruby has a “create as needed” philosophy. Smalltalk has a “consistency of implementation” philosophy.
The things that stand out to me about Ruby are instance variables and eigenclasses. Chapter 5 of the Bluebook says metaclasses were created to give classes customizable initialization. That appears to me to be what Ruby doesn’t have – constructor methods. It has :new and :new() (OK, ActiveRecord has :create for automatic generation of setters and getters). You cannot initialize an object and set the values of instance variables in one message.
If I read this rightly, you need to initialize instance variables. i.e.:
class MyClass def my_method @v = 1 end end obj = MyClass new obj.my_method
On page 35 of “Metaprogramming Ruby” it says:
“If Bill, hadn’t called obj.my_method(), then obj would have no instance variable at all.”
Initialization for ivars. That’s interesting.
Constructor methods in Smalltalk are class side methods. You can add class side methods in Ruby. Each time you do so, an object called an eigenclass is created. This follows the “create as needed” philosophy that I see as Matz’s attitude to language design. As opposed to the “consistency of implementation” that I see as Kay’s.
And that’s why we have a debugger as we do an nobody else does. We have a deep – perhaps four tier deep – object model that is quite rigorously implemented. These two things seem to me to provide a greater objective reality against which a specific program can be judged. I think that is a definition of reflection. A layer of abstraction provides a basis of comparison.
“No object in a class-based system can be self sufficient; another object (its class) is needed to express its structure and behaviour. This leads to a conceptually infinite meta-regress: a point is an instance of class Point, which is an instance of metaclass Point, which is an instance of metametaclass Point, ad infinitum. On the other hand, in prototype-based systems an object can include its own behaviour; no other object is needed to breathe life into it. Prototypes eliminate meta-regress.” (page 5)