class
and object
expressions.
When you define methods using the expressions described in this section, you do not have to do anything extra to create a new generic function or to make sure the existing generic function knows about your new definition. ScriptX automatically updates the appropriate generic function for your method definition or creates a new generic function for new methods.
The generic function you name, and thus, the one you specialize, is the
one that is visible in the current module. You can have many different generic
functions of the same name in different modules. For information on names
and modules, see "Modules" on page 183.
Method Definition Syntax
method methodName
where:self
arguments -> body
As elsewhere, you must refer to class and instance variables in the body of a method definition using the standard class and instance variable access expression (
self.
variable ). ScriptX does not provide a mechanism for automatically accessing class and instance variables by name within a method definition.return
expression to specify the value the method returns. Without an explicit return, the method returns the value of the last expression evaluated. When in doubt on a return value, consider returning self
, undefined
, or OK
.object myObj (RootObject)
inst vars a:500
inst methods
method incrementA self inc -> (
self.a := self.a + inc
)
end
class GenericClass (RootObject)
instance methods
method printClass self -> (
print ("I'm an instance of " + (getClassName self))
)
method printMe self -> (
format debug "This is me: %*.\n" self @normal
printClass self
)
end
global myGenericClass := new GenericClass
printMe myGenericClass
This is me: GenericClass@0x1161b7c
"I'm an instance of GenericClass"
class MyLL (Array)
instance methods
method addItemToBeginning self item -> (
addNth self 1 item
format debug "%* added " item @normal
return self
)
end
global myLinkedList := new MyLL
addItemToBeginning myLinkedList 12345
12345 added #(12345) as MyLL
More Examples
MyClass
, defines three methods:
addAllIVs
, which takes no required arguments (except self
) and sums the values of the instance variables a
, b
, and c
.
class MyClass ()
instance vars a,b,c
instance methods
method addAllIVs self -> (
self.a + self.b + self.c
)
method addEmUp self #rest allArgs -> (
local s := 0
for i in allArgs do (s := s + i)
s := s + (addAllIVs self)
)
method changeIVs self #key incA:(10) incB:(10) incC:(10) -> (
self.a := self.a + incA
self.b := self.b + incB
self.c := self.c + incC
print self.a; print self.b; print self.c
return self
)
end
Now that the class and its methods have been defined, here are some examples of its use:
object exmpl (MyClass)
settings a:1, b:5, c:12
end
exmpl.a
1
exmpl.b
5
exmpl.c
12
addAllIVs exmpl
18
addEmUp exmpl 3 8 4 6
39
changeIVs exmpl -- no keywords, defaults are all 10
11
15
22
changeIVs exmpl incA:4 incB:-7
15
8
32
Free methods are methods that can be defined outside the boundaries of a class or object definition. Free methods are useful for adding or redefining method definitions in existing classes or objects without having to redefine the entire class or object.
Free method definitions look similar to regular method definitions, with the addition of a special clause that specifies the class or object to which this method belongs. There are two forms of method definition: one for adding instance methods to an object, and one for adding either class or instance methods to a class.
method methodName self { object object } args -> body
[ class ] method methodName self { class class } args -> bodyIn both forms, methodName is the name of the generic function that invokes this method. If the
class
reserved word is included before the method
reserved word, that method is a class method.The args part of each free method definition supplies the arguments this method takes (besides
self
). These can be positional arguments, rest arguments, or keyword arguments. See Chapter 5, "Functions, Threads and Pipes," for a description of each of these types of arguments.
The expression within braces is called a restriction, and is used to point to the class or object that the method is associated with. Note that the restriction must come after the
self
argument, but before any other arguments.
myArray[1]
)
tryThisOut := #(1,2)
method appendSum self {object tryThisOut} n m ->
(append self (n + m); return self)
appendSum tryThisOut 3 4
#(1,2,7)
The second form adds either an instance method or a class method to the class specified by class, which can contain one of the expressions from the list above for object.
class MyClass () end
method jellydonut self {class MyClass} name ->
format debug "%* is not a jellydonut!\n" name @unadorned
i := new MyClass
jellydonut i "cruller"
"cruller is not a jellydonut!"
When adding methods to the core classes, be careful not to override existing methods. That is, avoid defining a method of the same name as one that already exists in that class, particularly initialization (
init
and afterInit
) methods. A class may depend on internal behavior defined by those methods; overriding those methods may cause errors in the operation of that class. As an alternative to overriding existing methods in the ScriptX Core Classes, consider creating a subclass of that class with your own definitions instead.
TwoDShape
defines the draw
method; a subclass of TwoDShape
that re-implements the draw
method is said to override the draw
defined in its superclass. For an example of instance specialization, an instance of TwoDShape
that re-implements the draw
method is said to override the draw
defined in its class.When you override an existing method, your method must have the same name and positional arguments as the original method, but can have new keyword arguments. In addition, the method can use the functionality provided by superclasses by calling
nextMethod
. As such, there are two ways to override an existing method:
nextMethod
.
You use
nextMethod
to call the overriding method from the body of a method, like this:method methodName self arguments -> (
The
. . . optionally do something here . . .
nextMethod self arguments
. . . optionally do something else here . . .
)
nextMethod
expression passes the call upward through the inheritance hierarchy, calling methods with the same name, so that each superclass can invoke its own implementation of that method on the instance. Another way to look at this is that invoking nextMethod
simply allows you to call the original methods that would have been invoked had you not overridden. Each nextMethod
encountered calls the next method up the chain, hence the term nextMethod
.nextMethod
calls methods in superclasses, you should supply to nextMethod
any arguments that you want those superclasses to handle. For example, since the draw
method in TwoDShape
takes three arguments (self
, surface
, clip
), when you create a subclass of TwoDShape
where you override draw
, you would call nextMethod
with those same arguments:method draw surface clip -> (
nextMethod self surface clip
)
append
method for a given object such that the original append
is called only if the item to be appended to this object is an instance of the ImmediateInteger
class. (The append
method is defined in the Sequence
class.) Otherwise, an error message is printed to the debug
stream and the method returns undefined
:global myArrayOfIntegers := #()
-- override the append method on myArrayOfIntegers
method append self {object myArrayOfIntegers} item -> (
if (getClass item = ImmediateInteger) then (
nextMethod self item
)
else (
format debug "Not an ImmediateInteger: %*\n" item @normal
return undefined
)
)
Multiple Inheritance with nextMethod
RootObject
. In this case, nextMethod
calls the next method in depth-first order, as described in "Multiple Inheritance" on page 122. Use getSupers
to see the order in which the methods are called. For example, given a class that inherits from both TwoDShape
and Bounce
, nextMethod
calls the classes in the order shown here:
The ScriptX core classes provide mechanisms to define additional
behavior for certain methods and protocols in the core classes. For example, if
you are interested in overriding any of the methods that add or delete items in
collections, as the example above does, consider the advantages of using the
class BouncingShape (TwoDShape, Bounce)
end
getSupers BouncingShape | print
#<Class Substrate:Bounce>
#<Class Substrate:TwoDController>
#<Class Substrate:Controller>
#<Class Substrate:IndirectCollection>
#<Class Substrate:Collection>
#<Class Substrate:TwoDShape>
#<Class Substrate:TwoDPresenter>
#<Class Substrate:Presenter>
#<Class Substrate:RootObject>
IndirectCollection
class. For more information, see the "Collections"
chapter of the ScriptX Components Guide.
This document is part of the ScriptX Language Guide, one of the volumes of the ScriptX Technical Reference Series. ScriptX is developed by the ScriptX Engineering Team at Apple Computer, successor to the Kaleida Engineering Team at Kaleida Labs, Inc.