When you program in etoys you first draw something, then you convert it to an object, then you display a Viewer which enables you to observe the properties (heading, x position, y position etc.) and write scripts for the object. You can also produce a second object and then pass variables between objects. For example, you can pass the heading of a rocket's steering wheel from the steering wheel to a script which belongs to the rocket.
There is stuff happening here which I haven't seen in other programming systems. What other system enables you to visually pass a variable from one object to another and then lets you observe it running and change it while it is running? What is about Squeak / Smalltalk that makes this possible when it is apparently not possible in other programs like Game Maker or Flash?
Specifically, I want to understand what is happening behind the scenes. What classes are involved? And in connection to an earlier blog about OOPs: Is etoys programming an example of "class create" or "object use"?
This involves looking behind the etoys visual interface to the Smalltalk classes and code which is making it possible.
Understanding this might have important implications wrt how we first teach programming to our youth. Can OOPs be taught to youth before procedural programming?
I previously wrote a blog about OOPs in which I quoted from mark guzdial's blog extensively. Mark is saying that OOPs is harder than procedural, with specific reference to "class create" being harder but not so much wrt "object use":
- "Object-use" means instantiating objects, applying methods to objects, writing new methods.Charles Stewart left a comment on my blog suggesting I look at prototype based programming, he left a link to a wikipedia article about it. When I looked that up it said that a prototype was cloning of instances and called it class-less and it made reference to the concept coming from Self and Squeak
- "Class-create" means creating new classes and solving problems by writing different methods in different classes (distributing responsibility).
Then when I read about the background to morphic I came across this (User-Scripting has since evolved into etoys):
"In the User-Scripting style, you construct surface graphics by directly assembling standard Morphic parts -- e.g. Rectangles, Images, Joysticks, etc., by dragging them from a Parts Bin and arranging them as desired, and then you add user-defined state and behavior by adding instance variables and writing methods for "Players" who represent the individual morphs you wish to script.How to look behind the scenes?
The user thus does not directly subclass any particular kind of Morph, but rather she assembles Morphs and gives them special state and behavior by associating them with "Players", which are the fundamental user-scriptable object types for User Scripting."
- IntroductionToMorphic/Morphic-Styles-Scripting
If you click on the light gray debug halo, the one with a spanner icon, a menu appears which enables you to inspect the morph. Then by clicking on extension you can see the important classes involved in the programming side of things. Then you select (highlight) the class name you want more information about and followed by Alt+b to open the browser on that class. This contains all the information you want.
Smalltalk has the open-ness and tools to enable you to look under the hood which is a significant other advantage for learning. Any proprietary program which hides or blocks information is reducing the potential to learn important under the hood knowledge.
By poking around in this way I discovered that the important classes involved are MorphExtension and the Player class, which are described as follows:
Object subclass: #MorphExtension
instanceVariableNames: 'locked visible sticky balloonText balloonTextSelector externalName isPartsDonor actorState player eventHandler otherProperties'
classVariableNames: ''
poolDictionaries: ''
category: 'Morphic-Kernel'
MorphExtension provides access to extra instance state that is not required in most simple morphs. This allows simple morphs to remain relatively lightweight while still admitting more complex structures as necessary. The otherProperties field takes this policy to the extreme of allowing any number of additional named attributes, albeit at a certain cost in speed and space.
Model subclass: #Player
instanceVariableNames: 'costume costumes'
classVariableNames: 'BiggestSubclassNumber TimeOfError'
poolDictionaries: 'References'
category: 'Morphic-Scripting'
The fundamental user-scriptable entity. Always represented by a user-specific subclass of Player; instance vars and methods relate to user-defined structures.
costume is a Morph, the primary morph I am currently wearing for graphical display.
Scripts are defined in subclasses of Player. These are UniClasses.
Messages in scripts are sent to Players. A Player may delegate to its costume, or to an object the costume suggests. Or, a Player may designate some other object to receive the script messages it does not understand. (see doesNotUnderstand:)
The UnscriptedPlayer subclass of the Player class is also important:
Player subclass: #UnscriptedPlayer
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Morphic-Scripting'
My instances are Player objects that have not been scripted, and which hence do not require a unique scripts dictionary, etc. As soon as needed, I am transformed automatically into a unique subclass of Player.
I looked at the before and after situation of adding a script to a rocket object. The important changes are in bold.
Before:
a MorphExtension (3333) [externalName = rocket ] [player = an UnscriptedPlayer (1733) named rocket] [other: (rotationCenter -> 0.5@0.5) (forwardDirection -> 0.0) (baseGraphic -> Form(70x64x32))]
After:
a MorphExtension (3333) [player = a Player66 (1733) named rocket] [other: (rotationCenter -> 0.5@0.5) (trailDotSize -> 6) (forwardDirection -> 0.0) (baseGraphic -> Form(70x64x32))]
So, when I added the script the UnscriptedPlayer class changes into Player66 class
Then if I inspect Player66 class in the browser I can see that it has the script I wrote and that the Steer object has been passed into the Player class of the rocket object:
script1
self forward: 1.
self setPenSize: 1.
self turn: Steer getHeading / (self beNotZero: 3)
Conclusions:
Even though I've learnt a lot I'm still not certain about whether this is "class create" or "object use". I still have a lot to learn about OOPs before I can clearly answer this question or even judge whether it is an important question to ask.
However, I do have a much clearer picture of what is happening behind the scenes when I do etoys scripting
Probably the main thing I have gained from this exercise was the knowledge that Squeak has good tools for quickly inspecting objects and classes, that as a system it encourages learning in this sort of way.
3 comments:
I think this is what Self people called instance based or prototype based programming (same concept as JavaScript and used heavily by the famous Prototype AJAX library). eToys is written in Squeak Smalltalk, but working in eToys is very different than classic Smalltalk working in class browsers (also fun and open, just different).
Great post! I've also just started digging into Etoys in detail and I hadn't cottoned onto the way the scripts were translated into Smalltalk. One more piece of the puzzle :-)
Etoys is very different and in some ways superior to Squeak from a certain context.
Its more traditional and it has has lot of wonderful ideas without the concern of efficiency that Squeak has. It just feels more concrete. I've only scratched the surface and this post is a great under the hood look.
Post a Comment