forked from SquareBracketAssociates/UpdatedPharoByExample
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPharoObjectModel.pier
1156 lines (896 loc) · 52.2 KB
/
PharoObjectModel.pier
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
!The Pharo object model
@cha:model
The Pharo programming model is heavily inspired by that of Smalltalk. It
is simple and uniform: everything is an object, and objects communicate only by
sending each other messages. This simplicity and uniformity, however, can be a
source of difficulty for programmers used to other languages. In this chapter we
present the core concepts of the Pharo object model. In particular we discuss
the consequences of representing classes as objects. This will be extended in
Chapter *: Classes and Metaclasses>../Metaclasses/Metaclasses.pillar@cha:metaclasses*.
!!The rules of the model
@sec:rules
The object model is based on a set of simple rules that are applied
''uniformly''. The rules are as follows:
""Rule 1"". Everything is an object.
""Rule 2"". Every object is an instance of a class.
""Rule 3"". Every class has a superclass.
""Rule 4"". Everything happens by sending messages.
""Rule 5"". Method lookup follows the inheritance chain.
Let us look at each of these rules in some detail.
!!Everything is an Object
The mantra ''everything is an object'' is highly contagious. After only a short
while working with Pharo, you will start to be surprised at how this rule
simplifes everything you do. Integers, for example, are truly objects, so you
can send messages to them, just as you do to any other object.
[[[
3 + 4
--> 7 "send '+ 4' to 3, yielding 7"
20 factorial
--> 2432902008176640000 "send factorial, yielding a big number"
]]]
The representation of the result of the expression ==20 factorial== is certainly
different from the representation of ==7==, but because they are both objects,
none of the code, not even the implementation of ==factorial==, needs to know
about this.
Perhaps the most fundamental consequence of this rule is that classes are
objects too.
@@important Classes are objects too.
Furthermore, classes are not second-class objects: they are really first-class
objects that you can send messages to, inspect, and so on. This means that Pharo
is a truly reflective system, which gives a great deal of expressive power to
developers.
Deep in the implementation of Pharo, there are three different kinds of objects:
# ordinary objects with instance variables that are passed by references
# ''small integers'' that are passed by value
# indexable objects like arrays that hold a contiguous portion of memory
The beauty of Pharo is that you normally don't need to care about the
differences between these three kinds of object.
!!Every object is an instance of a class
Every object has a class; you can find out which by sending it the message
==class==.
[[[
1 class
--> SmallInteger
20 factorial class
--> LargePositiveInteger
'hello' class
--> ByteString
#(1 2 3) class
--> Array
(4@5) class
--> Point
Object new class
--> Object
]]]
A class defines the ''structure'' of its instances via instance variables, and
the ''behavior'' of its instances via methods. Each method has a name, called
its ''selector'', which is unique within the class.
Since ''classes are objects'', and ''every object is an instance of a class'',
it follows that classes must also be instances of classes. A class whose
instances are classes is called a ''metaclass''. Whenever you create a class,
the system automatically creates a metaclass for you. The metaclass defines the
structure and behavior of the class that is its instance. 99\% of the time you
will not need to think about metaclasses, and may happily ignore them. (We will
have a closer look at metaclasses in Chapter *: Classes and Metaclasses>../Metaclasses/Metaclasses.pillar@cha:metaclasses*.)
!!!Instance variables
Instance variables in Pharo are private to the ''instance'' itself. This is in
contrast to Java and C\+\+, which allow instance variables (also known as
''fields'' or ''member variables'') to be accessed by any other instance that
happens to be of the same class. We say that the ''encapsulation boundary'' of
objects in Java and C\+\+ is the class, whereas in Pharo it is the instance.
In Pharo, two instances of the same class cannot access each other's instance
variables unless the class defines ''accessor methods''. There is no language
syntax that provides direct access to the instance variables of any other
object. (Actually, a mechanism called reflection does provide a way to ask
another object for the values of its instance variables; meta-programming is
intended for writing tools like the object inspector, whose sole purpose is to
look inside other objects.)
Instance variables can be accessed by name in any of the instance methods of the
class that defines them, and also in the methods defined in its subclasses. This
means that Pharo instance variables are similar to ''protected'' variables in
C\+\+ and Java. However, we prefer to say that they are private, because it is
considered bad style in Pharo to access an instance variable directly from a
subclass.
!!!!Instance encapsulation example
The method ==Point>>dist:== computes the distance between the receiver and another
point. The instance variables ==x== and ==y== of the receiver are accessed
directly by the method body. However, the instance variables of the other point
must be accessed by sending it the messages ==x== and ==y==.
[[[label=scr:distance|caption=Distance between two points|language=smalltalk
Point>>>dist: aPoint
"Answer the distance between aPoint and the receiver."
| dx dy |
dx := aPoint x - x.
dy := aPoint y - y.
^ ((dx * dx) + (dy * dy)) sqrt
]]]
[[[
1@1 dist: 4@5
--> 5.0
]]]
The key reason to prefer instance-based encapsulation to class-based
encapsulation is that it enables different implementations of the same
abstraction to coexist. For example, the method ==dist:== need not know or
care whether the argument ==aPoint== is an instance of the same class as the
receiver. The argument object might be represented in polar coordinates, or as
a record in a database, or on another computer in a distributed system. As long
as it can respond to the messages ==x== and ==y==, the code in Script
*@scr:distance* will still work.
!!!Methods
All methods are public. Methods are grouped into protocols that indicate their
intent. Some common protocol names have been established by convention, for
example, ==accessing== for all accessor methods, and ==initialization== for
establishing a consistent initial state for the object. The protocol ==private==
is sometimes used to group methods that should not be seen from outside.
Nothing, however, prevents you from sending a message that is implemented by
such a "private" method.
Methods can access all instance variables of the object. Some developers prefer
to access instance variables only through accessors. This practice has some
value, but it also clutters the interface of your classes, and worse, exposes
private state to the world.
!!!The instance side and the class side
Since classes are objects, they can have their own instance variables and their
own methods. We call these ''class instance variables'' and ''class methods'',
but they are really no different from ordinary instance variables and methods:
class instance variables are just instance variables defined by a metaclass, and
class methods are just methods defined by a metaclass.
A class and its metaclass are two separate classes, even though the former is an
instance of the latter. However, this is largely irrelevant to you as a
programmer: you are concerned with defining the behavior of your objects and the
classes that create them.
+Browsing a class and its metaclass>file://figures/colorInstanceClassSide.png|label=fig:colorInstanceClassSide|width=90+
For this reason, the browser helps you to browse both class and metaclass as if
they were a single thing with two "sides": the ''instance side'' and the ''class
side'', as shown in Figure *@fig:colorInstanceClassSide*. By default, when you
select a class in the browser, you're browsing the ''instance'' side (i.e., the
methods that are executed when messages are sent to an ''instance'' of
==Color==). Clicking on the ""Class side"" checkbox (located under the class
pane) switches you over to the ''class side'' (the methods that will be executed
when messages are sent to the ''class'' ==Color== itself).
For example, ==Color blue== sends the message ==blue== to the class ==Color==.
You will therefore find the method ==blue== defined on the class side of
==Color==, not on the instance side.
[[[
aColor := Color blue. "Class-side method blue (convenient instance creation method)"
aColor
--> Color blue "Color instances are self-evaluating"
aColor red
--> 0.0 "Instance-side accessor method red (returns the red RGB value)"
aColor blue
--> 1.0 "Instance-side accessor method blue (returns the blue RGB value)"
]]]
You define a class by filling in the template proposed on the instance side.
When you accept this template, the system creates not just the class that you
defined, but also the corresponding metaclass (which you can then edit by clicking
on the ""Class side"" checkbox). The only part of the metaclass creation
template that makes sense for you to edit directly is the list of the metaclass's
instance variable names.
Once a class has been created, browsing its instance side (""Class side""
checkbox un-checked) lets you edit and browse the methods that will be possessed
by instances of that class (and of its subclasses).
!!!Class methods
Class methods can be quite useful; browse ==Color class== for some good
examples. You will see that there are two kinds of methods defined on a class:
''instance creation methods'', like ==Color class>>blue==, and those that perform
a utility function, like ==Dictionary class>>removeUnreferencedKeys==. This is
typical, although you will occasionally find class methods used in other ways.
It is convenient to place utility methods on the class side because they can be
executed without having to create any additional objects first. Indeed, many of
them will contain a comment designed to make it easy to execute them.
% Commenting out for the moment: showColorCube is no longer in the image.
% TODO: create a different example
%Browse method ==Color class>>showColorCube==, double-click just inside the
%quotes on the comment =="Color showColorCube"== and press ==CMD-d==. You will
%see the effect of executing this method. Select
%==World > System > Restore display (r)== to undo the effects.
For those familiar with Java and C\+\+, class methods may seem similar to static
methods. However, the uniformity of the Pharo object model (where classes are
just regular objects) means that they are somewhat different: whereas Java
static methods are really just statically-resolved procedures, Pharo class
methods are dynamically-dispatched methods. This means that inheritance,
overriding and super-sends work for class methods in Pharo, whereas they don't
work for static methods in Java.
!!!Class instance variables
With ordinary instance variables, all the instances of a class have the same set
of variable names (though each instance has its own private set of values), and
the instances of its subclasses inherit those names. The story is exactly the
same with class instance variables: each class has its own private class
instance variables. A subclass will inherit those class instance variables,
''but the subclass will have its own private copies of those variables''. Just
as objects don't share instance variables, neither do classes and their
subclasses share class instance variables.
For example, you could use a class instance variable called ==count== to keep
track of how many instances you create of a given class. However, any subclass
would have its own ==count== variable, so subclass instances would be counted
separately.
!!!!Example: Class instance variables and subclasses
Suppose we define the class ==Dog==, and its subclass ==Hyena==. ==Hyena==
will naturally inherit the class instance variable ==count== from ==Dog==.
[[[caption=Dogs and Hyenas|language=smalltalk
Object subclass: #Dog
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'PBE-CIV'
Dog class
instanceVariableNames: 'count'
Dog subclass: #Hyena
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'PBE-CIV'
]]]
Now suppose we define class methods for ==Dog== to initialize its ==count== to
==0==, and to increment it when new instances are created:
[[[caption=Keeping count of new dogs|label=scr:dogCount|language=smalltalk
Dog class>>>initialize
count := 0.
Dog class>>>new
count := count +1.
^ super new
Dog class>>>count
^ count
]]]
Now when we create a new ==Dog==, its count is incremented, and so is that of
every ==Hyena== (but the hyenas are counted separately).
''Side note:'' Notice the use of ==initialize== on the classes, in the following
code. In Pharo, when you instantiate an object such as ==Dog new==,
==initialize== is called automatically as part of the ==new== message send (you
can see for yourself by browsing ==Behavior new==). But with classes, simply
defining them does not automatically call ==initialize==, and so we have to call
it explicitly here. See also the discussion about lazy initialization, below.
[[[label=scr:dogCount2|language=smalltalk
Dog initialize.
Hyena initialize.
Dog count --> 0
Hyena count --> 0
aDog := Dog new.
Dog count --> 1 "Incremented"
Hyena count --> 0 "Still the same"
]]]
Class instance variables are private to a class in exactly the same way that
instance variables are private to the instance. Since classes and their
instances are different objects, this has the following consequences:
""1."" A class does not have access to the instance variables of its own
instances. So, the class ==Color== does not have access to the variables of an
object instantiated from it, ==aColorRed==. In other words, just because a class
was used to create an instance (using ==new== or a helper instance creation
method like ==Color red==), it doesn't give ''the class'' any special direct
access to that instance's variables. The class instead has to go through the
accessor methods (a public interface) just like any other object.
""2."" The reverse is also true: an ''instance'' of a class does not have access
to the class instance variables of its class. In our example above, ==aDog==
(an individual instance) does not have direct access to the ==count== variable
of the ==Dog== class (except, again, through an accessor method).
@@important A class does not have access to the instance variables of its own instances. An instance of a class does not have access to the class instance variables of its class.
For this reason, instance initialization methods must always be defined on the
instance side, the class side has no access to instance variables, and so cannot
initialize them! All that the class can do is to send initialization messages,
using accessors, to newly created instances.
Java has nothing equivalent to class instance variables. Java and C++ static
variables are more like Pharo class variables (discussed in
Section *@sec:classVars*), since in those languages all of the subclasses and
all of their instances share the same static variable.
!!!!Example: Defining a Singleton
The Singleton pattern provides a typical example of the use of class instance
variables and class methods. Imagine that we would like to implement a class
==WebServer==, and to use the Singleton pattern to ensure that it has only one
instance.
We define the class ==WebServer== as follow.
[[[caption=A sample singleton class, ==WebServer==|language=smalltalk
Object subclass: #WebServer
instanceVariableNames: 'sessions'
classVariableNames: ''
poolDictionaries: ''
category: 'Web'
]]]
Then, clicking on the ""Class side"" checkbox, we add the (class) instance
variable ==uniqueInstance==.
[[[caption=The class side of the singleton class|language=smalltalk
WebServer class
instanceVariableNames: 'uniqueInstance'
]]]
As a result, the ''class'' ==WebServer== now has a new instance variable
(in addition to the variables that it inherits from ==Object==, such as
==superclass== and ==methodDict==).
We can now define a class method named ==uniqueInstance==, as shown below. This
method first checks whether ==uniqueInstance== has been initialized. If it has
not, the method creates an instance and assigns it to the class instance
variable ==uniqueInstance==. Finally the value of ==uniqueInstance== is
returned. Since ==uniqueInstance== is a class instance variable, this method can
directly access it.
[[[label=scr:uniqueInstance|caption=Class-side accessor method ==uniqueInstance==|language=smalltalk
WebServer class>>>uniqueInstance
uniqueInstance ifNil: [ uniqueInstance := self new ].
^ uniqueInstance
]]]
The first time that ==WebServer uniqueInstance== is executed, an instance of the
class ==WebServer== will be created and assigned to the ==uniqueInstance==
variable. The next time, the previously created instance will be returned
instead of creating a new one. (This pattern, checking if a variable is nil
in an accessor method, and initializing its value if it is nil, is called
''lazy initialization'').
Note that the instance creation code inside the conditional in Script *@scr:uniqueInstance*
is written as ==self new== and not as ==WebServer new==. What is the difference?
Since the ==uniqueInstance== method is defined in ==WebServer class==, you might
think that there is no difference. And indeed, until someone creates a subclass
of ==WebServer==, they are the same. But suppose that ==ReliableWebServer== is
a subclass of ==WebServer==, and inherits the ==uniqueInstance== method. We
would clearly expect ==ReliableWebServer uniqueInstance== to answer a
==ReliableWebServer==. Using ==self== ensures that this will happen, since
==self== will be bound to the respective receiver, here the classes
==WebServer== and ==ReliableWebServer==. Note also that ==WebServer== and
==ReliableWebServer== will each have a different value for their
==uniqueInstance== instance variable.
!!!!A note on lazy initialization
''Do not over-use the lazy initialization pattern.'' The setting of initial
values for instances of objects generally belongs in the ==initialize== method.
Putting initialization calls only in ==initialize== helps from a readability
perspective \-\- you don't have to hunt through all the accessor methods to see
what the initial values are. Although it may be tempting to instead initialize
instance variables in their respective accessor methods (using ==ifNil:==
checks), avoid this unless you have a good reason.
For example, in our ==uniqueInstance== method above, we used lazy initialization
because users won't typically expect to call ==WebServer initialize==. Instead,
they expect the class to be "ready" to return new unique instances. Because of
this, lazy initialization makes sense. Similarly, if a variable is expensive to
initialize (opening a database connection or a network socket, for example),
you will sometimes choose to delay that initialization until you actually need
it.
!!Every class has a superclass
Each class in Pharo inherits its behaviour and the description of its
structure from a single ''superclass''. This means that Smalltalk has single
inheritance.
[[[
SmallInteger superclass --> Integer
Integer superclass --> Number
Number superclass --> Magnitude
Magnitude superclass --> Object
Object superclass --> ProtoObject
ProtoObject superclass --> nil
]]]
Traditionally the root of the Smalltalk inheritance hierarchy is the class
==Object== (since everything is an object). In Pharo, the root is actually a
class called ==ProtoObject==, but you will normally not pay any attention to
this class. ==ProtoObject== encapsulates the minimal set of messages that all
objects ''must'' have. However, most classes inherit from ==Object==, which
defines many additional messages that almost all objects ''should'' understand
and respond to. Unless you have a very good reason to do otherwise, when
creating application classes you should normally subclass ==Object==, or one of
its subclasses.
A new class is normally created by sending the message ==subclass:
instanceVariableNames: ...== to an existing class. There are a few other methods
to create classes. To see what they are, have a look at ==Class== and its
==subclass creation== protocol (found in the ==Kernel-Classes== package).
Although Pharo does not provide multiple inheritance, it supports a mechanism
called Traits for sharing behaviour across unrelated classes. Traits are
collections of methods that can be reused by multiple classes that are not
related by inheritance. Using traits allows one to share code between different
classes without duplicating code.
!!!Abstract methods and abstract classes
An abstract class is a class that exists to be subclassed, rather than to be
instantiated. An abstract class is usually incomplete, in the sense that it
does not define all of the methods that it uses. The "placeholder" methods, those
that the other methods assume but which are not themselves defined, are called
abstract methods.
Pharo has no dedicated syntax to specify that a method or a class is
abstract. Instead, by convention, the body of an abstract method consists of the
expression ==self subclassResponsibility==. This is known as a ''marker
method'', and indicates that subclasses have the responsibility to define a
concrete version of the method. ==self subclassResponsibility== methods should
always be overridden, and thus should never be executed. If you forget to
override one, and it is executed, an exception will be raised.
Similarly, a class is considered abstract if one of its methods is abstract.
Nothing actually prevents you from creating an instance of an abstract class;
everything will work until an abstract method is invoked.
!!!!Example: the abstract class ==Magnitude==
==Magnitude== is an abstract class that helps us to define objects that can be
compared to each other. Subclasses of ==Magnitude== should implement the methods
==<==, ===== and ==hash==. Using such messages, ==Magnitude== defines other
methods such as ==>==, ==>===, ==<===, ==max:==, ==min:== ==between:and:== and
others for comparing objects. Such methods are inherited by subclasses. The
method ==Magnitude>><== is abstract, and defined as shown in Script
*@scr:MagnitudeLessThan*.
[[[label=scr:MagnitudeLessThan|caption===Magnitude>> <==|language=smalltalk
< aMagnitude
"Answer whether the receiver is less than the argument."
^self subclassResponsibility
]]]
By contrast, the method ==>=== is concrete, and is defined in terms of ==<==.
[[[label=scr:MagnitudeGTE|caption===Magnitude>> >===|language=smalltalk
>= aMagnitude
"Answer whether the receiver is greater than or equal to the argument."
^(self < aMagnitude) not
]]]
The same is true of the other comparison methods (they are all defined in terms
of the abstract method ==<==).
==Character== is a subclass of ==Magnitude==; it overrides the
==<== method (which, if you recall, is marked as abstract in ==Magnitude== by
the use of ==self subclassResponsibility==) with its own version (see
Script *@scr:characterLessThan*). ==Character== also explicitly defines methods
===== and ==hash==; it inherits from ==Magnitude== the methods ==>===, ==<===,
==~=== and others.
[[[label=scr:characterLessThan|caption===Character>> <===|language=smalltalk
< aCharacter
"Answer true if the receiver's value < aCharacter's value."
^self asciiValue < aCharacter asciiValue
]]]
!!!Traits
A ''trait'' is a collection of methods that can be included in the behaviour of
a class without the need for inheritance. This makes it easy for classes to have
a unique superclass, yet still share useful methods with otherwise unrelated
classes.
To define a new trait, simply right-click in the class pane and select
==Add Trait==, or replace the subclass creation template by the trait
creation template, below.
[[[label=scr:tauthor|caption=Defining a new trait|language=smalltalk
Trait named: #TAuthor
uses: { }
category: 'PBE-LightsOut'
]]]
Here we define the trait ==TAuthor== in the package ==PBE-LightsOut==. This
trait does not ''use'' any other existing traits. In general we can specify a
''trait composition expression'' of other traits to use as part of the ==uses:==
keyword argument. Here we simply provide an empty array.
Traits may contain methods, but no instance variables. Suppose we would like to
be able to add an ==author== method to various classes, independent of where
they occur in the hierarchy.
We might do this as follows:
[[[label=scr:author|caption=An author method|lang=smalltalk
TAuthor>>>author
"Returns author initials"
^ 'on' "oscar nierstrasz"
]]]
Now we can use this trait in a class that already has its own superclass, for
instance the ==LOGame== class that we defined in Chapter *: A First Application>../FirstApplication/FirstApplicatioin.pier@cha:firstApp*. We
simply modify the class creation template for ==LOGame== to include a ==uses:==
keyword argument that specifies that ==TAuthor== should be used.
[[[label=scr:sbegamewithtrait|caption=Using a trait|language=smalltalk
BorderedMorph subclass: #LOGame
uses: TAuthor
instanceVariableNames: 'cells'
classVariableNames: ''
poolDictionaries: ''
category: 'PBE-LightsOut'
]]]
If we now instantiate ==LOGame==, it will respond to the ==author== message as
expected.
[[[
LOGame new author --> 'on'
]]]
Trait composition expressions may combine multiple traits using the ==+==
operator. In case of conflicts (i.e., if multiple traits define methods with
the same name), these conflicts can be resolved by explicitly removing these
methods (with ==-==), or by redefining these methods in the class or trait that
you are defining. It is also possible to ''alias'' methods (with ==@==),
providing a new name for them.
Traits are used in the system kernel. One good example is the class ==Behavior==.
[[[label=scr:behaviorwithtraits|caption=Behavior defined using traits|language=smalltalk
Object subclass: #Behavior
uses: TPureBehavior @ {#basicAddTraitSelector:withMethod:->#addTraitSelector:withMethod:}
instanceVariableNames: 'superclass methodDict format'
classVariableNames: 'ObsoleteSubclasses'
poolDictionaries: ''
category: 'Kernel-Classes'
]]]
Here we see that the method ==addTraitSelector:withMethod:== defined in the
trait ==TPureBehavior== has been aliased to
==basicAddTraitSelector:withMethod:==.
!!Everything happens by sending messages
This rule captures the essence of programming in Pharo.
In procedural programming (and in some object-oriented languages such as Java),
the choice of which piece of code to execute when a procedure is called is made
by the caller. The caller chooses the procedure or function to execute
''statically'', by name.
In Pharo, we do ''not'' "call methods". Instead, we ''send
messages.'' The choice of terminology is significant. Each object has its
own responsibilities. We do not ''tell'' an object what to do by applying some
procedure to it. Instead, we politely ''ask'' an object to do something for us
by sending it a message. The message is not a piece of code: it is nothing
but a name and a list of arguments. The receiver then decides how to respond
by selecting its own ''method'' for doing what was asked. Since different
objects may have different methods for responding to the same message, the
method must be chosen ''dynamically'', when the message is received.
[[[
3 + 4 --> 7 "send message + with argument 4 to integer 3"
(1@2) + 4 --> 5@6 "send message + with argument 4 to point (1@2)"
]]]
As a consequence, we can send the ''same message'' to different objects, each of
which may have ''its own method'' for responding to the message. We do not tell
the ==SmallInteger== ==3== or the ==Point== ==(1@2)== how to respond to the
message ==\+ 4==. Each has its own method for ==\+==, and responds to ==\+ 4==
accordingly.
One of the consequences of Pharo's model of message sending is that it
encourages a style in which objects tend to have very small methods and delegate
tasks to other objects, rather than implementing huge, procedural methods that
assume too much responsibility. Joseph Pelrine expresses this principle
succinctly as follows:
''"Don't do anything that you can push off onto someone else."''
Many object-oriented languages provide both static and dynamic operations for
objects. In Pharo there are only dynamic message sends. For example, instead of
providing static class operations, we simply send messages to classes (which
are simply objects).
Ok, so ''nearly'' everything in Pharo happens by sending messages. At some point
action must take place:
''Variable declarations'' are not message sends. In fact, variable declarations
are not even executable. Declaring a variable just causes space to be allocated
for an object reference.
''Assignments'' are not message sends. An assignment to a variable causes that
variable name to be freshly bound in the scope of its definition.
''Returns'' are not message sends. A return simply causes the computed result to
be returned to the sender.
''Primitives'' (and Pragmas/annotations) are not message sends. They are
implemented in the virtual machine.
Other than these few exceptions, pretty much everything else does truly happen
by sending messages. In particular, since there are no ''public fields'' in
Pharo, the only way to update an instance variable of another object is to send
it a message asking that it update its own field. Of course, providing setter
and getter methods for all the instance variables of an object is not good
object-oriented style. Joseph Pelrine also states this very nicely:
''"Don't let anyone else play with your data."''
!!Method lookup follows the inheritance chain
What exactly happens when an object receives a message?
The process is quite simple:
""1."" The class of the receiver looks up the method to use to handle the message.
""2."" If this class does not have that method method defined, it asks its
superclass, and so on, up the inheritance chain.
""3."" When the method is found, the arguments are bound to the parameters of
the method, and the virtual machine executes it.
It is essentially as simple as that. Nevertheless there are a few questions that
need some care to answer:
-''What happens when a method does not explicitly return a value?''
-''What happens when a class reimplements a superclass method?''
-''What is the difference between ==self== and ==super== sends?''
-''What happens when no method is found?''
The rules for method lookup that we present here are conceptual; virtual machine
implementors use all kinds of tricks and optimizations to speed up method
lookup. That's their job, but you should never be able to detect that they are
doing something different from our rules.
First let us look at the basic lookup strategy, and then consider these further
questions.
!!!Method lookup
Suppose we create an instance of ==EllipseMorph==.
[[[
anEllipse := EllipseMorph new.
]]]
If we now send this object the message ==defaultColor==, we get the result
==Color yellow==.
[[[
anEllipse defaultColor --> Color yellow
]]]
The class ==EllipseMorph== implements ==defaultColor==, so the appropriate
method is found immediately.
[[[label=scr:defaultColor|caption=A locally implemented method|language=smalltalk
EllipseMorph>>>defaultColor
"Answer the default color/fill style for the receiver"
^ Color yellow
]]]
In contrast, if we send the message ==openInWorld== to ==anEllipse==, the method
is not immediately found, since the class ==EllipseMorph== does not implement
==openInWorld==. The search therefore continues in the superclass,
==BorderedMorph==, and so on, until an ==openInWorld== method is found in the
class ==Morph== (see Figure *@fig:openInWorldLookup*).
[[[label=scr:openInWorld|caption=An inherited method|language=smalltalk
Morph>>>openInWorld
"Add this morph to the world."
self openInWorld: self currentWorld
]]]
+Method lookup follows the inheritance hierarchy>file://figures/openInWorldLookup.png|width=8|label=fig:openInWorldLookup+
!!!Returning self
Notice that ==EllipseMorph>>defaultColor== (Script *@scr:defaultColor*)
explicitly returns ==Color yellow==, whereas ==Morph>>openInWorld== (Script
*@scr:openInWorld*) does not appear to return anything.
Actually a method ''always'' answers a message with a value (which is, of course,
an object). The answer may be defined by the ==^== construct in the method, but
if execution reaches the end of the method without executing a ==^==, the method
still answers a value \-\- it answers the object that received the message. We
usually say that the method ''answers self'', because in Pharo the
pseudo-variable ==self== represents the receiver of the message, much like the
keyword ==this== in Java. Other languages, such as Ruby, by default return the
value of the last statement in the method. Again, this is not the case in Pharo,
instead you can imagine that a method without an explicit return ends with
==^ self==.
This suggests that ==openInWorld== is equivalent to ==openInWorldReturnSelf==,
defined below.
[[[label=scr:openInWorldReturnSelf|caption=Explicitly returning ==self==|language=smalltalk
Morph>>>openInWorld
"Add this morph to the world."
self openInWorld: self currentWorld
^ self "Don't do this unless you mean it!"
]]]
Why is explicitly writing ==^ self== not a good thing to do? Well, when you
return something explicitly, you are communicating that you are returning
something of interest to the sender. When you explicitly return ==self==, you
are saying that you expect the sender to use the returned value. This is not the
case here, so it is best not to explicitly return ==self==.
This is a common idiom in Smalltalk, which Kent Beck refers to as ''Interesting
return value'':
''"Return a value only when you intend for the sender to use the value."''
!!!Overriding and extension
If we look again at the ==EllipseMorph== class hierarchy in Figure
*@fig:openInWorldLookup*, we see that the classes ==Morph== and ==EllipseMorph==
both implement ==defaultColor==. In fact, if we open a new morph (==Morph new
openInWorld==) we see that we get a blue morph, whereas an ellipse will be
yellow by default.
We say that ==EllipseMorph== ''overrides'' the ==defaultColor== method that it
inherits from ==Morph==. The inherited method no longer exists from the point of
view of ==anEllipse==.
Sometimes we do not want to override inherited methods, but rather ''extend''
them with some new functionality, that is, we would like to be able to invoke
the overridden method ''in addition to'' the new functionality we are defining
in the subclass. In Pharo, as in many object-oriented languages that support
single inheritance, this can be done with the help of ==super== sends.
The most important application of this mechanism is in the ==initialize==
method. Whenever a new instance of a class is initialized, it is critical to
also initialize any inherited instance variables.
However, the knowledge of how to do this is already captured in the
==initialize== methods of each of the superclass in the inheritance chain. The
subclass has no business even trying to initialize inherited instance variables!
It is therefore good practice whenever implementing an initialize method to send
==super initialize== before performing any further initialization:
[[[label=scr:morphinit|caption=Super initialize|language=smalltalk
BorderedMorph>>>initialize
"initialize the state of the receiver"
super initialize.
self borderInitialize
]]]
@@important An ==initialize== method should always start by sending ==super initialize==.
!!!Self sends and super sends
We need ==super== sends to compose inherited behaviour that would otherwise be
overridden. The usual way to compose methods, whether inherited or not, however,
is by means of ==self== sends.
How do ==self== sends differ from ==super== sends? Like ==self==, ==super==
represents the receiver of the message. The only thing that changes is the
method lookup. Instead of lookup starting in the class of the receiver, it
starts in the superclass of the class of the method where the ==super== send
occurs.
Note that ==super== is ''not'' the superclass! It is a common and natural
mistake to think this. It is also a mistake to think that lookup starts in the
superclass of the receiver. We shall see with the following example precisely
how this works.
Consider the message ==constructorString==, which we can send to any morph:
[[[
anEllipse constructorString
--> '(EllipseMorph newBounds: (0@0 corner: 50@40) color: Color yellow)'
]]]
The return value is a string that can be evaluated to recreate the morph.
How exactly is this result obtained through a combination of ==self== and
==super== sends? First, ==anEllipse constructorString== will cause the method
==constructorString== to be found in the class ==Morph==, as shown in Figure
*@fig:constructorStringLookup*.
+==self== and ==super== sends>file://figures/constructorStringLookup.png|label=fig:constructorStringLookup|width=80+
[[[label=scr:constructorString|caption=A ==self== send|language=smalltalk
Morph>>>constructorString
^ String streamContents: [:s | self printConstructorOn: s indent: 0].
]]]
The method ==Morph>>constructorString== performs a ==self== send of
==printConstructorOn:indent:==. This message is also looked up, starting in the
class ==EllipseMorph==, and found in ==Morph==. This method in turn does a
==self== send of ==printConstructorOn:indent:nodeDict:==, which does a ==self==
send of ==fullPrintOn:==. Once again, ==fullPrintOn:== is looked up starting in
the class ==EllipseMorph==, and ==BorderedMorph>>fullPrintOn:== is found in
==BorderedMorph== (see Figure *@fig:constructorStringLookup* once again). What
is critical to notice is that the ==self== send causes the method lookup to
start again in the class of the receiver, namely the class of ==anEllipse==.
@@important A ==self== send triggers a ''dynamic method lookup starting in the class of the receiver.''
[[[label=scr:fullPrintOn|caption=Combining ==super== and ==self== sends|language=smalltalk
BorderedMorph>>>fullPrintOn: aStream
aStream nextPutAll: '('.
super fullPrintOn: aStream.
aStream nextPutAll: ') setBorderWidth: '; print: borderWidth;
nextPutAll: ' borderColor: ', (self colorString: borderColor)
]]]
At this point, ==BorderedMorph>>fullPrintOn:== does a ==super== send to extend
the ==fullPrintOn:== behaviour it inherits from its superclass. Because this is
a ==super== send, the lookup now starts in the superclass of the class where the
==super== send occurs, namely in ==Morph==. We then immediately find and
evaluate ==Morph>>fullPrintOn:==.
Note that the ==super== lookup did not start in the superclass of the receiver.
This would have caused lookup to start from ==BorderedMorph==, resulting in an
infinite loop!
@@important A ==super== send triggers a ''static method lookup'' starting in the superclass of the class of the method performing the ==super== send.
If you think carefully about ==super== send and Figure
*@fig:constructorStringLookup*, you will realize that ==super== bindings are
static: all that matters is the class in which the text of the ==super== send is
found. By contrast, the meaning of ==self== is dynamic: it always represents the
receiver of the currently executing message. This means that ''all'' messages
sent to ==self== are looked up by starting in the receiver's class.
!!!Message not understood
What happens if the method we are looking for is not found?
Suppose we send the message ==foo== to our ellipse. First the normal method
lookup would go through the inheritance chain all the way up to ==Object== (or
rather ==ProtoObject==) looking for this method. When this method is not found,
the virtual machine will cause the object to send ==self doesNotUnderstand:
#foo==. (See Figure *@fig:fooNotFound*.)
+Message ==foo== is not understood>file://figures/fooNotFound.png|label=fig:fooNotFound|width=80+
Now, this is a perfectly ordinary, dynamic message send, so the lookup starts
again from the class ==EllipseMorph==, but this time searching for the method
==doesNotUnderstand:==. As it turns out, ==Object== implements
==doesNotUnderstand:==. This method will create a new ==MessageNotUnderstood==
object which is capable of starting a Debugger in the current execution context.
Why do we take this convoluted path to handle such an obvious error? Well, this
offers developers an easy way to intercept such errors and take alternative
action. One could easily override the method ==Object>>doesNotUnderstand:== in
any subclass of ==Object== and provide a different way of handling the error.
In fact, this can be an easy way to implement automatic delegation of messages
from one object to another. A ==Delegator== object could simply delegate all
messages it does not understand to another object whose responsibility it is to
handle them, or raise an error itself!
!!Shared variables
Now we will look at an aspect of Pharo that is not so easily covered by our five
rules: shared variables.
Pharo provides three kinds of shared variables:
""1."" ''Globally'' shared variables
""2."" ''Class variables'': variables shared between instances and classes. (Not
to be confused with class instance variables, discussed earlier).
""3."" ''Pool variables'': variables shared amongst a group of classes
The names of all of these shared variables start with a capital letter, to warn
us that they are indeed shared between multiple objects.
!!!Global variables
In Pharo, all global variables are stored in a namespace called ==Smalltalk==,
which is implemented as an instance of the class ==SystemDictionary==. Global
variables are accessible everywhere. Every class is named by a global variable.
In addition, a few globals are used to name special or commonly useful objects.
The variable ==Transcript== names an instance of ==TranscriptStream==, a stream
that writes to a scrolling window. The following code displays some information
and then goes to the next line in the ==Transcript==.
[[[
Transcript show: 'Pharo is fun and powerful' ; cr
]]]
Before you ==Do it==, open a transcript by selecting ==World > Tools... >
Transcript==.
''Hint:'' Writing to the ==Transcript== is slow, especially when the transcript
window is open. So, if you experience some sluggishness and are writing to the
Transcript, think about collapsing it.
!!!!Other useful global variables
""==Smalltalk=="" is the instance of ==SystemDictionary== that defines all of
the globals (including ==Smalltalk== itself). The keys to this dictionary are
the symbols that name the global objects in Pharo code. So, for example:
[[[language=smalltalk
Smalltalk at: #Boolean --> Boolean
]]]
Since ==Smalltalk== is itself a global variable:
[[[language=smalltalk
Smalltalk at: #Smalltalk --> a SystemDictionary(lots of globals)
(Smalltalk at: #Smalltalk) == Smalltalk --> true
]]]
""==Sensor=="" is an instance of ==EventSensor==, and represents input to Pharo.
For example, ==Sensor keyboard== answers the next character input on the
keyboard, and ==Sensor leftShiftDown== answers ==true== if the left shift key is
being held down, while ==Sensor mousePoint== answers a ==Point== indicating the
current mouse location.
""==World=="" is an instance of ==PasteUpMorph== that represents the screen.
==World bounds== answers a rectangle that defines the whole screen space; all
Morphs on the screen are submorphs of ==World==.
""==ActiveHand=="" is the current instance of ==HandMorph==, the graphical
representation of the cursor. ==ActiveHand=='s submorphs hold anything being
dragged by the mouse.
""==Undeclared=="" is another dictionary, which contains all the undeclared
variables. If you write a method that references an undeclared variable, the
browser will normally prompt you to declare it, for example as a global or as an
instance variable of the class. However, if you later delete the declaration,