Objects
For those who care to learn just a little bit more, SenseTalk’s object model offers a much richer environment, without adding greatly to its complexity.
The information explained here provides an overview of the modular structure of SenseTalk, and introduces the key concepts and terminology that you will want to be familiar with as you proceed with learning how to write your own object-oriented SenseTalk software.
Setting the Stage
SenseTalk is a "scripting" language. You create SenseTalk software by writing scripts that describe what different elements of your system will do. The usual way to do this in SenseTalk is to create a number of different “objects” which will be the actors in your system. Each object has its own script that tells what it does in the system. By taking this modular approach, each script can be relatively short and self-contained, which makes even complex systems relatively easy to work with.
The objects in your system will interact by sending and receiving messages. An object responds to a message if it has a handler for that message. A script consists of a series of handlers, one for each message that the object will respond to. Any other messages will be ignored by the object.
One SenseTalk object can help another. When you have a number of objects that need to have similar behaviors or abilities, you can create a helper object which embodies those shared behaviors and abilities. Each of the other objects can then be helped by the helper object, eliminating the need for them to each have those behaviors defined in their own scripts. This saves you a lot of time and effort. It also makes it easy to update a behavior by simply changing the script of the helper object. All of the other objects will then “inherit” the changed behavior.
It should be noted, for those who may be familiar with other object oriented languages, that SenseTalk is somewhat different. Most other languages define “classes” of objects and generally only implement behavior at the class level, for all objects in that class. In SenseTalk, each individual object has its own script, so it can have its own unique behavior.
SenseTalk has no classes, but its helpers provide a similarly rich set of capabilities by allowing objects to use (or inherit) functionality provided by any number of other objects. This all-object (classless) approach is both simpler and more versatile than class-based systems.
Objects Defined
SenseTalk is an Object-Oriented Language. SenseTalk scripts describe the behavior of objects. Depending on the host environment, an object may be something visible which can be directly manipulated and interacted with, such as a button or an image on the screen, or it may be more abstract, such as an object representing a bank account or an exercise schedule.
Whether an object represents something visual or something that is purely conceptual, all objects in the SenseTalk world share certain characteristics. Any object can have attributes, called properties, which store information that is important to that object. An object may also have behaviors, which are defined by the object’s script and define what the object can do.
In the case of a picture shown on a computer screen, its properties might include such things as its height and width as well as the colors of all the dots that form the image itself. An object representing a person’s contact information would include properties such as their name, telephone number, and mailing address.
An object’s behaviors as defined by its script might include such things as turning an image upside down, or sending an email message to a person on a mailing list. Other “behaviors” of an object may be more passive, such as providing information about the object which is not directly represented in its properties. For example, an object representing a person might include a property that stores their birth date. The object’s script could provide the person’s age by accessing the current date and calculating the number of years that have passed since their birth date.
Property Lists
Property lists were described in an earlier section as a collection of values identified by their keys. It was mentioned there that a property list is actually a simple object.
A property list is an object that’s mainly properties. Behaviors can be added to a property list, though, either by assigning helpers, or by setting the script property to a valid script. For more information, see Helpers. To set the script of an object, simply store the text of one or more handlers into the “script” property of that object:
put {width:7,length:12} into myRect
set the script of myRect to {{
function area
return my width * my length
end area
}}
put myRect's area-- 84
Script Files
In many SenseTalk scripting environments, each script is stored in a text file on the disk. In these environments, each such file is a SenseTalk object, and the contents of the file is the object’s script.
A script consists of a sequence of SenseTalk commands (also called statements) which define the behaviors of that object. Here is the simplest and shortest complete SenseTalk script you are likely to encounter:
put "Hello, World!"
A script file is an object that’s mainly behaviors. The script above constitutes a very simple object, with a single behavior that will be invoked when the script is run: displaying the words “Hello, World!”.
Property Declarations
In addition to behaviors, a script file may also include property declarations if desired. When an object is created by loading a script file from disk, any property declarations in that script will define initial values for the object’s properties.
A property declaration can be placed outside of any handler. If it is the first thing in your script, the initial handler comes after it. For example, it would appear before a params declaration. Otherwise, it ends the initial handler like a declared handler does. For more information on working with handlers, see Handlers.
Syntax:
Properties
propertyKey1:value1,
{propertyKey2:value2}
end Properties
Syntax definitions for language elements follow these formatting guidelines:
- boldface: Indicates words and characters that must be typed exactly
- italic: Indicates expressions or other variable elements
- {} (curly braces): Indicate optional elements.
- [] (square brackets) separated by | (vertical pipes): Indicate alternative options where one or the other can be used, but not both.
Example syntax:
In this example, "open file" is required and must be typed exactly. "fileName" is a variable element; it is the path to and name of the file being opened. The following expression is optional and indicates why the file is being opened. If this expression is added, "for" is required and must be typed exactly. One of the following must be included, but only one, and they also must be typed exactly: "reading", "writing", "readwrite", "appending", or "updating".
Example:
This property declaration defines five properties of the object, and assigns them initial values. Values that contain spaces and special characters must be enclosed in quotation marks, as shown here for the name and birthDate properties. Simple values do not require quotes, but may be quoted if you prefer. Some properties may be assigned a list of values, as shown for the helpers property.
Properties
name: "Charlie Brown",
birthDate:"May 14, 1942",
hairColor: brown,
numberOfSiblings:1,
helpers: ["Linus", "Lucy"]
end properties
Script Folders
A folder can also serve as a SenseTalk object. When treating a folder as an object, each script file within the folder is treated as a handler of that object. If a script with the special name "_initialHandler_" exists in that folder, it will be loaded and used as the folder object's initial handler, and any properties defined in that script will be used as the initial property values of the folder object.
Using Objects
Creating Property Lists
A simple property list (an object with properties but no handlers) can be created by simply listing its properties and values in the format shown here:
put {x:44, y:108, z:-19} into point2
Creating Simple Objects
An object can be helped by other objects (helpers are described in detail later in this section):
put {name:"Hank", age:47} helped by parent,actor into person
Creating Initialized Objects
Fully-initialized objects can be created with a new object expression:
put new object with {width:14, length:9} into dimensions
When an object is created using new object as shown in the example here, the result is essentially the same as the simple objects shown earlier. The only difference is that an “initialize” message is sent to the newly-created object. Of course, it won’t have a handler to respond to that message, unless it has a helper with such a handler. You can create the object with one or more helpers, like this:
put new object with (partNum:1234) helped by part into aPart
Creating with Prototype Objects
A more common way to use a new object expression is to specify a “prototype object”, as shown in the following example:
set child to be a new Person with {name:"Penny", age:6}
In this example, Person is a prototype object. Prototype objects (if they want) can control exactly how the new object is constructed (the details of how this works are given later in this section). In the usual case, however, the new object will have the properties given in the script plus copies of any other properties from the prototype, and will be helped by the prototype object. So, unless the Person object overrides the usual behavior, the statement above would be equivalent to this:
set child to a new object with ((name:"Penny", age:6) adding properties of (object Person)) helped by Person
Accessing Object Properties
An object’s properties can be accessed in several different ways, using a dot (.) or apostrophe-S (‘s) and the property name after the object, or the property name followed by “of” before the object:
put {make:Yamaha, model:"U3", finish:"Walnut"} into piano
put "My piano is a " & piano.make && piano's model
put "It has a pretty " & finish of piano & "finish"
Object properties are containers. Properties can be added, deleted, or their values changed, as described in detail for property lists in Lists and Property Lists.
Using “Me” and “My” to Access an Object’s Own Properties
It is very common for an object to need to access its own properties from within its script. Rather than referring to itself by name, it can do this using the terms me and my:
put the age of me
if my name begins with "S" then return "Smilin' " & my name
Undefined Properties and the StrictProperties global property
Ordinarily, if you access a non-existent property of an object, SenseTalk will simply return empty as the value of that property. Occasionally, this may lead to trouble, such as if you inadvertently misspell the name of a property. To help with debugging your script in such cases, or if you simply prefer a more rigorous approach, you may set the strictProperties global property to true. When this property is set, any attempt to access a property of an object that has not been previously set will throw an exception.
put a new object into emptyObj
put (property abc of emptyObj) is empty -- displays 'true'
set the strictProperties to true
put (property abc of emptyObj) is empty -- throws an exception
Using “Object” to Ensure Object Access
In most contexts, SenseTalk can recognize when an object is needed and will treat a string value as the name of a script object. However, there are times when the meaning may be ambiguous. In these situations, the word object can be used to indicate that a value should be treated as an object name:
put "Person"'s greeting -- since "Person" is text, not an object, the greeting function will be called
put (object "Person")'s greeting -- treat "Person" as an object name, and access its greeting property