I. Smalltalk A. Message expression syntax 1. Basic expressions a. Identifiers ------------------------------------------ SMALLTALK: IDENTIFIERS Identifier Conventions Upper case: ClassNames, ClassVars, Globals lower case: instancevars, messages Pseudo Variables self, super Special Names Instances: true, false, nil Classes: True, False, UndefinedObject Case matters: True (the class) vs. true (the instance) ------------------------------------------ b. Literals ------------------------------------------ LITERALS 3 30.45 $a "the character a" 'a string, don''t laugh!' #aSymbol #aSymbol:composed:ofKeywords: #* "also a symbol" #(1 2 3) "an array constant" #(foo bar 'and me') ------------------------------------------ 2. messages ------------------------------------------ KINDS OF MESSAGES Unary messages: 1 negated theta sin Pen new home green Binary messages: 3 + 4 5 - 4 * 2 (sum / count) * (reserve amount) Keyword messages: 3 max: 2 anArray at: 2 put: 3 (anArray at: 2) at: 3 ------------------------------------------ 3. Parsing ------------------------------------------ PRECEDENCE unary highest (most tight) binary keyword lowest (least tight) ASSOCITIVITY IS LEFT TO RIGHT 4 - 5 * 3 is (4 - 5) * 3 2 * theta sin is 2 * (theta sin) frame width: otherFrame width * 2 is frame width: ((otherFrame width) * 2) FOR YOU TO DO Parenthesize the following: foo at: 2 negated put: 4 + 3 * 2 negated ------------------------------------------ 4. cascaded messages, with semicolons (;) ------------------------------------------ CASCADED MESSAGES Transcript cr; show: 'also sent. '; cr; endEntry. ------------------------------------------ B. Blocks (closures) 1. statically scoped ------------------------------------------ BLOCKS (class BlockContext) Block = Examples: [ Transcript show: i ] [ k := k+1. Transcript show: k. k] [ :x :y | Transcript show: (x + y) ] FOR YOU TO DO What is (a) printed and (b) returned by: [ :y | Transcript show: (y + 2). y ] value: 4 ------------------------------------------ What are these like in Java? What good are these blocks? C. Control Structures ------------------------------------------ CONTROL STRUCTURES (x > y) ifTrue: [x printOn: Transcript] ifFalse: [y printOn: Transcript] (x > y) and: [y > z] [i > 0] whileTrue: [i printOn: Transcript. i := i - 1] 3 timesRepeat: [4 printOn: Transcript] (1 to: 5 by: 2) do: [:i | i printOn: Transcript] ------------------------------------------ ------------------------------------------ FOR YOU TO DO 1. Write a statement that prints the numbers from i to 10 in ascending order. 2. Write a statement that prints all the numbers in a set s that are greater than 3 s do: ------------------------------------------ D. Classes 1. Stack example ------------------------------------------ MAKING A CLASS Collection subclass: #Stack instanceVariableNames: 'elems' classVariableNames: '' poolDictionaries: '' category: 'ComS541-InClass' Really: From browser, select in category pane, "add item...", then type in "ComS541-InClass". Then in pane shown, edit in the parts that are different. then select "accept". TO ADD METHODS From browser, select instance vs. class and then add a method category, then (without selecting a method) edit the template provided ------------------------------------------ ------------------------------------------ "class methods" new ^ self basicNew initialize "instance methods" initialize elems := OrderedCollection new push: anElement elems addFirst: anElement pop elems removeFirst top ^ elems first do: aBlock elems do: aBlock printString | s | s := 'Stack('. elems do: [ :e | s := s , ' ', e printString]. ^ s , ')' ------------------------------------------ 2. Exercise ------------------------------------------ FOR YOU TO DO Complete the following class for a counter Object subclass: #Counter instanceVariableNames: ' ' classVariableNames: '' poolDictionaries: '' category: 'ComS541-Examples' "class methods" new "instance methods" initialize increment value printOn: aStream ------------------------------------------ E. object creation in Smalltalk ------------------------------------------ OBJECT CREATION How Stack new executes 1. send the "new" message to the class object "Stack" This: - runs the code ------------------------------------------ F. Semantics of variables, assignment, and mutation 1. object identity ------------------------------------------ OBJECT IDENTITY objects have an identitity = state = EXAMPLE | s | s := Stack new. s push: 1. s push: (Stack new push: 2). s push: s ------------------------------------------ 2. mutable vs. immutable ------------------------------------------ MUTABLE vs. IMMUTABLE OBJECTS def: an object is *mutable* if otherwise an object is *immutable* ------------------------------------------ are integers mutable in smalltalk? 3. variables and assigment ------------------------------------------ VARIABLES AND ASSIGNMENT def: a variable in Smalltalk is EXAMPLE p1 := p2 like Pascal: type PointStruct = record x,y: integer end; Point = ^PointStruct; var p1, p2: Point; { pointers!} new(p); new(p2); p1 := p2. or C: typedef struct {x,y:int} PointStruct; typedef PointStruct * Point; Point p1, p2; ... p1 = p2; ------------------------------------------ Does an assigment copy the object in Smalltalk? What kind of value is in a Smalltalk variable? II. Inheritance A. Hierarchy of classes B. Example: Adding size to stacks ------------------------------------------ INHERITANCE SIZED STACK class: SizedStack subclass of: Stack instance variables: size "instance methods" initialize ------------------------------------------ 1. Inheriting data 2. Inheriting operations Where does code execution start from? ------------------------------------------ STEPS IN MESSAGE SENDING let receiver = object that is sent message let static-class = class where code being executed is defined let rc = if receiver is not super, then receiver's (dynamic) class else if receiver is "super" then superclass of static-class 1. [fetch] look for method in class rc (a) if found: (b) if not found and rc is not "Object": (c) if not found and rc is "Object": 2. [execute] bind self to bind super to bind formals to allocate temporary vars (|temp|), execute the code of the method ------------------------------------------ a. semantic examples without super ------------------------------------------ MESSAGE PASSING EXAMPLE class: C subclass of: Object instance variables: "instance methods" m1 ^self m2 m2 ^ #C class: D subclass of: C instance variables: "instance methods" m2 ^ #D ------------------------------------------ what is the result of (D new) m1 ? What if we added another method to D... m3 ^ super m2 and then ran x m3? What if m2's body was "^ self m2" instead? b. semantic examples with super ------------------------------------------ MESSAGE PASSING EXAMPLE WITH SUPER class: C subclass of: Object instance variables: "instance methods" m1 ^ self m2 m2 ^ 'C' m3 ^ 'm3' class: D subclass of: C instance variables: "instance methods" m2 ^ super m3 m3 ^ 'E' ------------------------------------------ What is the result (if any) of the expression "C new m1"? What is the result (if any) of the expression "(D new) m1"? C. Example: CachedStack ------------------------------------------ CACHED STACK class: CachedStack subclass of: Stack instance variables: cache "instance methods" ------------------------------------------ 1. How to define printString? ------------------------------------------ INHERITING METHODS "instance method for CachedStack" printOn: aStream aStream nextPutAll: self class name. aStream nextPut: $(. (cache notNil) ifTrue: [ aStream nextPut: $ . cache printOn: aStream]. elems do: [ :e | s := s , ' ' , e printOn: aStream]. aStream nextPut: $). problems with the above: - ------------------------------------------ 2. Inheriting printString by factoring out common parts ------------------------------------------ INHERITANCE BY FACTORING OUT COMMON PARTS "instance methods for Stack" printOn: aStream aStream nextPutAll: self class name. aStream nextPut: $(. self printElementsOn: aStream. aStream nextPut: $). printElementsOn: aStream elems do: [ :e | e printOn: aStream] FOR YOU TO DO Define whatever methods are *needed* to have printString work for CachedStack ------------------------------------------ What else is needed to make this work? Can you define that? D. exercise Can you write Gague, like Counter, but also has a decrement method? E. Design issue: Single vs. multiple inheritance ------------------------------------------ SINGLE VS. MULTIPLE INHERITANCE def: a language has *single inheritance* if each class has def: a language has *multiple inheritance* if each class may have Motivating problem: what if want SizedCachedStack? ------------------------------------------ III. OO programming tactics what ways of programming are enabled by the mechanisms of OO languages like Smalltalk? A. mutation (from assignment) 1. mutatator operations ------------------------------------------ MUTATION | l | l := OrderedCollection new. l add: 3 ==> OrderedCollection(3) ------------------------------------------ 2. aliasing ------------------------------------------ ALIASING def: x and y are aliases for an object if Uses of aliasing: ------------------------------------------ How can you use aliasing in a program? How can you prevent aliasing? ------------------------------------------ EXPOSING THE REP class: RepExposureDemo subclass of: Object instance variables: rep "class methods" with: aSet ^ self new initialize: aSet "instance methods" initialize: aSet rep := aSet contains: anElement ^ rep contains: anElement What if a client does the following? | mySet it | mySet := Set new. it := RepExposureDemo with: mySet. mySet add: #isOk. it includes: #isOk ------------------------------------------ What could go wrong? ------------------------------------------ HOARE LOGIC BASEICS Triple {P} S {Q} means if P is true and S terminates then Q must be true Assigment axiom {Q[e/x]} x := e {Q} e.g., {(x = 5)[x+1/x]} x := x+1 {x = 5} ------------------------------------------ B. encapsulation (from objects) Is there a fundamental difference between data and operations? 1. composition vs. inheritance ------------------------------------------ COMPOSITION vs. INHERITANCE Given: class: Point subclass of: Object instance variables: x y composition |-----------------| |-----------| | Rectangle |------->*| Point | |-----------------| |-----------| class: Rectangle subclass of: Object instance variables: upperLeft, lowerRight inheritance |-----------------| | Point | |-----------------| | | / \ |-----------------| |-----------| | Rectangle |-------->| Point | |-----------------| |-----------| class: Rectangle subclass of: Point instance variables: lowerRight ------------------------------------------ should Rectangle be represented by 2 points or should it be a subclass of Point? What are the maintainence advantages or problems? C. protocols and polymorphism (from message passing) 1. protocols, types, and behavior ------------------------------------------ PROTOCOLS, TYPES, and BEHAVIOR def: the protocol (interface) of an object is def: the behavioral protocol of an object is def: a type is def: S is a subtype of T if def: S is a behavioral subtype of T if ------------------------------------------ What should a type be? What would it mean for S to not be a subtype of T? What should a behavioral subtype be? What would it mean for S to not be a behavioral subtype of T, and still be a subtype of T? 2. subtype vs. inheritance What does subtyping have to do with inheritance? What do we know about these? ------------------------------------------ SUBTYPING VS. INHERITANCE | ===================|====================== class | | subclass | | type | | subtype | | behavioral subtype | | ------------------------------------------ ------------------------------------------ ORTHOGANALITY EXAMPLE IntSetType ^ IntSetClass | subtype | IntervalType IntervalClass ------------------------------------------ How does C++ handle this? Java? What if we required subclass = subtype? What can we do to make a subclass not be a behavioral subtype? 3. polymorphism ------------------------------------------ POLYMORPHISM def: code is *polymorphic* if e.g., displayAll: objList objList do: [ :o | o display ] ------------------------------------------ What's the language design tradeoff here? D. code reuse (from inheritance) 1. factoring behavior so relevant parts can be inherited ------------------------------------------ FROM INHERITANCE What benefits? 1. Factoring ------------------------------------------ 2. abstract classes ------------------------------------------ ABSTRACT CLASSES def: an abstract class is def: a deferred method is ------------------------------------------ What are these called in C++? 3. frameworks 4. using message passing instead of tests in code ------------------------------------------ USE MESSAGE PASSING INSTEAD OF TESTS Boolean / \ True False "instance methods for True" ifTrue: b1 ifFalse: b2 ^ b1 value "instance methods for False" ifTrue: b1 ifFalse: b2 ^ b2 value ------------------------------------------ ------------------------------------------ FOR YOU TO DO Program: LISPList / \ Empty Cons "instance methods in LISPList:" length (self isEmpty) ifTrue: [^0] ifFalse: [^ 1 + self tail length] deferred methods: head, tail, isEmpty Notes: (Empty new) head gives an error Cons has instance variables: hd,tl and method head:tail: to initialize ------------------------------------------ How would you do that in Haskell? ------------------------------------------ ADTs vs. OO Programming Empty | Cons ====================|===================== head tail isEmpty ------------------------------------------