Self(ID:1361/sel006)

Prototyping OOPL 


Small, dynamically-typed object-oriented language, based on prototypes and delegation. Objects may inherit state, and dynamically change their patterns of inheritance. Threads.


OOL for exploratory programming based on a small number of simple and concrete ideas: prototypes, slots, and behavior. Designed for expressive power and malleability. SELF combines a pure, prototype object model with uniform access to state and behavior. Unlike other languages, SELF allows objects to inherit state and to change their patterns of inheritance dynamically.  

Developed by Sun Microsystems and Stanford University and made available by the SELF Group in 1995.

Sun is no longer involved with the SELF project.  

Places
People:
Structures:
Related languages
O => Self   Influence
Smalltalk => Self   Influence
Self => CECIL   Influence
Self => Dylan   Influence
Self => Glyphic Script   Influence
Self => Kevo   Influence
Self => Klone   Incorporated some features of
Self => NeoLogo   Influence
Self => Newton Script   Influence
Self => Self-91   Evolution of
Self => Us   Augmentation of

References:
  • David Ungar and Randy B. Smith "Self: The Power of Simplicity" view details Abstract: SELF is an object-oriented language for exploratory programming based on a small number of simple and concrete ideas: prototypes, slots, and behavior. Prototypes combine inheritance and instantiation to provide a framework that is simpler and more flexible than most object-oriented languages. Slots unite variables and procedures into a single construct. This permits the inheritance hierarchy to take over the function of lexical scoping in conventional languages. Finally, because SELF does not distinguish state from behavior, it narrows the gaps between ordinary objects, procedures, and closures. SELF’s simplicity and expressiveness offer new insights into objectoriented computation. Extract:
    Object-oriented programming languages are gaining acceptance, partly because they offer a useful perspective for designing computer programs. However, they do not all offer exactly the same perspective; there are many different ideas about the nature of object-oriented computation. In this paper, we present SELF, a programming language with a new perspective on objects and message passing. Like the Smalltalk-80 language, SELF is designed to support exploratory programming, and therefore includes runtime typing (i.e. no type declarations) and automatic storage reclamation. But unlike Smalltalk, SELF includes neither classes nor variables. Instead, SELF
    has adopted a prototype metaphor for object creation. Furthermore, while Smalltalk and most other object-oriented languages support variable access as well as message passing, SELF objects access their state information by sending messages to “self,” the receiver of the current message. Naturally this results in many messages sent to “self,” and the language is named in honor of these messages. One of the strengths of object-oriented programming lies in the uniform access to different kinds of stored and computed data, and the ideas in SELF result in even more uniformity, which results in greater expressive power. We believe that these ideas offer a new and useful view of object-oriented computation.
          in SIGPLAN Notices 22(12) December 1987 (OOPSLA '87) view details
  • Lieberman, Henry; Stein, Lynn and Ungar, David "Treaty of Orlando" pp43-44 view details DOI Extract: Of Types and Prototypes: The Treaty of Orlando
    Of Types and Prototypes: The Treaty of Orlando
    WHEREAS among the purported benefits of object oriented technology are flexibility and dynamicism, the vast majority of existing object oriented languages have type hierarchies which require conformance to strictly hierarchical ideals and impose penalties on innovation;
    WHEREAS these issues have a brief but intense history of discussion and debate: [Lieberman 86] was a reaction to these problems of traditional models, proposing an alternative object sharing mechanism based on the idea of delegation - or forwarding of messages - to replace the traditional implicit inheritance mechanisms, and suggesting that modeling shared behavior through objects representing prototypes could replace the traditional model of abstract classes; [Ungar 87] reacted to the complications engendered by traditional class and inheritance mechanisms by proposing a mechanism also based on prototypes, which model did not propose explicit delegation, but which also shared - through ?dynamic inheritance? - the essential characteristics of allowing dynamic sharing patterns and attaching idiosyncratic behavior to individual objects, resulting in a drastic simplification of the Smalltalk object model, using the PARENT link to more flexibly serve for the more complex class/subclass/instance protocol; [Stein 87] attempted a rapprochement between the delegation and inheritance views by noticing that the class/subclass relationship already captured delegation, if a shift was made to a class-only representation rather than Lieberman?s instance-only representation; and in unifying classes and instances, extended the class-instance relationship to allow idiosyncratic ?instances,? providing individual object behavior and more dynamic sharing patterns than traditional inheritance models;
    WHEREAS these, our proposals, have helped to elucidate the following three independent dimensions along which this sharing mechanism - which some call delegation and others inheritance, but which is in any case fundamental to object oriented systems - can be examined, namely: FIRST, whether STATIC or DYNAMIC: When does the system require that the patterns of sharing be fixed? Static systems require determining the sharing patterns by the time an object is created, while dynamic systems permit determination of sharing patterns during runtime, when an object actually receives a message. SECOND, whether IMPLICIT or EXPLICIT: Does the system have an operation that allows a programmer to explicitly direct the patterns of sharing between objects, or does the system do this automatically and uniformly? Explicit delegation (or inheritance) allows the ability to delegate only a single method, rather than ?anything that can?t be handled locally.? and THIRD. whether PER OBJECT or PER GROUP: Is behavior specified for an entire group of objects at once, as it is with traditional classes or types, or can idiosyncratic behavior be attached to an individual object? Conversely, can behavior be specified/guaranteed for a group?
    and WHEREAS there exist a spectrum of programming situations ranging from large production programs where efficiency and reliability are the primary criteria, and which change relatively slowly, to exploratory research programming, which demands flexibility and rapid response to change:
    RESOLVED, that different programming situations call for different combinations of these features: for more exploratory, experimental programming environments, it may be desirable to allow the flexibility of dynamic, explicit, per object sharing; while for large, relatively routine software production, restriction to the complimentary set of choices - strictly static, implicit, and group-oriented - may be more appropriate;
    RESOLVED, that a second, and separate, fundamental mechanism can be distinguished: the class-instance, or generator, relationship, in which one object (the ?class?) determines the type of the objects it generates, and that although this relationship has always in the past been associated with static, implicit, per group systems, these restrictions are neither necessary nor inherently desirable, and it is time to free the class-instance relationship from its traditional subservience and consider it as a separate and equal partner in the building of object oriented systems;
    RESOLVED, that as systems follow a natural evolution from dynamic and disorganized to static and more highly optimized, the object representation should also have a natural evolutionary path; and that the development environment should itself provide more flexible representations, together with tools - ideally automatic - for adding those structures (of class, of hierarchy, and of collection, for example) as the design (or portions thereof) stabilizes;
    and RESOLVED, that this agreement shall henceforth be known as the TREATY OF ORLANDO.
    Henry Lieberman
    Massachusetts Institute of Technology
    Lynn Andrea Stein
    Brown University
    David Ungar
    Stanford University
    6 October 1987
    Orlando, Florida, USA

          in SIGPLAN Notices 23(05) May 1988 ('OOPSLA '87: Addendum to the proceedings) view details
  • Lee, E. "Object Storage and Inheritance for SELF, a Prototype-Based ObjectOriented Programming Language". Engineer's thesis, Stanford University view details
          in SIGPLAN Notices 23(05) May 1988 ('OOPSLA '87: Addendum to the proceedings) view details
  • Chambers, C., and Ungar, D. "Customization: Optimizing Compiler Technology for SELF, a Dynamically-Typed Object-Oriented Programming Language" view details
          in Proceedings of the SIGPLAN '89 Conference on Programming Language Design and Implementation (PLDI 89) view details
  • Chambers, C., Ungar, D., and Lee, E. "An Efficient Implementation of SELF, a Dynamically-Typed Object-Oriented Language Based on Prototypes" pp49-70 view details
          in SIGPLAN Notices 24(10) October 1989 incorporating the Proceedings of the Conference on Object Oriented Programming Systems Languages and Applications, New Orleans (OOPSLA 89) view details
  • Chambers, C., Ungar, D., Chang, B., and Hölzle, U. "Parents are Shared Parts of Objects: Inheritance and Encapsulation in SELF" view details
          in Journal of Symbolic Computation, 8, 1989 view details
  • Tylutki, G. "What Good is self?" pp77-?? view details
          in Computer Language Magazine 7(11) view details
  • Chambers, C. "The design and implementation of the SELF Compiler, an optimizing compiler for objectoriented programming languages," Report number STAN-CS-92-1420, Department of Computer Science, Stanford University, March 1992 view details Abstract: Object-oriented programming languages promise to improve programmer productivity by supporting abstract data types, inheritance, and message passing directly within the language. Unfortunately, traditional implementations of object-oriented language features, particularly message passing, have been much slower than traditional implementations of their non-object-oriented counterparts: the fastest existing implementation of Smalltalk-80 runs at only a tenth the speed of an optimizing C implementation. The dearth of suitable implementation technology has forced most object-oriented languages to be designed as hybrids with traditional non-object-oriented languages, complicating the languages and making programs harder to extend and reuse. This dissertation describes a collection of implementation techniques that can improve the run-time performance of object-oriented languages, in hopes of reducing the need for hybrid languages and encouraging wider spread of purely object-oriented languages. The purpose of the new techniques is to identify those messages whose receiver can only be of a single representation and eliminate the overhead of message passing by replacing the message with a normal direct procedure call; these direct procedure calls are then amenable to traditional inline-expansion. The techniques include a type analysis component that analyzes the procedures being compiled and extracts representation-level type
    information about the receivers of messages. To enable more messages to be optimized away, the techniques include a number of transformations which can increase the number of messages with a single receiver type. Customization transforms a single source method into several compiled versions, each version specific to a particular inheriting receiver type; customization allows all messages to self to be inlined away (or at least replaced with direct procedure
    calls). To avoid generating too much compiled code, the compiler is invoked at run-time, generating customized versions only for those method/receiver type pairs used by a particular program. Splitting transforms a single path through a source method into multiple separate fragments of compiled code, each fragment specific to a particular combination of run-time types. Messages to expressions of these discriminated types can then be optimized away in the split versions. The techniques are designed to coexist with other requirements of the language and programming environment, such as generic arithmetic, user-defined control structures, robust error-checking language primitives, source-level debugging, and automatic recompilation of out-of-date methods after a programming change. These techniques have been implemented as part of the compiler for the SELF language, a purely object-oriented language designed as a refinement of Smalltalk-80. If only pre-existing implementation technology were used for SELF, programs in SELF would run one to two orders of magnitude slower than their counterparts written in a traditional non-object-oriented language. However, by applying the techniques described in this dissertation, the performance of the SELF system is five times better than the fastest Smalltalk-80 system, better than that of an optimizing Scheme implementation, and close to half that of an optimizing C implementation.
    These techniques could be applied to other object-oriented languages to boost their performance or enable a more object-oriented programming style. They also are applicable to non-object-oriented languages incorporating generic arithmetic or other generic operations, including Lisp, Icon, and APL. Finally, they might be applicable to languages that include multiple representations or states of a single program structure, such as logic variables in Prolog and futures in Multilisp.
          in Computer Language Magazine 7(11) view details
  • Ungar, David "Self" view details Extract: The language self
    The language Self was originally designed with to be a successor to Smalltalk. Self was designed by removal; we removed classes and variables from Smalltalk and tried to see if we could still program in it. Peter Deutsch inspired us to unify Smalltalk's seven kinds of variables into one kind of access, which we based on inheritance.
    In Self, prototypes are prototypical; a prototype has exactly the same set of slots as any copy. In fact, there is no linguistic way to distinguish them. For example, the prototypical panel object would have slots named "members" "topic" "name" and "parent", and each copy would have exactly the same slots. Any shared information would reside in its ancestors.
    Each prototype implements a "copy" message, which "clones" (shallow-copies) the prototype and then performs any initialization required.
    Self unifies variables and methods in a fashion that makes code more reusable. Every identifier in Self code is interpreted as a message-send, and if no receiver is given, the message is sent to "self." Either data or code can be found in a slot as the result of sending a message. Data is just returned, and code is run. Assignment is accomplished when sending a message with one argument finds a slot containing a special assignment primitive operation.
    In Self, objects inherit from objects, and inheritance performs exactly one function; that of allowing the same information to be in two places (objects) at the same time. For example, consider a method that is inherited. You could just copy that method down to every object that inherited the method, and the system would behave the same, until you needed to change the method. Then you would have change every copy, or replace every copy somehow. Inheriting one copy of a method makes it easier to change, as well as grouping methods into class-like clumps (called traits objects in Self) for easy comprehension.
    And, since Self lets you freely intermix methods and data, data can be inherited just as methods. Such inherited data slots enable objects to share state (somewhat like class variables) more flexibly. Since in Self, objects inherit from objects, and since there are no classes to enforce structural conformance, objects can easily alter their parents at runtime.
    This dynamic inheritance capability turns out to be an excellent vehicle for implementing objects whose behavior changes completely as they move among a small set of states. For example, a window that can either be expanded or iconified can be easily implemented in Self by switching a parent pointer between an object holding state and inheriting behavior for the icon, and another object representing the expanded version. Data common to the states can be conveniently stored in the child. I know of no other programming construct as well-suited to this sort of example.

          in Smith, Randall B (moderator) "Prototype-based languages (panel): object lessons from class-free programming" OOPSLA 94 pp102-112 view details
  • Smith, Randall B.; Maloney, John; Ungar, David "The Self-4.0 user interface: manifesting a system-wide vision of concreteness, uniformity, and flexibility" Proceedings of the tenth annualConference on Object Oriented Programming Systems Languages and Applications Austin, Texas, United States pp47-60 1995 view details Abstract: Manipulating programs is hard, while manipulating objects in the physical world is often easy. Several attributes of the physical world help make it comprehensible and manipulable: concreteness, uniformity, and flexibility. The Self programming system attempts to apply these attributes to the world within the computer. The semantics of the language, the efficiency and fidelity of its implementation, and the architecture of its user interface conspire to make the experience of constructing programs in Self immediate and tangible. We describe the mechanisms used to achieve this goal, and illustrate those mechanisms within the context of an extended programming task. DOI
          in Smith, Randall B (moderator) "Prototype-based languages (panel): object lessons from class-free programming" OOPSLA 94 pp102-112 view details
  • Ungar, David "Annotating objects for transport to other worlds" pp73-87 view details Abstract: In Self 4.0, people write programs by directly constructing webs of objects in a larger world of objects. But in order to save or share these programs, the objects must be moved to other worlds. However, a concrete, directly constructed program is incomplete, in particular missing five items of information: which module to use, whether to transport an actual value or a counterfactuaI initial value, whether to create a new object in the new world or to refer to an existing one, whether an object is immutable with respect to transportation, and whether an object should be created by a low-level, concrete expression or an abstract, type-specific expression. In Self 4.0, the programmer records this extra information in annotations and attributes. Any system that saves directly constructed programs will have to supply this missing information somehow. DOI Extract: Introduction
    Computer Scientists usually think of a computer program as a static description, abstraction, or model of a dynamic computation. This formulation has led to significant accomplishments: methods for the analysis of complex problems, a clean separation of programming from execution, frameworks for reasoning about a computation's correctness and speed, ways to capture intangible intentions and plans (as declarations), and pedagogical advantages for people with mathematical backgrounds. Given these benefits, it is not surprising that most object-oriented programming systems require some descriptive information right at the start. For example, before a Smalltalk, C++, Eiffel, or Beta programmer can begin to experiment with a point object, he must first step back and define a description, the class of all possible points. Or, before a C++ programmer can use a hand-drawn icon in her program, she must figure out some way to get the picture data into her program.
    But, it may not always be appropriate to enforce description before experimentation. While some problems may demand a lot of prior analysis, others may demand more exploration of the solution space. Some programmers may work best by designing first and coding later, but others, especially more casual programmers solving smaller problems, may work better by iterating construction and design. We are interested in building a programming environment that ultimately can support both styles in order to foster creativity, exploration, and accessibility.
    The Self object-oriented programming system strives for a more concrete and direct feeling [Smith 1], [Smith 2]. We hope that this emphasis will make it easier to explore and create desired behaviors and that the more descriptive information can be added later, if so desired. That is why the language is based upon prototypes instead of classes, why the implementation hides the information needed to describe objects' formats, and why the user interface supports hyperdirect manipulation. That is also why a Self program is considered to be a collection of live, running objects, rather than static text. Since the programmer directly manipulates objects by adding methods or variables and by setting variables to their initial values, we say that the Self system and its applications are built out of directly constructed objects or programs. Such a concrete notion of a program can bring the programmer closer to his task, as if he were touching them instead of looking at them.
    However, any system that posits a less-descriptive model of programming must address some thorny issues, and one of the thorniest is saving programs. A program (or cluster of objects) constructed in one world of objects (a.k.a. a snapshot, or virtual image) needs to be saved in some archival form such as a source file so that it can be moved into another world of objects. While the code can just be copied, the web of data references defies simple transcription. For example, the user might build a stack object out of a stack pointer and a vector, then try to move the stack into another world of objects. A literal move would try to move the vector object and would literally copy the stack pointer value, when what the user really intends is to create a new vector and set the stack pointer to zero. A system that attempts to move directly constructed objects like this stack must confront the gap between the extensional information present in memory and the intentional information it needs.
    Recognizing and characterizing this gap was the hardest part of building the Self 4.0 transporter, a subsystem intended to move directly constructed Self programs from one world to another. In the course of developing the transporter we were forced to confront many situations in which different kinds of missing information had to be supplied. In other words, by taking on the task of recreating a more descriptive, intentional program from an existential, extensional web of objects, we had to rediscover what would have been conveyed in the description that was not contained in the objects. At this writing (1995), the transporter has gone through one major redesign, has been in daily use for 2 years in its present form, and is part of a publicly available system.2 This paper summarizes what we have learned about the gap between conventional, descriptive programs and directly constructed ones. We hope that other researchers who are faced with similar problems can profit from a discussion of the difference between the information needed to perform a given object-oriented computation and the information needed to transport the objects that perform that computation into another world.
    The rest of this paper introduces our modules in section 2, discusses the pieces of descriptive information needed to transport programs in section 3, presents some measurements in section 4, recounts prior work in section 5, and summarizes itself in section 6.
    Extract: What is a module? Changes or Pieces
    What is a module? Changes or Pieces
    As mentioned above, this work addresses a need to move programs from one web of objects to another. Accordingly, it would seem to follow that a program should be modelled as a change to a web of objects. Alternatively, a program could be modelled as pieces of stuff (objects, slots) in the world of objects. For example, in the "changes" model, a programmer could point to a method and ask "What module caused this object to be created?" whereas in the "pieces" model a programmer would ask "To which module does this object belong?" The "changes" model holds out the promise of a powerful, rigorous framework: If a program could be represented as a set of changes, and if these changes were appropriately commutative, it would be easy to transport it.^ The "pieces" model seems more concrete, since an object or slot is easier to picture than a change.
    Given our obsession with concreteness, it is not surprising that we chose the "pieces" model. Two smaller concerns also contributed to this choice: minimizing the research risk in an essential portion of infrastructure, and the desire to be able to bootstrap the system by reading source files into an empty world. We therefore opted to save the pieces as Self source expressions suitable for bootstrapping and to continue to manage changes with a conventional source code control system, the Revision Control System (RCS) [Tichy].
    Although other systems may opt for the more powerful "changes" model, they will still have to cope with a scarcity of intentional information in directly constructed programs. For example, under the "changes" model, the stack pointer is considered to be an addition to the stack instead of a part of it, but this paradigmatic shift does not resolve the dilemma of whether to save its current value or its initial value. Although we took the simpler path, these lessons should also help more adventuresome travellers.

    Extract: What belongs to a module? Objects or slots?
    What belongs to a module? Objects or slots?
    What is the unit of object-oriented programming? In most programming textbooks writing a program is described as creating one or more new classes. For example, a program to construct palindromes might add a new Palindrome class. In Self the same operation would be the creation of one or more new objects, that could either function as prototypes, or
    repositories of shared behavior called traits [Ungar]. So the equivalent operation would be the creation of a new object to serve as the prototypical palindrome, and another object to be the parent of all palindromes holding the shared traits.
    In a Smalltalk image there are many instances that are not part of a program, but are created by the program. These objects need not be transported, unlike the classes that comprise the program. Similarly, a Self world also contains objects created by the program that should not be saved by the transporter. However since Self unifies classes and instances, its incidental objects are harder to distinguish from the essential ones that must be written as part of the program. The Self 4.0 transporter observes this distinction by only attempting to save objects that are accessible from the lobby, which is the root of the global name space. These saved, globally accessible objects are called well-known objects. For example the prototype palindrome and its parent would be well-known, but most copies of the prototype would not.
    On the other hand, in addition to the creation of new well-known objects, the introduction of new functionality to a system often requires the extension of old well-known objects with new attributes. For example, a Smalltalk program to find palindromes might add a method to class String called isPal-indrome. It could not subclass String, because it must operate with strings created by other programs. In Self, a slot can be used to hold a method, a local or global constant, or a class, instance, or local variable. Therefore the Self analogue to extending existing classes is the addition of slots to existing objects (see Figure 3). So for the transporter, a program is not a set of objects, but rather is composed of individual slots. Such a fine granularity adds a degree of flexibility that seems useful for any singly dispatched object-oriented language.
    Extract: Maintaining Identity: Reference vs. Creation
    3.3  Maintaining Identity: Reference vs. Creation
    Although a slot's annotation may direct the transporter to write out its actual contents, just what that means remains open to question. Does the programmer intend for the slot to create a new object or merely to refer to some existing one? If two slots point to the same object, only one of them had better create the object; the other should be initialized to refer to the contents of the first.
    In other words, every time the transporter follows a reference to an object, it must decide if that reference or some other one creates the object. If an object is reachable by more than one reference, there is no way to tell which one was intended to create it without additional information.
    Accordingly, each object is annotated with its creator slot, a backpointer to the slot that is intended to create it. (The backpointer actually points to an object containing the slot's holder and name.) In Self the inheritance graph funnels through an object called the lobby that plays a role somewhat like Class Object in Smalltalk. Global variables are expressed in Self by putting slots in the lobby or one of its parents. Although the creator annotation only points one level back, by transitively following these annotations the transporter can find the complete path from the lobby to an object, if it exists. The transporter then uses this path to install the slot when it is read in.
    The creator annotation also lets the transporter distinguish between well-known objects created by reading in source files, and clones created by running programs. In Self, a copy of the original prototypical set will look the same as the original, and will even have the same annotations. However, since its putative creator slot (set) will not point back to the clone, the transporter can tell the difference between objects it must file out and those that merely have been incidentally created. Thus, the creator information helps identify a well-known object without a costly search for all references to the object. Extract: When Does Identity Matter?
    3.4  When Does Identity Matter?
    Most objects include externally observable mutable state, and so their identities matter. For example, if two variables refer to the same stack, they are causally connected; pushing a value with one will affect the value popped from the other. Since programs rely on causal connections, the transporter must preserve them and the creator annotations described above accomplish this. However, some objects behave immutably; although such an object may cache information, there is no way to cause an externally visible side-effect upon it. For example a point object in Self behaves immutably, so that adding two points yields a new point containing the sum, rather than changing one of the old ones. For such objects, their values are more important that their identities, which are not observable anyway. (For example, points override the identity message, ==, to send equality, =.) With such objects, it is better to avoid maintaining their identity, and to just transport out a new copy for every reference. Otherwise, for example, every slot containing a particular color object would be initialized as referring to whatever slot was annotated as the creator of that color. The transporter must preserve an immutable object's value rather than its identity.
    Unlike the other information discussed up to this point, externally observable mutability is associated with an object's abstract type, and follows inheritance patterns. For example all of Self's number objects (small integers, big integers, floats) are externally immutable and all of them also inherit from a common ancestor. Because of mutability's correspondence with inheritance patterns, the transporter does not use annotations to encode this property. Instead, it is encoded in an attribute, isImmutableFor-FilingOut. Objects inherit a default value of false, but immutable objects override this slot with true. Implementation aside, the important point is that whether or not an object is observably immutable cannot be effectively determined extensionally and must be supplied with extra information.

    3.5  Abstract vs. Concrete Creation
    Although it is always possible to create an object by concretely enumerating its slots, such a low-level expression is not acceptable when there is a more, succinct abstract expression that will do. For example, a point could be filed out as ( | x<- 3 . y <- 4 . parent* = traits point I) but 3@4 is far better. In addition to its conciseness and legibility, the more abstract expression is much more robust in the face of change to the implementation of points. For example, 3@4 would still work if points were reimplemented as polar, but the slot expression would not.
    Unfortunately, this choice between concrete and abstract representation cannot be made by simply inspecting the objects. Each object must be asked if it is willing to supply an abstract representation and if so, what that expression is. As in the case for mutability, the abstract representation depends on the object's position in the inheritance hierarchy (its user-defined abstract type). Accordingly the transporter sends each object a message, storeStringlfFail:, to find such an expression if it exists.
    One last piece of information is needed because of Self's prototypical model. The prototype must always be created concretely. If the prototypical point were defined as 0 @ 0 in the source file, when reading the file the "@" method would attempt to copy the prototypical point, which would not yet exist! Thus for Self, there is also a message to identify the prototype that is needed for storeStringlfFail: and which should not be created abstractly (called store-StringNeeds). If the object cannot be created abstractly, the transporter creates a new object slot-by-slot. The availability and construction of data-type-specific abstract initialization expressions is the last piece of generally missing information about an object that must be supplied by the programmer.
    Extract: "Classes" in Self: Inheriting Structure
    3.6  "Classes" in Self: Inheriting Structure
    In a minimalist language, some information that has been omitted from the language design may have to be reintroduced as intentional information for saving a program. Although the Self language includes inheritance for sharing state and behavior, it does not include any mechanism to inherit containers of state. For example, the prototypical morph object (a graphical element in our user interface framework) contains many slots that every morph should have, and some mechanism is needed to ensure that their presence is propagated down to more specialized morphs like the circleMorph.

    In a class-based language, this need is met by a rule ensuring that subclasses include any instance variables defined in their superclasses. But in Self, a parent link inherits only state and behavior, not information about which slots are present. This omission keeps the Virtual Machine simple and increases flexibility, since a child can override an instance variable with a method. Originally, we expected to share format information with data-parents [Ungar], but never implemented dynamic inheritance efficiently enough.
    Instead, slots from one prototype are automatically copied down to others by annotating the other objects with the source of the copy (copy-down parent) and a list of slots to omit from copying.

          in SIGPLAN Notices 30(10) ACM Press, October 1995 (Proceedings of the 10th Annual Conference on Object-Oriented Programming: Systems, Languages and Applications (OOPSLA'95)) view details
  • Moore, Ivan "Restructuring of Self programs" view details
          in Cointe, P. editor, Proceedings of the Ninth European Conference on Object-Oriented Programming (ECOOP), July 1996. LNCS 1098, Springer-Verlag view details
  • Noble, James - report on Ivan Moore's presentation of Guru view details Abstract: Ivan Moore from Manchester University described the automatic restructuring of Self programs. He argued that although prototype based languages were good for exploratory programming, the resulting programs were often poorly structured, especially with respect to inheritance. He has build a tool (called Guru) which restructures Self programs to improve their inheritance hierarchies.
    Guru examines objects in a Self program and determines which slots are common to several objects. Guru then restructures the inheritance hierarchy to maximise sharing. Common slot definitions are moved into new objects, and the objects which need these slots changed to access them by inheritance. Guru also modifies expressions to maximise sharing, by moving common subexpressions into new expressions. These modifications are done in place -- existing objects are modified to reflect the new structure, but since the modifications preserve the program's semantics, they should not change the program's behaviour.

    This presentation sparked an interesting discussion. It highlighted the differences between the "European School" of object oriented design which emphasises conceptual modelling, and the "American School" which emphasises productivity and reuse. The Europeans argued that sets of objects with common properties were not abstractions, and so the restructured hierarchies would be non-intuitive and difficult to understand. The "Americans" argued that they could finally ignore the design of inheritance hierarchies altogether, use inheritance to maximise code reuse, and then restructure the program to simplify the hierarchy if necessary.
          in Cointe, P. editor, Proceedings of the Ninth European Conference on Object-Oriented Programming (ECOOP), July 1996. LNCS 1098, Springer-Verlag view details
  • Noble, James - report on James Nolbe's presentation on Self encapsulators view details Abstract: James Noble from Macquarie University in Sydney discussed monitoring Self programs via encapsulators. An encapsulator is a object which is wrapped around another object and which monitors the all messages the wrapped object receives. He described four alternative designs for encapsulators in Self, which differ in the way they detect messages and the way they send messages on to the wrapped object. Forwarding encapsulators and delegating encapsulators detect messages via the does-not-understand exception, and send messages to the wrapped object by standard message sends and delegated sends respectively. Inheriting encapsulators and custom encapsulators detect messages with explicit interceptor messages, and use inheritance to send messages to the wrapped object. Overall, he found Self a good language in which to experiment due to its concreteness, simplicity, and flexibility.

    Tom Mens asked about the relationship between encapsulators and composition filters. James commented that composition filters were a very general mechanism, while encapsulators were an implementation of a restricted mechanism in a prototype based system. Ivan Moore asked whether encapsulators were really necessary, as he had achieved the same results by directly modifying objects, rather than wrapping them. James replied that he had found wrapping objects much quicker than modifying them in the (old) versions of Self he had used.
          in Cointe, P. editor, Proceedings of the Ninth European Conference on Object-Oriented Programming (ECOOP), July 1996. LNCS 1098, Springer-Verlag view details
  • Noble, James - report on Mario Wolczko's presentation of an implementation of Smalltalk in Self 4.0 view details Abstract: Mario Wolczko from Sun Microsystems Laboratories described an implementation of Smalltalk in Self 4.0. This project was intended to demonstrate that prototypes can emulate classes and to provide an example of Self programming, rather than to produce a production Smalltalk system. Smalltalk-in-Self's performance at least equals the performance of ParcPlace Smalltalk, and more than doubles ParcPlace's performance for some benchmarks.
    Smalltalk is emulated in Self without changes to the Self virtual machine. Smalltalk programs are parsed, and then one Self object is constructed to mimic each Smalltalk instance. The essential Smalltalk classes (in particular, the metaclass hierarchy) and Smalltalk primitives were constructed by hand in Self, GNU Smalltalk was used to provide a core class library, and a user interface build in Self using Self components. The current version of Smalltalk-in-Self does not support non-local blocks, its ``become'' primitive is more expensive that most other Smalltalks', and its primitive objects (such as integers and floating point numbers) have both Smalltalk and Self behaviour.

    This presentation raised some questions about the relationship between prototypes and classes. Tom Mens asked whether Self could be implemented in Smalltalk. Mario answered that this would be difficult and time consuming, whereas the Smalltalk-in-Self implementation was built in three weeks. Peter Grogono then asked whether this showed that prototypes were really lower level constructs than classes. Mario answered that this was true to some extent, as prototypes are simpler and more flexible than classes.
          in Cointe, P. editor, Proceedings of the Ninth European Conference on Object-Oriented Programming (ECOOP), July 1996. LNCS 1098, Springer-Verlag view details
  • Noble, James - report on Randall Smith's presentation of Us view details Abstract: Randy Smith from Sun Microsystems Laboratories discussed subjective programming. He argued that current object oriented systems are too object oriented. These systems adopt a mechanistic model of the world, in which the same message to the same object always receives the same answer. Subjective programming extends the objects and messages of traditional object oriented programming by incorporating perspectives, also known as subjects. In a subjective language, an object can be seen from multiple perspectives -- that is, an object can respond differently to messages sent from different perspectives. Randy described how perspectives can be used to implement multiple views of objects in user interfaces, to provide reflection and encapsulation, to assist debugging, and to manage multiple versions of objects, especially when produced by different teams.
    Randy then introduced a subjective language called Us. Us extends Self so that an object can have multiple selves. Us introduces layer objects (or layers) which parallel Self objects. Layers can contain slots and can be linked into an inheritance hierarchy. In Us, a perspective is implemented by a layer plus the layers from which it inherits. Each slot belongs to both a normal object and a layer object. Messages are sent to a receiver object and a receiver layer, and the message lookup algorithm finds slots which are in both the object and the layer to which the message is sent.

    Randy outlined several shortcomings of the current design of Us. Layers and objects are almost symmetrical, however the lookup algorithm breaks this symmetry as it searches the layer inheritance hierarchy before the object inheritance hierarchy. The extra complexity of the layers model may be confusing for programmers. A simple implementation of Us has been implemented in Self, however its performance leaves much to be desired.

          in Cointe, P. editor, Proceedings of the Ninth European Conference on Object-Oriented Programming (ECOOP), July 1996. LNCS 1098, Springer-Verlag view details
  • Noble, James "Encapsulators in Self" view details
          in Cointe, P. editor, Proceedings of the Ninth European Conference on Object-Oriented Programming (ECOOP), July 1996. LNCS 1098, Springer-Verlag view details
  • Smith, Randall "The Subjective Prototype" view details Extract: The subjective prototype
    If A and B send the same message to the same object, should they get the same response?  Systems in which the response is the same are called "objective" systems, and most object oriented languages are objective.

    But objectivity is not necessarily good.

    Consider the everyday physical world. Messages like "what is your speed?" and "move to the right" can yield different results when sent from different senders. Or, consider the linguistic world. When a baseball player is standing on third base, the instruction "go home!" would cause him to start running if issued by the coach, but it might be ignored as an insult if shouted by a spectator.

    Same receiver, same message, yet different results.

    This kind of subjectivity is an inescapable aspect of the relativistic worlds in which we live, yet our O-O programming languages cannot directly model such phenomena. They are built upon 19th century models.

    To be more concrete and relevant: Subjectivity is manifest in many familiar computer programming problems today, and here are three examples:
                
    1. Merging of programming changes.

      When multiple programmers work on the same system, they must occasionally stop to combine changes.  This is often painful because of interference between the changes being merged. Merging mechanism (like source code control schemes) are outside the language. The same object must have the same state and behavior to both programmers. The language does not have a natural way to express the notion of "point of view," so change comparison and merging is relatively awkward.
    2. Multiple views.
      It is often advantageous to have one data set viewed from two or more perspectives. One view of an array of numbers might be a pie chart, while another view might be a bar chart. In fact, a central motivation for the Model-View-Controller paradigm was its ability to support multiple views. But this solution requires a distinction between the view and the thing being viewed. It is difficult to have the various objects in the system contain "display" methods appropriate to each object yet associated cleanly within each perspective.  The method cannot be both in an object and in a point of view. Again, objective languages fail to cleanly express subjectivity.
    3. Encapsulation.

      An object has privileged access to its internal state, which is of course a Good Thing. But the mechanisms used to make a distinction between the access from inside and access from outside is always an extra add-on to the language -- access to state via instance variable reads are made distinct from access to state through message sending. Another example: some methods might have extra "private" marks to prevent them from being sent from outside. But these marks are a completely separate mechanism that clutter the language semantics. In a subjective language, an object could adopt a private point of view on itself.  No distinction between variable-accessed and message-accessed state.  No special marks on methods to distinguish between private and public.
         
         
    So in order to model subjective situations, objective programming languages have to stand on their heads. Let's instead create a subjective programming paradigm from the ground up.  As a fundamental model, I find the prototype approach to OO programming appealing because it offers a simple, flexible, and concrete model.  What would the prototype-based approach to subjective programming look like?

    I, with Dave Ungar, have proposed a language called "Us," in honor of the multiplicity of perspectives supported by a subjective language.

    The object model is similar to that of Self: everything is an object, an object consists of a collection of named slots, and every slot references some other object. All computation happens by message passing.

    In Us, we make the following extra provision: every slot is in an object, AND in a "layer" object.  To send a message, you must specify the receiver, AND the layer. So it is possible to send the same message to the same object using different layers to attain different results.

    We say you send a message TO an object THROUGH a layer.

    There is inheritance in Self: if a message does not match a slot name, the method lookup continues through a chain of parents until a match is found.

    So in Us, for symmetry sake, we also imagine a chain of parents, but a chain of layers as well. If a slot is not found in a layer, method lookup will continue on to the "layer parent" until a match there is found.

    Thus instead of a one-dimensional lookup as with Self, Us has a two-dimensional lookup. Like searching a table of rows and columns.

    Now do we search the rows first, or the columns? (Receiver inheritance prioritized over layer inheritance, or the other way round?) We have concluded that it is most sensible to search layers first. But we feel somewhat guilty that one has to be chosen over another.

    What does a layer look like?  Well, layers are first class objects in our Us language, so you can send them messages. Of course you have to send the layer a message through possibly some other layer. You might send a layer a message like "printYourSlotNames." We find it necessary to distinguish between the slots in a layer as an object and the slots in a layer as a message receiver. This distinction also disturbs us a bit.

    Prototypes are useful basis for a subjective object language, because they offer a more uniform and simple object model. The complexity added by subjectivity is therefore limited.

    Will subjectivity catch on, eventually replacing objective programming? Subjectivity is philosophically more sound than traditional objective programming, but there are simplifications we humans make that are worthwhile, even though we may at times pay a price. Perhaps objectivity is one such simplification. I remain undecided on the question. Perhaps a discussion at the workshop would prove enlightening.

          in Cointe, P. editor, Proceedings of the Ninth European Conference on Object-Oriented Programming (ECOOP), July 1996. LNCS 1098, Springer-Verlag view details
  • Wolczko, Mario "Self includes: Smalltalk" view details
          in Cointe, P. editor, Proceedings of the Ninth European Conference on Object-Oriented Programming (ECOOP), July 1996. LNCS 1098, Springer-Verlag view details
    Resources