Frame provides two basic capabilities: structure and inheritance. The synergy between these two features, as well as their use within the Livelink Scripting Framework, provides developers with many powerful capabilities.
In its most basic form, a Frame consists of a set of named slots. Each slot can contain a single value and can be referenced with the dot operator. Thus, a Frame is the equivalent of a structure in most programming languages. A frame's slots are identified with a String and cannot be accidentally created (e.g., due to a typo) during an assignment. In addition, referencing a slot that does not exist will produce a runtime exception, rather than returning an Undefined value. There are operations in the Frame class that permit a Frame's structure to be examined and even modified.
In addition to the structuring mechanisms described above, Frame has the additional ability to have special slots, parent slots, which affect how slots are looked up within a Frame. A parent slot acts like a normal slot, except that it cannot be assigned to. In addition, when a Frame's slot is referenced with a dot operator (e.g., myFrame.someSlot), the presence of one or more parent slots can affect how and where the slot is used.
When a frame dot expression occurs on the left side of an assignment (i.e., an lvalue), then the following steps occur:
When a frame dot expression occurs in any other context where its value is desired (i.e., an rvalue), then the following steps occur:
In other words, Frame behaves as a prototype-based object (similar to OSpace's ObjRef datatype). Frame has the following advantages compared to ObjRef:
Frame has some shortcomings with respect to ObjRef:
If inheritance is ignored, both Frame and Assoc share certain features that make them amenable to being used to build structures within the OScript environment. Assoc values can have arbitrary keys of any datatype, whereas Frames must have named slots of type String. In addition, Assoc is most useful when the structure is extremely dynamic and where keys may need to be added at any time. Frame is more useful for safety and debugging because references to incorrectly typed slot names will be cause an error during testing and debugging, rather than continuing with possibly erroneous results. Of course, Assoc is also very useful when it is used for the purpose for which it was designed, as an Associative array. Assoc can efficiently map between arbitrary keys (of any datatype) and their associated values.
If a slot named "Destructor" is available on the Frame or its parents, and that slot's value is executable, then the slot will be invoked when the Frame is destroyed. Note that the Frame will still exist during the execution of Destructor(), and if the refcount of the Frame has increased during Destructor(), then the frame will NOT be deleted. Be careful with this feature.
Livelink values use a simple reference-counting scheme to determine when an unused value can be deleted. Powerful datatypes such as Assoc and Frame both share the unfortunate characteristic that it is possible to create cyclic data structures whose values can never be deleted automatically. Please be careful, as this can result in memory leaks and other even nastier side effects.
Here is an example of using Frame.New() to create a frame representing a 3D coordinate:
Frame coordinateProto = Frame.New( {}, { { "x", 0 }, { "y", 0 }, { "z", 0 } } ) Frame aCoordinate = Frame.New( { { "parentCoordinate", coordinateProto } }, \ { { "x", 5 } } ) echo( coordinateProto.x ) echo( coordinateProto.y ) echo( coordinateProto.z ) echo( aCoordinate.x ) aCoordinate.x = 55 echo( aCoordinate.x ) coordinateProto.x = 10 echo( aCoordinate.x ) coordinateProto.y = 10 echo( aCoordinate.y ) aCoordinate.y = 100 echo( aCoordinate.y ) echo( coordinateProto.zzzz ) // runtime error. feature not found exception.
The output from the above is:
0 0 0 5 55 55 // Changing the value of the proto slot doesn't change the child's slot 10 // Changing the value of the parent slot does change the child's inherited slot 100 // Child's override hides parent's value.
Integer FrameType
A constant indicating the datatype number of the datatype Frame, such that, if Type( val ) == Frame.FrameType, then val is a Frame.
kAnywhereInteger kAnywhere
Used with Frame.HasSlot() to indicate that both the frame and its parents should be searched.
kFrameInteger kFrame
Used with Frame.HasSlot() to indicate that only the frame should be searched.
kParentsInteger kParents
Used with Frame.HasSlot() to indicate that only the frame's parents should be searched.
AddParentBoolean AddParent( Frame theFrame, Dynamic parentDefinition )
Adds a parent to a frame.
theFrame | - | Frame to modify. |
parentDefinition | - | A List ({ slotName, parentValue }) specifying the parent slot. |
Note that a parent slot cannot be added if a slot with the given name is already present on the Frame.
Boolean AddSlot( Frame theFrame, String slotName, [Dynamic initialValue] )
Adds a non-parent slot of the specified name to the frame, optionally providing an initial value.
theFrame | - | Frame value to modify. |
slotName | - | String name of the slot to add. |
initialValue | - | Initial value for the slot. |
Note that this function will do nothing if the slot already exists on the frame. In this case, it will return FALSE.
Boolean HasParent( Frame theFrame, Dynamic parentValue )
Determines whether a parent with the specified value exists.
theFrame | - | Frame to examine. |
parentValue | - | Value of parent to look for. |
Note that parentValue is not the slot name for the parent, but is the actual associated parent value.
Boolean HasSlot( Frame theFrame, String slotName, [Integer searchOptions] )
Returns true if a Frame has a slot with the specified name.
theFrame | - | Frame within which to look for a slot. |
slotName | - | Name of the slot for which to look. |
searchOptions | - | If specified, Frame.kFrame, Frame.kParents, or the default Frame.kAnywhere. |
Using the above example frames, aCoordinate and coordinateProto:
echo( Frame.HasSlot( aCoordinate, "z" ) ) echo( Frame.HasSlot( aCoordinate, "z", Frame.kFrame ) ) echo( Frame.HasSlot( aCoordinate, "x", Frame.kParents ) ) echo( Frame.HasSlot( aCoordinate, "z", Frame.kAnywhere ) )
The output from the above is:
true false true true
Frame New( [List parentDefs], [List instanceDefs] )
Create a new frame with parent slots as specified in parentDefs and with instance slots as specified in instanceDefs.
parentDefs | - | A List of parent slot defs, each of which can consist of either a parent value or a List containing { parentSlotName, parentValue } . |
instanceDefs | - | A List of slot defs, each element of which is either a simple String slotName, or a List containing { slotName, slotValue } . See note below. |
Note that both parentDefs and instanceDefs above can specify slot definitions as a List consisting of a slotName and a slotValue. However, both of these definition lists permit slot definitions to be specified in a simpler, defaulted form. In the case of parentDefs, a definition can consist of a single, non-List value. This value becomes associated with an unnamed parent slot. In the case of instanceDefs, a String can be specified instead of a List-based slot definition. In this case, the String will become the slot name. See the class description for an example.
List Parents( Frame theFrame )
Return a List of parent definitions for the specified frame.
theFrame | - | Frame to examine. |
Using the above example frames, aCoordinate and coordinateProto:
echo( Frame.Parents( coordinateProto ) ) echo( Frame.Parents( aCoordinate ) )
The output from the above is:
{}
{{'parentCoordinate',<LL:Frame>{{}x:10,y:10,z:0}</LL:Frame>}}
Boolean RemoveParent( Frame theFrame, Dynamic parentDefinition )
Removes the specified parent definition from a Frame.
theFrame | - | Frame to modify. |
parentDefinition | - | A List ({ slotName, parentValue }) specifying the parent slot. |
This function does not remove the actual slot definition from the Frame, only the inheritance aspect. In order to remove the slot from the Frame, you must also use Frame.RemoveSlot().
Boolean RemoveSlot( Frame theFrame, String slotName )
Removes a slot from a frame.
theFrame | - | Frame from which to remove the slot. |
slotName | - | Name of the slot to remove. |
Note that this function will remove both parent slots and regular slots. However, when a parent slot is removed with this function, the parent will still be part of the Frame's prototype inheritance. In order to remove a parent from the Frame's inheritance, Frame.RemoveParent() must also be used.
List SlotNames( Frame theFrame )
Returns a List of Strings, corresponding to the names of the non-parent slots in theFrame.
theFrame | - | The frame to examine. |
Using the above example frames, aCoordinate and coordinateProto:
echo( Frame.SlotNames( coordinateProto ) ) echo( Frame.SlotNames( aCoordinate ) )
The output from the above is:
{'x','y','z'} {'x','y'}
List Slots( Frame theFrame )
Returns a List of non-parent slot descriptions. Each descriptions consists of a two-element List containing { slotName, slotValue }.
theFrame | - | The frame to examine. |
Using the above example frames, aCoordinate and coordinateProto:
echo( Frame.Slots( coordinateProto ) ) echo( Frame.Slots( aCoordinate ) )
The output from the above is:
{{'x',10},{'y',10},{'z',0}} {{'x',55},{'y',100}} // New or overridden slots are displayed.