Vision Program Execution
Overview
Vision works by sending messages to objects. Every Vision expression is constructed in terms of building blocks of the form:
object message
For example, the expression:
3.14 asInteger
sends the asInteger message to a familiar looking number.
The result of sending a message to an object is itself an object. As an object, it can be the target of a message. For example, the expression:
3.14 asInteger sqrt
sends the sqrt message to the object returned by the asInteger message to that same familiar looking number. The Vision language can represent numbers and strings directly, but where do other objects come from? Since every name in a Vision expression is the name of a message, what object receives the gm message in the following expression?
gm sales
To answer these questions, Vision provides a special collection of variables called the Magic Words. These variables return objects that give every expression a Context in which to execute. There are ten magic words in Vision -- ^current, ^my, ^self, ^super, ^here, ^global, ^tmp, ^today, ^date, and ^local -- and three kinds of context -- Block Context, Global Context, and Dynamic Context.
Block Context is the largest and most complex of the contexts. Half of the magic words -- ^current, ^my, ^self, ^super, ^here -- return objects related to the block context of an expression. Every expression executes in an Environment, a default place to look for the implementation of a message as well as a place to record the values of local variables and parameters. The ^current magic word magic word returns the object filling that role. A block can be sent to an object and asked to execute in the context of that object. When that happens, the block needs a way to access messages defined in the environment from which it came. The ^my magic word provides that capability. Whenever a block or message is sent to an object, Vision remembers the object to which the block or message was sent. ^self magic word returns that object. An expression sometimes needs to override the default search rules Vision uses to locate messages. The ^super and ^here magic words make that possible.
Global Context accesses the fixed, global state of your Vision session. Three magic words supply the global context for an expression -- ^global, ^tmp, and ^today. Of these, ^global and ^tmp are the most significant since ^today simply returns the start date of the current session. When a Vision session starts, an initial top level object is derived from your session's startup options. That object provides a view of the data base and a workspace for the session. It is returned by the ^global magic word. Any object created during a session and made accessible either directly or indirectly from the ^global object may be saved if the session updates the data base. That is not desirable for objects that represent intermediate results or inherently transient objects like connections to files, processes, or services. To provide a purely temporary place to record those objects, Vision provides an object that is not automatically saved as part of an update. That object is derived from the ^global object and accessed via the ^tmp magic word.
Dynamic Context defines your perspective on the data base. Two magic words provide the dynamic context for an expression -- ^date and ^local. The ^date magic word provides an expression with a point in time from which it should view the data base. It is changed by operations like date evaluate: []. The ^date magic word is a special case of a more general concept. Any part of an application can be written to behave in a perspective dependent way. One of many examples is the target currency used to manipulate and display monetary values. To implement a dynamic perspective, an application needs to provide any expressions that depend on that perspective with access to the values that define it. The ^local magic word returns the object that records those values.
The rest of this document describes these topics in detail.
Objects Layers
The objects accessed and constructed as part of the context for a Vision expression take special advantage of the Vision object model. This section reviews that model.
Vision objects are constructed in layers. The layers of an object correspond to its Class structure. Each layer provides an environment that adds the properties and methods defined at the corresponding level of the class hierarchy. The outermost layer of an object represents the most specialized class in the hierarchy. When a message is sent to an object, the layers of the object are searched for a definition of that message, starting at the outermost. This is known as Resolving the Message.
Classes provide a template for constructing similar objects. Layers provide an implementation for those objects. Since object layers implement object classes in the natural way, the distinction between classes and layers can be ignored in most cases. Object layers provide a more flexible form of inheritance, however, and that flexibility is exploited by blocks and methods to implement their behavior.
The easiest way to understand object layers and their relationship to object classes is to think of classes as tables and layers as rows in those tables. A class provides definitions for the columns of the table while a layer holds the property values associated with a particular instance of that class. Just as every class except Object inherits the definitions of properties and methods from its superclass, every layer except those at Object inherits property values from a super-layer associated with the object's superclass. That behavior is provided automatically by Vision. While there is no ambiguity concerning the superclass from which a particular layer inherits, there is ambiguity regarding the super-layer. That ambiguity is resolved when the layer is created. For example, when an object is created with the new or createInstance message, Vision automatically creates a new layer for each class in the object's superclass chain. It links those newly created layers together so that each layer can uniquely find its superior. For example:
Object createSubclass: "MySubclass" ; !instance1 <- MySubclass createInstance;creates instance1 with two layers -- one associated with MySubclass and the other with Object. Each of these layers stores the values for properties defined at its associated class. For example, if MySubclass defines two fixed properties, x and y,
MySubclass defineFixedProperty: 'x'; MySubclass defineFixedProperty: 'y';
and Object defines the fixed property, code:
Object defineFixedProperty: 'code';
the layer of instance1 associated with MySubclass stores instance1's values for x and y:
instance1 :x <- 23; instance1 :y <- 92;
while the layer associated with Object stores the value for code:
instance1 :code <- "instance1";
Instantiation is not the only mechanism by which object layers are created. The other is object specialization. Object specialization adds a layer to an existing object. Internally, object specialization is the mechanism Vision uses to store and access the local variables and parameters of a block. Externally, it is the basis for subclass creation and a number of important Vision messages. One of those messages is extendBy:. The extendBy: message is defined for all objects. It takes a single argument which is expected to be a block. It returns its recipient with an added layer. That added layer contains the values of the local variables computed during the execution of the block:
!instance1a <- instance1 extendBy: [ !greeting <- "Hi, I'm instance 1a" ];
Unlike class specialization which inherits only the definitions of messages from a superclass, adding a layer to an object produces an object that inherits both the message definitions and property values of the original object. For example, the value of:
instance1a x
is 23 -- the same as instance1's value of x. If an operation on instance1 changes the value of x, instance1a sees the new value; if an operation on instance1a changes the value of x, instance1 sees the change.
Multiple layers can inherit from the same layer. For example, the expression:
!instance1b <- instance1 extendBy: [ !z <- 16.5 ];
creates another specialization of instance1 which, along with instance1a, shares all of instance1's messages and values.
Objects created using object specialization operations like extendBy: are full fledged Vision objects in all respects. For example, instance1a is a member of a unique subclass of MySubclass which can be instantiated by sending the createInstance message to instance1a:
!instance2a <- instance1a createInstance; instance2a :greeting <- "Hi, I'm instance 2a" ; instance2a :x <- -17.6; instance2a :y <- 2.718;
The layers of any object can be peeled away. Every object responds to the super message by removing its outermost layer and returning the object that results. The super message does not modify its recipient -- it simply returns its next deepest layer. For example,
instance1a super
returns the same object as instance1 -- the object which it specialized.
The ability to dynamically add and remove layers of an object is key to the operation of Vision programs.
Block Context
Block context is the largest and most complex of the contexts. Every expression needs a default place to look for the implementation of a message as well as a place to record the values of local variables and parameters. A block can be sent to an object and asked to execute in the context of that object. When that happens, the block needs a way to access messages defined in the environment from which it came. Whenever a block or message is sent to an object, Vision needs to remember the object to which the block or message was sent. Finally, an expression sometimes needs a way to override the default search rules Vision uses to locate messages. All of these issues are dealt with using the rules of block context described in this section. Topics described in this section include:
Using Blocks To Operate On Objects
Using Blocks To Define Methods
The ^current Object
One of the first questions this document asked was how Vision knows where to find gm in the expression:
gm sales
Vision interprets all names as messages to some object. By that rule, the symbol gm is the name of a message. As a message, it must be sent to an object to produce a result. What object is that? The answer is the object returned by the ^current magic word. Any Vision expression can begin with the name of a unary message. If it does, that message is understood to be a message to ^current. As a result, gm sales is a shorthand for and equivalent to:
^current gm sales
and gm earnings / gm shares is a shorthand for:
^current gm earnings / ^current gm shares
Every Vision expression needs a default place to look for the implementation of a message as well as a place to record the values of local variables and parameters. ^current returns that object. For expressions that you type interactively, ^current returns the top level object accessed when you started your Vision session. If you type the expression:
showMessages
in your interactive session, you will get a list of the messages defined at your top level workspace.
^current is only understood as the default recipient for the initial unary message in a message expression. If you need to send a binary or keyword message to ^current, you must explicitly include ^current as part of your expression. For example, if you want to send the print: message to ^current, you must type:
^current print: aWidth
If you enter:
print: aWidth
you will get a syntax error from Vision.
Not every expression you give Vision to process is executed immediately using the messages available from your top level workspace. In fact, most expressions are part of blocks and methods that are executed only when appropriate. Blocks and methods also have access to a ^current object. Their ^current object is based on their execution environment.
Basically, the purpose of the ^current object is to provide a place to store and retrieve the values of the local variables of a block. Because methods are blocks too, the same statement applies to them as well. Every block carries with it a list of the local definitions it requires. When you enter an expression like !x <- 10 in a block, you are stating that the block requires a fixed property whose name is x. The local definitions associated with the block are used to construct the ^current object that the block will use when it executes.
Because ^current is an object, it needs to inherit from somewhere. It makes sense to have it inherit from someplace convenient. Consider the following Vision code fragment:
!x <- 10; !y <- 5; [ !x <- 20; x print; y print; ] value; x print;
When executed, this code displays the following result:
20 5 10
What allows Vision to produce this result? Inside the block, we have defined a local variable named x and executed two expressions. Those expressions ask Vision to print the objects obtained by sending the messages x and y to the block's ^current object. Because the block provides its own definition and value for x, it is no surprise that the first value displayed is 20, the value locally assigned to x inside the block. The second value displayed -- 5 -- is the value of y defined immediately outside the block. That is convenient, useful, and an example of what is known in programming language jargon as lexical scope. Sending the value message to the block asked the block to construct a ^current object for itself that inherits from the ^current object available where the block is defined. Note that the local value of 20 for x only applies inside the block. The value of x outside the block remains unchanged, as demonstrated by the third value displayed.
The ^current object constructed for the benefit of the block is constructed by adding a layer to an existing object using object specialization. In this case, that object is the ^current object available at the spot the block is defined. Within the block, that layer can be removed using the super message. For example:
!x <- 10; [ !x <- 20; x print; super x print; ] value;
displays the result:
20 10
The ^current object can also be returned from the block or passed as an argument, but that is a topic for another section.
As noted, variables created within a block can be accessed from within the block and from any new blocks defined within that block; however, these variables are not available outside the block. For example, when the code fragment:
myList count > 100 ifTrue: [ !output <- "This is a big list" ] ifFalse: [ !output <- "This is a small list" ]; output
is executed, the final output message will not be found. Although both blocks passed to the ifTrue:ifFalse: message define local variables called output, neither of these definitions is available at the spot where the final output message is sent. The previous program could be rewritten as:
myList count > 100 ifTrue: [ !output <- "This is a big list"; output ] ifFalse: [ !output <- "This is a small list"; output ]
or:
!output; myList count > 100 ifTrue: [ :output <- "This is a big list" ] ifFalse: [ :output <- "This is a small list" ]; output
or:
!output <- myList count > 100 ifTrue: [ "This is a big list" ] ifFalse: [ "This is a small list" ]; output
or:
!output <- "This is a small list"; myList count > 100 ifTrue: [ :output <- "This is a big list" ]; output
All of these program fragments produce the same result. The last three produce their result by updating the value of the output property defined at the same level as the blocks in these examples.
The examples presented thus far are examples in which the ^current object constructed for a block inherits from the environment in which the block was defined. Blocks passed to messages like value, whileTrue:, ifTrue:ifFalse: and its variants all behave this way. These messages implement conditional control structures similar to those found in traditional programming languages. The expressions in the blocks used with these messages should see the same collection of names as seen by their non-conditional brethren. Constructing a ^current object that inherits from the environment of the block's definition produces that behavior.
Using Blocks To Operate On Objects
It is often useful to send a number of messages to the same object. For example, you could write code like:
gm sales print; gm assets print; gm earnings print;
to send a number of different messages to the same object. To allow you to 'factor out' the repeated recipient, Vision provides the do: message. Using the do: message, you can write the same code as:
gm do: [ sales print; assets print; earnings print; ];
The block supplied to the do: message will need a ^current object when it is run; however, in this example that ^current object does not inherit from the environment of the block's definition -- it inherits from the object gm supplied to the do: message. The do: message is one of a number of important Vision messages that behave this way. When Vision constructs a block's ^current object, it gets to decide which object ^current should inherit from based on what it is going to do with the block. In this case, that object is a dynamically supplied one.
Messages such as do: which dynamically change the ^current object are performing a Context Switch. Other messages that dynamically switch the context include send:, extendBy: and most of the Collection messages that require a block as a parameter.
A block whose ^current object does not inherit from the environment in which the block was defined still has access to that environment. Consider the following code fragment:
!industrySales <- gm industry sales; gm do: [ name print; sales print; (sales / ^my industrySales) printNL
];
Whenever Vision creates a block's ^current object from a dynamically specified object, it makes the object that ^current would have inherited from available as the value of the ^my magic word. Using the ^my magic word, it is possible to retrieve values from the environment in which the block appears. In this example, ^my returns the object that understands the gm message and defines the industrySales local variable.
Whenever Vision creates a block's ^current object from a dynamically specified object, it also makes that dynamic object available as the value of the ^self magic word. Using the ^self magic word, you can access the dynamic object without any of the local redefinitions made by the block. This is often helpful. If gm already responds to the eps message, that definition will be hidden by the local one in the following example:
gm do: [ !eps <- earnings > 0 ifTrue: [ earnings / shares ] ifFalse: [ 0 ]; "Adjusted eps = " print; eps printNL; ];
One way you can access the original definition is by sending the eps message to ^self. The following code displays the values returned by both the original and locally redefined eps messages:
gm do: [ !eps <- earnings > 0 ifTrue: [ earnings / shares ] ifFalse: [ 0 ]; "Original eps = " print; ^self eps printNL; "Adjusted eps = " print; eps printNL; ];
At this point, you should be telling yourself that there is another way to achieve the same result. Because ^current is constructed as a specialization of the dynamic object returned by ^self, you could also write:
gm do: [ !eps <- earnings > 0 ifTrue: [ earnings / shares ] ifFalse: [ 0 ]; "Original eps = " print; super eps printNL; "Adjusted eps = " print; eps printNL; ];
Because super removes the outer layer of an object, ^current super returns exactly the same object as ^self. If you use the super message, however, you need to remove the correct number of layers. In this case, you needed to remove one layer -- the layer added to form ^current for the top level block. When your expression appears in a more deeply nested block, you need to remove more layers. The following code would not produce the result you expect:
gm do: [ !eps <- earnings > 0 ifTrue: [ earnings / shares ] ifFalse: [ 0 ]; eps > 0 ifTrue: [ "Original eps = " print; super eps printNL; "Adjusted eps = " print; eps printNL; ]; ];
As before, the super message removes one layer from ^current; however, at the point where this super message is sent, ^current refers to the current environment of the ifTrue: block. Inside the ifTrue: block, you need to remove two layers to get back to the dynamic object -- the layer added by the ifTrue: block and the layer added by the do: block. To get the right answer, you need to use:
gm do: [ !eps <- earnings > 0 ifTrue: [ earnings / shares ] ifFalse: [ 0 ]; eps > 0 ifTrue: [ "Original eps = " print; super super eps printNL; "Adjusted eps = " print; eps printNL; ]; ];
or you need to use ^self:
gm do: [ !eps <- earnings > 0 ifTrue: [ earnings / shares ] ifFalse: [ 0 ]; eps > 0 ifTrue: [ "Original eps = " print; ^self eps printNL; "Adjusted eps = " print; eps printNL; ]; ];
When to use ^current and when to use ^self is a recurring question in Vision programming. For operations like do:, where ^current always inherits from ^self, you can use either and usually get the right answer. In some cases, however, you need to be sensitive to the magic word you choose. A good rule of thumb is to use ^self when you are sending a message to the dynamic object and to allow Vision to use ^current, which it does by default for unary messages, when you are accessing a local variable or parameter of the block.
As noted, Vision makes extensive use of blocks that inherit from dynamically specified objects. A number of messages are defined at Object that use blocks this way. The do: message illustrated here is one of those messages. It evaluates a block in the context of a dynamically specified object and returns that object. The send: message is another. It returns the value of the final expression in the block. The extendBy: message, discussed in the object layers section of this document, is a third. It returns the ^current object used by the block while it was being evaluated. In fact, the expression:
gm extendBy: [ !eps <- earnings / shares ]
and:
gm send: [ !eps <- earnings / shares ; ^current ]
produce exactly the same result -- an object that inherits all of the property values and messages of gm along with an additional property named eps.
Vision's collection iteration and query operations also make use of dynamically specified block inheritance. The do:, send:, and extendBy: messages are all redefined by the collection classes to apply their block argument to each element of the collection. For example, the code fragment:
4 sequence do: [ print; sqrt print; newLine print; ];
displays the square roots of the first four integers. All of the query, sorting, grouping, and aggregation operations accept one or more blocks that are also applied to the collection's elements using the dynamic inheritance rules described here. For example,
Company masterList select: [ sales > 10000 ]. sortDown: [ sales ]. do: [ name print: 40; sales printNL; ];
evaluates all of the initial unary messages in its blocks in the context of the elements of the lists it is processing.
Using dynamic inheritance to construct the ^current object for a block is a convenient shorthand. The messages that use dynamic inheritance all operate on one or more objects. The blocks they accept as their arguments specify operations to be performed or values needed for that object or collection of objects. By arranging the block's affairs so that it inherits from the object with which it is working, the block gains direct access to the information it needs. That principle also applies to the definition of methods.
Using Blocks To Define Methods
When you define a method, you are defining code that runs in response to a message. Because that code is a block, it will have a ^current object constructed for it when it runs. To construct that object, Vision needs to choose an object from which it will inherit. The following examples illustrate how Vision makes that choice.
To start with, assume that Subclass1 and Subclass2 are defined by the following Vision code:
Object createSubclass: "Subclass1" ; Subclass1 createSubclass: "Subclass2" ; Subclass1 define: 'message1' toBe: "You got the definition of message1 at Subclass1" ; Subclass1 define: 'message2' toBe: "You got the definition of message2 at Subclass1" ; Subclass2 define: 'message1' toBe: "You got the definition of message1 at Subclass2" ;and that Subclass2 defines a method that displays the values of message1 and message2 as follows:
Subclass2 defineMethod: [ | displayStuff | message1 printNL; message2 printNL; ];
Evaluating the expression:
Subclass2 displayStuff
produces the result:
You got the definition of message1 at Subclass2 You got the definition of message2 at Subclass1
This is probably what you expected. Since message1 is defined at Subclass2, sending message1 to an instance of Subclass2 finds that definition. Since message2 is defined at Subclass1 but not at Subclass2, sending message2 to an instance of Subclass2 finds the inherited definition at Subclass1.
Suppose Subclass1 defines a similar method:
Subclass1 defineMethod: [ | displayStuff1 | message1 printNL; message2 printNL; ];
Because Subclass2 inherits from Subclass1, you can evaluate the expression:
Subclass2 displayStuff1
When you do, you get the result:
You got the definition of message1 at Subclass1 You got the definition of message2 at Subclass1
This may not be what you expected. Even though the displayStuff1 message was sent to an instance of Subclass2, Vision found Subclass1's definition for message1. It did not find Subclass2's redefinition. Apparently, when displayStuff1 is run in this case, it is sending message1 to an instance of Subclass1, not of Subclass2. That is in fact the case. When Vision runs a method, it constructs the ^current object for that method so that it inherits from the object layer associated with the subclass that defines the method, not the object layer to which the message was sent. In this example, that is the layer of Subclass2 associated with Subclass1.
When Vision executes a method in response to a message, ^self is set to the object to which the message was sent. ^self in Vision is similar to self in Smalltalk and this in C++. Using ^self instead of ^current, displayStuff1 can be rewritten as:
Subclass1 defineMethod: [ | displayStuff1 | ^self message1 printNL; ^self message2 printNL; ];
Using this definition of displayStuff1, when you evaluate the expression:
Subclass2 displayStuff1
you get the result:
You got the definition of message1 at Subclass2 You got the definition of message2 at Subclass1
which is probably what you expected to see.
As these examples illustrate, when you write a method, you need to consider when to use ^self and when to use ^current. A good rule of thumb is to use ^self when you are sending a message to the object used to invoke your method and ^current, which Vision uses by default for unary messages, when you are accessing a local variable or parameter of the method or one of its nested blocks. When you use ^self, you are giving Vision access to all of the definitions and redefinitions available from the object used to invoke your method. In contrast, when you use ^current, either explicitly or implicitly, you are telling Vision to ignore the class of the recipient and just use the definitions available locally in the method, the class at which the method is defined, and any superclasses of that class. Except when you are accessing the value of a local variable or parameter, that is probably not what you intended. If it is, the ^here magic word, described below, is a better choice.
When Vision executes a method in response to a message, it also sets the ^my magic word. In these examples, ^my refers to the object that understands the Subclass1 and Subclass2 messages. This is exactly the same behavior as described for do: and its kin. When Vision changes the default search path for a block to something other than the block's definition environment, it arranges for ^my to return the definition environment of the block. You will probably never need to use ^my to access the environment that actually defined the method, but you can and it behaves consistently.
Using ^super to Access Superclass Definitions
In an object-oriented system, subclasses are used to implement more specialized behavior than their superclasses. One way that is done is by overriding a superclass' definition of a message in a subclass. Sometimes the override provides a completely new definition of the message that has nothing to do with the superclass' implementation. More often, its intent is to retain the behavior of the original message while adding the special processing required by the subclass.
The initialization of a new instance is a common application for this kind of specialization. For example, when Vision creates an object using the new or createInstance message, it sends the initializeGlobalInstanceProperties message to the newly created object:
Object defineMethod: [ | new | ^self clusterNew initializeGlobalInstanceProperties ];
The intent of the intializeGlobalInstanceProperties is to ask the object to assign meaningful initial values to its properties. Object provides an implementation of initializeGlobalInstanceProperties that takes care of the properties defined at Object:
Object defineMethod: [ | initializeGlobalInstanceProperties | ^self :baseObject <- ^self; ^self :creationDate <- ^today; ^self ];
Every class that needs the services of initializeGlobalInstanceProperties is expected to override its implementation to take care of its own needs. In taking care of itself, however, it cannot ignore its superclasses. For example, the implementation of initializeGlobalInstanceProperties at Object must still be called if methods that rely on the validity of baseObject and creationDate are to continue to work. To satisfy this need, a method needs a way to call an implementation that it overrides.
The ^super magic word provides the needed mechanism. The following code fragment shows the definition of initializeGlobalInstanceProperties at a subclass of Object:
Object createSubclass: "Entity"; Entity defineMethod: [|initializeGlobalInstanceProperties| ^super initializeGlobalInstanceProperties; ^self :aliases <- ^self defaultInstance aliases clusterNew; ^self ];
The first line in this method uses ^super to invoke the implementation of initializeGlobalInstanceProperties inherited from Object; the rest of the method takes care of Entity's special needs. This technique is recursive. If a subclass of Entity needs to override this message, it can use the same technique to ensure that Entity's version of this message is run. When the version of initializeGlobalInstanceProperties at Entity is called, it will take care of calling the version at Object.
The ^super magic word is a cross between ^self and ^current. It is a different from the other magic words you have seen because it behaves differently depending on whether you ask for its value or send it a message. If you ask for its value or return it as the result of an expression, ^super is indistinguishable from ^self. For example, the expression:
^self == ^super
always returns TRUE. When you send a message to ^super, however, Vision starts searching for the message at ^current. By starting at ^current, the search is restricted to the class and superclasses of the class that defined the current method. In the case of ^super, Vision looks for the second definition of the message. When used to access a message definition hidden by a subclass, the first definition found on the path from ^current is presumably the definition of the method that is currently running. Since calling it again would simply put Vision into an infinite loop, that is not the version Vision wants to find. For example, when Vision executes the ^super initializeGlobalInstanceProperties expression inside Entity's definition of initializeGlobalInstanceProperties, the first implementation of initializeGlobalInstanceProperties is the one at Entity. Skipping the first definition allows Vision to proceed up the superclass chain to the hidden definition that ^super needs to find -- in this case the one at Object.
Once the desired message implementation is found, ^super is once again indistinguishable from ^self. Even though Entity's definition of initializeGlobalInstanceProperties began its ^super initializeGlobalInstanceProperties search from ^current and not from ^self, ^self inside the method at Object references the same object as ^self inside the method at Entity. If the whole process illustrated in this example were initiated by the code:
Entity createSubclass: "Company"; !myCompany <- Company createInstance;
the value of ^self seen inside the initializeGlobalInstanceProperties method defined at Object is the instance of class Company created by the createInstance message -- not the ^current object created for the benefit of Entity's implementation of initializeGlobalInstanceProperties.
Although their names are similar, the ^super magic word and the super message do not do the same thing and are generally not interchangeable. ^super is a magic word that controls how Vision uses ^self and ^current to search for a message. Its intent is to allow a method to call a superclass' version of itself. In all other respects, it is synonymous with ^self. In contrast, super is a message that removes the outermost layer from an arbitrary object. While removing a layer from an object can sometimes be used to skip a particular message definition, removing that layer also makes subclass information associated with that layer unavailable to the recipient of the message. The super message is intended for use in cases where that consequence is acceptable or desirable. Those cases typically involve removing extensions added by operations like extendBy: and groupedBy: and implementing operations like showMessagesX that must examine each layer of an object.
Using ^here to Ignore Subclass Definitions
While the ability to override the implementation of a message in a subclass is a powerful capability, sometimes it is too powerful. When you write a method that sends a message to ^self, the message can be reimplemented by any of the classes between the class of ^self and the class at which your method is defined. When you write a method that sends a message to ^current, you restrict your search to the local state of your method, the class at which your method is defined, and the superclasses of that class; however, any methods you call by sending a message to ^current will not have access to the subclass information associated with your method's value of ^self. There needs to be a middle ground. The ^here magic word provides that middle ground.
The ^here magic word is another ^self -- ^current hybrid that evaluates to ^self when asked for its value or passed as a parameter but that searches for messages beginning with ^current. In fact, the only difference between ^here and ^super is that ^here returns the first definition of a message it finds while ^super returns the second.
^here is intended to assist you in modularizing code. It is designed to help in cases where a large method should be broken in several smaller methods or where a collection of related methods all require the services of a common set of helper methods. The use of ^here guarantees that these methods can call each other without danger of subclass override and without losing the subclass information associated with the recipient of the message that invoked the method. For example, the use of ^here in the following method fragments:
MyClass defineMethod: [ | doItThisWay | ^here doThisWayOrThatWaySetup; ... ]. defineMethod: [ | doItThatWay | ^here doThisWayOrThatWaySetup; ... ]. defineMethod: [ | doThisWayOrThatWaySetup | ... ^self goThisWayOrThatWay; ^self whatAmI print; ... ];
insures that doItThisWay and doItThatWay will always find the doThisWayOrThatWaySetup method defined at MyClass regardless of how that method might be redefined by the subclasses of MyClass. The use of ^here also guarantees that the expression ^self goThisWayOrThatWay in doItThisWayOrThatWay will always access to the full recipient of the doItThisWay or doItThatWay message -- something it would not have if ^current were used instead of ^here. For example, executing the following code:
MyClass createSubclass: "MySubClass"; MySubClass doItThisWay
will cause:
^self whatAmI print
in the definition of doThisWayOrThatWaySetup to display:
MySubClass
in addition to whatever else these methods do.
Global Context
Global context accesses the fixed, global state of your Vision session. Three magic words supply the global context for an expression -- ^global, ^tmp, and ^today. Of these, ^global and ^tmp are the most significant since ^today simply returns the start date of the current session. When a Vision session starts, an initial object is derived from the session's startup options. That object provides a view of the data base and a workspace for the session. It is returned by the ^global magic word. Any object created during a session and made accessible either directly or indirectly from the ^global object may be saved if the session updates the data base. That is not desirable for objects that represent intermediate results or inherently transient objects like connections to files, processes, or services. To provide a purely temporary place to record those objects, Vision provides an object that is not automatically saved as part of an update. That object is derived from the ^global object and accessed via the ^tmp magic word.
^globalEvery Vision data base is structured as a collection of object spaces. In addition to organizing a Vision data base on disk, each object space remembers a special Top Level Object whose messages return other objects from the data base. The top level objects associated with the object spaces are starting points for navigation in a Vision data base.
When you start a Vision session, you have the option of specifying which object space you want to use as your starting point. The object space you select determines the top level object you will be using. If you do not specify an object space, Vision constructs a temporary one for you. In that case, Vision derives a top level object for you by adding a layer to the top level object associated with object space 3. The following diagram illustrates the inheritance structure for the top level object you get:
top-level space (temporary or explicit object space) | | V object space 3 (shared application core) | | V object space 2 (shared built-in core) | | V Object
Whether you explicitly select an object space or Vision implicitly creates one for you, the top level object associated with that object space is always returned by the ^global magic word. As noted earlier, the same object is also returned by the ^current magic word when you are evaluating interactive Vision expressions.
The top level object responds to messages that return the default instances of built-in classes like Object, Integer, and String as well as the default instances of application classes like Company that you or your organization add to the data base. To add new objects and classes to a data base, you modify your top level object or an object accessible from it. For example, the expression:
Object createSubclass: "MyClass"
defines a MyClass message at your top level object that references the default instance of the new class it creates for you. To make MyClass a permanent part of the data base, you simply need to update the data base. When you do, subject to your privileges and update mode, any new or modified objects accessible from your top level object will be saved.
^tmpWhile Vision takes care to not save objects you do not have the right to save, it will try to save all of the new and updated objects referenced from your top level object. If it did not, the data base would be left in an inconsistent and damaged state.
Sometimes, you do not want everything you create during your session to be saved. For example, you may need to record an intermediate result temporarily. Alternatively, you may need to remember an inherently transient object like a connection to a process, server, or client. Unfortunately, if you directly or indirectly reference any of these objects from ^global, they will be saved. For example, if you interactively execute:
!partialResult <- ... # Do something useful
the object referenced by partialResult will be saved when the data base is updated.
One way to avoid saving an object like partialResult is to reference it from an object that Vision is not going to save. Vision provides just such an object. That object is returned by the ^tmp magic word.
Instead of referencing your temporary result from a name defined at ^global, you can reference it instead from a name defined at ^tmp. You need to do a little more work to define a name at ^tmp since the local variable definition shorthand !variable <- ... is not applicable to ^tmp. Two alternatives are:
^tmp define: 'partialResult' toBe: ( ... ) # Do something useful inside # the parentheses
and:
^tmp defineFixedProperty: 'partialResult'; ^tmp :partialResult <- ... # Do something useful here
As long as the object returned by ^tmp partialResult is not referenced from a permanent object like ^global or an object referenced from ^global, it will not get saved when the data base is updated. Of course, if you later evaluate an interactive expression like:
!xyz <- ^tmp partialResult;
you have committed Vision to saving the object.
Internally, the ^tmp object is constructed by adding a layer to ^global. In particular,
^tmp super == ^global
always returns TRUE. Because ^tmp inherits the messages and property values of ^global, you can use ^tmp virtually any place you would use ^global. For example:
!x <- 92; x print; ^tmp x print; ^tmp :x <- 17; x print;
will display:
92 92 17
when run interactively.
Dynamic Context
Dynamic context defines your perspective on the data base. Two magic words provide the dynamic context for an expression -- ^date and ^local. The ^date magic word provides an expression with a point in time from which it should view the data base. It is changed by operations like date evaluate: []. The ^date magic word is a special case of a more general concept. Any part of an application can be written to behave in a perspective dependent way. One of many examples is the specification of a target currency used to manipulate and display monetary values. To implement a dynamic perspective, an application needs to provide any expressions that depend on that perspective with access to the values that define it. The ^local magic word returns the object that records those values.
Dynamic context and perspectives are used to give your data base dimensionality. When you use a multi-dimensional data base, you need to specify where you are in the multi-dimensional space of that data base. For example, when you ask for a value like sales from an object representing General Motors or Honda, not only do you need to specify the object that should respond, you may also need to specify a date and a currency as well. Because dimensions apply to everything in general and nothing in particular, dynamic context gives you a way to specify your place without cluttering every expression with information about that place, whether it needs it or not. In effect, dynamic context provides a way to pass parameters to an expression without requiring that they be passed explicitly to or through every expression. This is more than just a convenience. New perspectives can be added at any point as your data base evolves. Adding those perspectives without the tools of dynamic context requires re-writing all of the code between where you are as a user and where information about that perspective is needed in the data base. That is not a reasonable thing to do.
^dateTime is a dimension that slices through an entire data base. When you build a data base using Vision, you model some part of the world. Included in that model are descriptions of the various kinds of objects with which you are working. The description of an object includes a description of its properties. Fundamentally, that is where time comes into play. One of the decisions you make when you define a property is whether that property varies over time. For example, if you are defining an object of type Person, some properties like blood type never change; others like income change regularly. For example:
Entity createSubclass: "Person"; Person defineFixedProperty: 'bloodType'. define: 'income'; !thatPersonOverThere <- Person createInstance; thatPersonOverThere do: [ :bloodType <- ^global Named BloodType OPositive; :income asOf: 92 put: 35000; :income asOf: 93 put: 45000; :income asOf: 94 put: 52000; :income asOf: 95 put: 47000; ];
When you ask for the income of thatPersonOverThere, you probably are not asking for the person's life history -- you want to know what that person's income was as of a particular point in time. If you ask for it interactively, Vision supplies the most recent value available as of today (technically as of ^today). For example, assuming that you interactively asked Vision to evaluate:
thatPersonOverThere income
on August 15, 1996, you would get the answer:
47000
When Vision accessed the income time series, it got a date from the ^date magic word. In an interactive session, ^date returns the start date of the session.
You change the date used to access time-series properties with the evaluate: message. For example:
92 evaluate: [ thatPersonOverThere income print ];
displays:
35000
The evaluate: message does not permanently change the value of ^date -- it only changes it while the block it was passed is running. For example, when the expression:
92 evaluate: [ ^date print ]; ^date print;
is run interactively on August 15, 1996, it displays:
12/31/1992 8/15/1996
^date is the only context changed by the evaluate: message. In particular, you have probably noted that the ^current objects constructed for the blocks in these examples understand the thatPersonOverThere message.
The evaluate: message is also implemented for date ranges. For example:
91 to: 96 by: 1 yearEnds. evaluate: [ ^date print: 12; thatPersonOverThere income printNL; ];
displays:
12/31/1991 NA 12/31/1992 35000 12/31/1993 45000 12/31/1994 52000 12/31/1995 47000 12/31/1996 47000
Changing the value of ^date always involves the use of a block which is evaluated in the context of that new date. That is because the evaluate: message does not modify a hidden global variable somewhere. Instead, it pushes a new ^date value onto a stack. When it returns, it pops that stack. This allows you to nest evaluate: messages and get the result you expect. For example:
95 to: 96 by: 1 yearEnds. evaluate: [ ^date year printNL; ^date to: ^date - 3 quarterEnds by: -1 quarterEnds. evaluate: [ " " print; ^date printNL: 12 ; ]; ];
displays:
1995 12/31/1995 09/30/1995 06/30/1995 03/31/1995 1996 12/31/1996 09/30/1996 06/30/1996 03/31/1996
Any class can implement a message that rebinds ^date. For example, all of the messages that enumerate the elements of a TimeSeries also make the effective date of the element available as the value of the ^date magic word when they are processing the element.
^localThe ^date magic word is a special case of a more general concept. The ^local magic word generalizes that concept.
^local works according to the same principles as ^date -- it has a default initial value that can be overridden for the duration of a block. Any part of an application that needs to return a perspective dependent result can send messages to ^local to determine what its perspective should be. The default ^local object should respond to those messages with answers that select the default perspective. To change to a different perspective, a ^local override should redefine those messages to return whatever new values are appropriate.
The framework supplied by Vision in support of ^local is very small. Basically, Vision supplies a default initial value for ^local and a single message that changes the ^local object for the duration of a block. By default, ^local returns the same object as ^tmp. That means that an application can define its default perspective in terms of properties defined persistently at ^global or transiently at ^tmp. To override ^local, all objects respond to the asLocalContextFor: aBlock message. That message arranges for its recipient to serve as the ^local object while it is evaluating its block argument. Like the evaluate: message defined at Date, the asLocalContextFor: message pushes its object onto a stack of ^local objects. When it returns, it pops the stack. As a result, calls to asLocalContextFor: can be nested without unexpected consequences and without causing a permanent change to ^local.
The preceeding discussion of ^local has been very general. That is because ^local is a general concept that can be used to implement multiple perspective dependent views of the data base simultaneously. One such perspective dependent view is the target currency used to manipulate and display monetary values.
To compare and aggregate monetary values, they must be expressed in a common currency. There is, however, no single right choice for that currency. It can be whatever is appropriate for the current user of the data base. Like time, it is cumbersome and inappropriate to require that every operation that returns a monetary value also accept an extra parameter to specify its result currency. This calls for the use of ^local and dynamic perspectives.
To implement dynamic target currencies, ^local must respond to a message returning an object of type Currency. That message is called currency and is defined by the top level object in object space 3. Because the default ^local object inherits from your top level object and all top level objects inherit from object space 3's top level object, that definition is available as the source of a default target currency.
To override the default target currency, the Currency class defines an evaluate: message similar to the evaluate: message defined at Date:
Currency defineMethod: [ | evaluate: aBlock | !currency <- ^self; ^local extendBy: [ !currency <- ^my currency ]. asLocalContextFor: aBlock ];
This method adds a new layer to the current ^local object. That layer overrides the currency property and is used as the new ^local object for whatever block the evaluate: message was asked to process. Although the evaluate: message could have supplied any object as the ^local context for that block, it makes sense to base that object on the current ^local object. That is because currency may not be the only dynamic perspective available. Since there may be others, the evaluate: message at Currency needs to start with the existing ^local object so that it does not lose or damage the other perspectives.
The selection of the ^local properties used to define a perspective along with the definition of an evaluate: message that overrides those properties completes the first part of the process of defining a new perspective. Using the perspective information available from ^local is the responsibility of methods attached to your classes. The following code fragments show how it can be done:
Entity createSubclass: "Company"; Company define: 'rawSales'. defineMethod: [ | sales | rawSales * currencyFactor]. defineMethod: [ | currencyFactor | ^self baseCurrency exchangeTo: ^self currency relativeTo: ^date ]. defineMethod: [ | currency | ^local currency isntNA ifTrue: [^local currency] ifFalse: [^self baseCurrency] ];
Most of the work in these methods is bookkeeping. The part that is relevant to dynamic contexts and ^local is the implementation of the currency method. Basically, this method asks ^local for a currency to use as a target.
The following illustrates how this capability works in a real data base. This expression displays GM's sales in each of four currencies:
Named Currency send: [DEM, GBP, USD, XAF]. do: [ ^self evaluate: [ ^local currency name print: 30; # (same as name in this case) ^global gm sales printNL ] ; ] ;This is what gets displayed:
German Deutschmark 235869.64 British Pound 97265.30 United States Dollar 152172.00 Franc 152172.00