Top Banner
CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009
63

CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

Dec 19, 2015

Download

Documents

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

CSEP505: Programming LanguagesLecture 9: Finish Concurrency; Start OOP

Dan Grossman

Winter 2009

Page 2: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 2

Where are we

• Thread creation

• Communication via shared memory– Synchronization with join, locks

• Message passing a la Concurrent ML– Very elegant– First done for Standard ML, but available in several

functional languages– Can wrap synchronization abstractions to make new ones – In my opinion, quite under-appreciated

• Back to shared memory for software transactions

Page 3: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 3

The basics

• Send and receive return “events” immediately• Sync blocks until “the event happens”• Separating these is key in a few slides

(* event.mli; Caml’s version of CML *)type ’a channel (* messages passed on channels *)val new_channel : unit -> ’a channel

type ’a event (* when sync’ed on, get an ’a *)val send : ’a channel -> ’a -> unit eventval receive : ’a channel -> ’a eventval sync : ’a event -> ’a

Page 4: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 4

Simple version

Note: In SML, the CML book, etc:send = sendEvt

receive = recvEvtsendNow = sendrecvNow = recv

let sendNow ch a = sync (send ch a) (* block *)let recvNow ch = sync (receive ch) (* block *)

Helper functions to define blocking sending/receiving• Message sent when 1 thread sends, another receives• One will block waiting for the other

Page 5: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 5

Example

Make a thread to handle changes to a bank account• mkAcct returns 2 channels for talking to the thread• More elegant/functional approach: loop-carried state

type action = Put of float | Get of floattype acct = action channel * float channellet mkAcct () = let inCh = new_channel() in let outCh = new_channel() in let bal = ref 0.0 in (* state *) let rec loop () = (match recvNow inCh with (* blocks *) Put f -> bal := !bal +. f; | Get f -> bal := !bal -. f);(*allows overdraw*) sendNow outCh !bal; loop () in Thread.create loop (); (inCh,outCh)

Page 6: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 6

Example, continued

get and put functions use the channels

let get acct f = let inCh,outCh = acct in sendNow inCh (Get f); recvNow outChlet put acct f = let inCh,outCh = acct in sendNow inCh (Put f); recvNow outCh

type acct val mkAcct : unit -> acctval get : acct->float->floatval put : acct->float->float

Outside the module, don’t see threads or channels!!

– Cannot break the communication protocol

Page 7: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 7

Key points

• We put the entire communication protocol behind an abstraction

• The infinite-loop-as-server idiom works well– And naturally prevents races– Multiple requests implicitly queued by CML implementation

• Don’t think of threads like you’re used to– “Very lightweight”

• Asynchronous = spawn a thread to do synchronous– System should easily support 100,000 threads– Cost about as much space as an object plus “current stack”

• Quite similar to “actors” in OOP– Cost no time when blocked on a channel– Real example: A GUI where each widget is a thread

Page 8: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 8

Simpler example

• A stream is an infinite set of values– Don’t compute them until asked– Again we could hide the channels and thread

let squares = new_channel()let rec loop i = sendNow squares (i*i); loop (i+1)let _ = Thread.create loop 1

let one = recvNow squareslet four = recvNow squareslet nine = recvNow squares…

Page 9: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 9

So far

• sendNow and recvNow allow synchronous message passing

• Abstraction lets us hide concurrency behind interfaces

• But these block until the rendezvous, which is insufficient for many important communication patterns

• Example: add : int channel -> int channel -> int– Must choose which to receive first; hurting performance or

causing deadlock if other is ready earlier

• Example: or : bool channel -> bool channel -> bool– Cannot short-circuit

• This is why we split out sync and have other primitives

Page 10: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 10

The cool stuff

• choose: when synchronized on, block until 1 of the events occurs• wrap: An event with the function as post-processing

– Can wrap as many times as you want• Note: Skipping a couple other key primitives (e.g., for timeouts)

type ’a event (* when sync’ed on, get an ’a *)val send : ’a channel -> ’a -> unit eventval receive : ’a channel -> ’a eventval sync : ’a event -> ’a

val choose : ’a event list -> ’a eventval wrap : ’a event -> (’a -> ’b) -> ’b event

Page 11: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 11

“And from or”

• Choose seems great for “until one happens”• But a little coding trick gets you “until all happen”• Code below returns answer on a third channel

let add in1 in2 out = let ans = sync(choose[ wrap (receive in1) (fun i -> sync (receive in2) + i); wrap (receive in2) (fun i -> sync (receive in1) + i)]) in sync (send out ans)

• 1st hw5 problem a more straightforward use of choose/wrap

Page 12: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 12

Another example

let or in1 in2 = let ans = sync(choose[ wrap (receive in1) (fun b -> b || sync (receive in2)); wrap (receive in2) (fun b -> b || sync (receive in1))]) in sync (send out ans)

• Not blocking in the case of inclusive or would take a little more cleverness– Spawn a thread to receive the second input (and ignore it)

Page 13: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 13

Circuits

If you’re an electrical engineer:• send and receive are ends of a gate• wrap is combinational logic connected to a gate • choose is a multiplexer (no control over which)

So after you wire something up, you sync to say “wait for communication from the outside”

And the abstract interfaces are related to circuits composing

If you’re a UNIX hacker:• UNIX select is “sync of choose”• A pain that they can’t be separated – want to nest chooses

Page 14: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 14

Remaining comments

• The ability to build bigger events from smaller ones is very powerful

• Synchronous message passing, well, synchronizes

• Key by-design limitation is that CML supports only point-to-point communication

• By the way, Caml’s implementation of CML itself is in terms of queues and locks– Works okay on a uniprocessor

Page 15: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 15

Where are we

• Thread creation

• Communication via shared memory– Synchronization with join, locks

• Message passing a la Concurrent ML

• Back to shared memory for software transactions– And an important digression to memory-consistency models

Page 16: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 16

Atomic

An easier-to-use and harder-to-implement primitive

lock acquire/release (behave as if)no interleaved computation

void deposit(int x){synchronized(this){ int tmp = balance; tmp += x; balance = tmp;}}

void deposit(int x){atomic { int tmp = balance; tmp += x; balance = tmp; }}

Page 17: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 17

Syntax irrelevant / Versus TM

• In a language with higher-order functions, no need for a new statement form– atomic : (unit -> ‘a) -> ‘a– “Just a library” to the parser/type-checker– But not just a library to the compiler and run-time system

• Atomic blocks vs. transactional memory (TM)– One high-level language construct vs. one way to implement– Neither necessarily needs the other (though common)– TM: Start, sequence of r/w, end

• Implicit conflict detection, abort, restart, atomic commit

Page 18: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 18

Viewpoints

Software transactions good for:• Software engineering (easier to avoid races & deadlocks)• Performance (optimistic “no conflict” without locks)

Why are they good:• Get parallelism unless there are actual run-time memory conflicts

– As easy as coarse-grained locks but parallelism of fine-grained• Push to language implementation conflict detection/recovery

– Much like garbage collection: convenient but has costsShameless plug: The Transactional Memory / Garbage Collection Analogy (OOPSLA 07)

• Hope to talk about at end of next week, time permitting– TM-based implementations super cool, but not this course

Page 19: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 19

Solving “tough” problems

• Trivial to write append correctly in an atomic world– Even if previous version didn’t “expect” an append method– And get all the parallelism you can reasonably expect

synchronized length() {…}synchronized getChars(…) {…}synchronized append(StringBuffer sb) { int len = sb.length(); if(this.count + len > this.value.length) this.expand(…); sb.getChars(0,len,this.value,this.count); …}

Page 20: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 20

Another tough problem

Operations on a double-ended queue

void enqueue_left(Object)void enqueue_right(Object)Object dequeue_left()Object dequeue_right()

Correctness– Behave like a queue, even when ≤ 2 elements– Dequeuers wait if necessary, but can’t “get lost”

Parallelism– Access both ends in parallel, except when ≤ 1 elements

(because ends overlap)

Example thanks to Maurice Herlihy

Page 21: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 21

Good luck with that…

• One lock?– No parallelism

• Locks at each end?– Deadlock potential– Gets very complicated, etc.

• Waking blocked dequeuers?– Harder than it looks

Page 22: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 22

Actual Solution

• A clean solution to this apparent “homework problem” would be a publishable result(!)– In fact it was: [Michael & Scott, PODC 96]

• So locks and condition variables are not a “natural methodology” for this problem

• Implementation with transactions is trivial– Wrap 4 operations written sequentially in atomic

• With retry for dequeuing from empty queue– Correct and parallel

Page 23: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 23

Not a panacea

• Over-sellers say barely-technically-accurate things like “deadlock is impossible”– Really…

class Lock { bool b = false; void acquire() { while(true) { while(b) /*spin*/; atomic { if(b) continue; b = true; return; } } } void release() { b = false; }}

Page 24: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 24

Not a panacea

Problems “we’re workin’ on”

• Abort/retry interacts poorly with “launch missiles”

• Many software TM implementations provide a weaker and under-specificed semantics when there are transactional/non-transactional data races (a long and crucial story)

• Memory-consistency model questions remain and may be worse than with locks…

Page 25: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 25

Memory models

• A memory-consistency model (or just memory model) for a shared-memory language specifies “what write(s) a read can see.”

• The gold standard is sequential consistency (Lamport):

“the results of any execution is the same as if the operations

of all the processors were executed in some sequential order,

and the operations of each individual process appear in this

sequences in the order specified by its program”

Page 26: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 26

Abusing SC

Assuming sequential consistency (SC), assert below cannot fail– Despite data races

x = 1;

y = 1;

r = y;

s = x;assert(s>=r);

initially x=0, y=0

Page 27: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 27

You don’t get SC

• Modern imperative and OO languages do not promise SC– (If they say anything at all)– The hardware makes it prohibitively expensive– Renders unsound almost every compiler optimization

• Example: common-subexpression elimination

x = a+b;y = a;z = a+b;assert(z>=y);

b = 1;a = 1;

initially a=0, b=0

Page 28: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 28

Relaxed != Nothing

• But (especially in a safe language), have to promise something– When is code “correctly synchronized”?– What can the implementation do if the code is not “correctly

synchronized”?

• The definitions are very complicated and programmers can usually ignore them, but do not assume SC

Page 29: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 29

Real languages

• Java: If every sequentially consistent execution of program P is data-race free, then every execution of program P is equivalent to some sequentially consistent execution– Not the definition; a theorem about the definition– Actual definition balances need of programmers, compilers,

and hardware• Not defined in terms of “allowed optimizations”• Even bad code can’t corrupt the SecurityManager

• C++ (proposed): Roughly, any data race is as undefined as an array-bounds error. No such thing as a benign data race and no guarantees if you have one. (In practice, programmers will assume things, like they do with casts.)

• Many other languages: Eerie silence

Page 30: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 30

Synchronization and ordering

• In relaxed memory models, synchronization operations typically impose ordering constraints– Example: this code cannot violate the assertion

x = 1;sync(lk){}y = 1;

r = y;sync(lk){}s = x;assert(s>=r);

initially x=0, y=0

• Recent research papers on what ordering constraints atomic blocks impose– Is an empty atomic block a no-op?

Page 31: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 31

Onto OOP

Now let’s talk about object-oriented programming• What’s different from what we have been doing

– Boils down to one important thing• How do we define it (will stay informal)• Supporting extensibility• Several other “advanced” topics

Page 32: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 32

OOP the sales pitch

OOP lets you:

1. Build extensible software concisely

2. Exploit an intuitive analogy between interaction of physical entities and interaction of software pieces

It also:• Raises tricky semantic and style issues that require careful

investigation• Is more complicated than functions

– Does not necessarily mean it’s worse

Page 33: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 33

So what is it?

OOP “looks like this”, but what is the essence

class Pt1 extends Object { int x; int get_x() { x } unit set_x(int y) { self.x = y } int distance(Pt1 p) { p.get_x() – self.get_x() } constructor() { x = 0 }}

class Pt2 extends Pt1 { int y; int get_y() { y } int get_x() { 34 + super.get_x() } constructor() { super(); y = 0 }}

Page 34: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 34

Class-based OOP

In (pure) class-based OOP:

1. Everything is an object

2. Objects communicate via message (handled by methods)

3. Objects have their own state

4. Every object is an instance of a class

5. A class describes its instances’ behavior

Why is this approach such a popular way to structure software?...

Page 35: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 35

OOP can mean many things

• An ADT (private fields)• Inheritance: method/field extension, method override• Implicit self/this• Dynamic dispatch• Subtyping• All the above in one (class) definition

Design question: Better to have small orthogonal features or one “do it all” feature?

Anyway, let’s consider how “unique to OO” each is…

Page 36: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 36

OO for ADTs

Object/class members (fields, methods, constructors) often have visibilities

What code can invoke a member/access a field?• Methods of the same object?• Methods defined in same class?• Methods defined in a subclass?• Methods in another “boundary” (package, assembly, friend, …)• Methods defined anywhere?

Page 37: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 37

Subtyping for hiding

• As seen before, can use upcasts to “hide” members– Modulo downcasts– Modulo binary-method problems

• With just classes, upcasting is limited• With interfaces, can be more selective

interface I { int distance(Pt1 p); }class Pt1 extends Object { … I f() { self } …}

Page 38: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 38

Records of functions

If OOP = functions + private state, we already have it– But it’s more (e.g., inheritance)

type pt1 = {get_x : unit -> int; set_x : int -> unit; distance : pt1 -> int} let pt1_constructor () = let x = ref 0 in let rec self = { get_x = (fun() -> !x); set_x = (fun y -> x := y); distance = (fun p -> p.get_x() +self.get_x()) } in self

Page 39: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 39

Subtyping

Most class-based OO languages purposely “confuse” classes & types • If C is a class, then C is a type• If C extends D (via declaration) then C ≤ D• Subtyping is reflexive and transitive

Novel subtyping? • New members in C just width subtyping• “Nominal” (by name) instead of structural• What about override…

Page 40: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 40

Subtyping, continued

• If C extends D, overriding m, what do we need:– Arguments contravariant (assume less)– Result covariant (provide more)

• Many “real” languages are more restrictive– Often in favor of static overloading

• Some languages try to be more flexible– At expense of run-time checks/casts

Good we studied this in a simpler setting

Page 41: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 41

Inheritance & override

Subclasses:• inherit superclass’s members• can override methods• can use super calls

Can we code this up in Caml?• No because of field-name reuse and lack of subtyping

– But ignoring that we can get close…

Page 42: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 42

Almost OOP?

let pt1_constructor () =let x = ref 0 in

let rec self = { get_x = (fun() -> !x); set_x = (fun y -> x := y); distance = (fun p -> p.get_x()+self.get_x()) } in self(* note: field reuse precludes type-checking *)let pt2_constructor () = (* extends Pt1 *) let r = pt1_constructor () in let y = ref 0 in let rec self = { get_x = (fun() -> 34 + r.get_x()); set_x = r.set_x; distance = r.distance; get_y = (fun() -> !y); } in self

Page 43: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 43

Problems

Small problems:• Have to change pt2_constructor whenever

pt1_constructor changes

• But OOPs have tons of “fragile base class” issues too– Motivates C#’s version support

• No direct access to “private fields” of superclass

Big problem:• Distance method in a pt2 doesn’t behave how it does in OOP• We do not have late-binding of self (i.e., dynamic dispatch)

Page 44: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 44

The essence

Claim: Class-based objects are:

• So-so ADTs• Same-old record and function subtyping• Some syntactic sugar for extension and override

• A fundamentally different rule for what self maps to in the environment

Page 45: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 45

More on late-binding

Late-binding, dynamic-dispatch, and open-recursion all related issues (nearly synonyms)

Simplest example I know:

let c1 () = let rec r = { even = (fun i -> i=0 || r.odd (i-1)); odd = (fun i -> i<>0 && r.even (i-1)) } in r

let c2 () = let r1 = c1() in let rec r = { even = r1.even; (* still O(n) *) odd = (fun i -> i % 2 == 1) } in r

Page 46: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 46

More on late-binding

Late-binding, dynamic-dispatch, and open-recursion all related issues (nearly synonyms)

Simples example I know:

class C1 { int even(int i) { i=0 || odd (i-1)) } int odd(int i) { i!=0 && even (i-1)) }}

class C2 { /* even is now O(1) */ int odd(int i) {i % 2 == 1}}

Page 47: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 47

The big debate

Open recursion:• Code reuse: improve even by just changing odd• Superclass has to do less extensibility planning

Closed recursion:• Code abuse: break even by just breaking odd• Superclass has to do more abstraction planning

Reality: Both have proved very useful; should probably just argue over “the right default”

Page 48: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 48

Our plan

• Dynamic dispatch is the essence of OOP

• How can we define/implement dynamic dispatch?– Basics, not super-optimized versions (see P501)

• How do we use/misuse overriding?

• Why are subtyping and subclassing separate concepts worth keeping separate?

Page 49: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 49

Defining dispatch

Methods “compile down” to functions taking self as an extra argument– Just need self bound to “the right thing”

Approach #1: • Each object has 1 “code pointer” per method• For new C() where C extends D:

– Start with code pointers for D (recursive definition!)– If C adds m, add code pointer for m– If C overrides m, change code pointer for m

• self bound to the (whole) object in method body

Page 50: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 50

Defining dispatch

Methods “compile down” to functions taking self as an extra argument– Just need self bound to “the right thing”

Approach #2: • Each object has 1 run-time tag• For new C() where C extends D:

– Tag is C• self bound to the object• Method call to m reads tag, looks up (tag,m) in a global table

Page 51: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 51

Which approach?

• The two approaches are very similar– Just trade space for time via indirection

• vtable pointers are a fast encoding of approach #2

• This “definition” is low-level, but with overriding simpler models are probably wrong

Page 52: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 52

Our plan

• Dynamic dispatch is the essence of OOP

• How can we define/implement dynamic dispatch?– Basics, not super-optimized versions (see P501)

• How do we use/misuse overriding?– Functional vs. OO extensibility

• Why are subtyping and subclassing separate concepts worth keeping separate?

Page 53: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 53

Overriding and hierarchy design

• Subclass writer decides what to override to modify behavior– Often-claimed, unchecked style issue: overriding should

specialize behavior• But superclass writer typically knows what will be overridden

• Leads to notion of abstract methods (must-override) – Classes w/ abstract methods can’t be instantiated– Does not add expressiveness– Adds a static check– C++ calls this “pure virtual”

Page 54: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 54

Overriding for extensibility

class Exp { // a PL example; constructors omitted abstract Exp interp(Env); abstract Typ typecheck(Ctxt); abstract Int toInt(); }class IntExp extends Exp { Int i; Value interp(Env e) { self } Typ typecheck(Ctxt c) { new IntTyp() } Int toInt() { i }}class AddExp extends Exp { Exp e1; Exp e2; Value interp(Env e) { new IntExp(e1.interp(e).toInt().add( e2.interp(e).toInt())) } Int toInt() { throw new BadCall() } // typecheck on next page}

Page 55: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 55

Example cont’d

• We did addition with “pure objects”– Int has a binary add method

• To do AddExp::typecheck the same way, assume equals is defined appropriately (structural on Typ):

Type typecheck(Ctxt c) { e1.typecheck(c).equals(new IntTyp()).ifThenElse( e2.typecheck(c).equals(new IntTyp()).ifThenElse( (fun () -> new IntTyp()), (fun () -> throw new TypeError())), (fun () -> throw new TypeError()))}

• Pure “OOP” avoids instanceof IntTyp and if-statements– (see Smalltalk for syntactic sugar)

Page 56: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 56

More extension

• Now suppose we want MultExp– No change to existing code, unlike ML!– In ML, can “prepare” with “Else of ‘a” constructor

• Now suppose we want a toString method– Must change all existing classes, unlike ML!– In OOP, can “prepare” with a “Visitor pattern”

• Extensibility has many dimensions – most require forethought!

Page 57: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 57

The Grid

• You know it’s an important idea if I take the time to draw a picture

interp typecheck toString …

IntExp Code Code Code Code

AddExp Code Code Code Code

MultExp Code Code Code Code

… Code Code Code Code

1 new function

1 new class

Page 58: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 58

Back to MultExp

• Even in OOP, MultExp is easy to add, but you’ll copy the typecheck method of AddExp

• Or maybe AddExp extends MultExp, but it’s a kludge• Or maybe refactor into BinaryExp with subclasses AddExp

and MultExp– So much for not changing existing code– Awfully heavyweight approach to a helper function

Page 59: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 59

Our plan

• Dynamic dispatch is the essence of OOP

• How can we define/implement dynamic dispatch?– Basics, not super-optimized versions (see P501)

• How do we use/misuse overriding?– Functional vs. OO extensibility

• Why are subtyping and subclassing separate concepts worth keeping separate?

Page 60: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 60

Subtyping vs. subclassing

• Often convenient confusion: C a subtype of D if and only if C a subclass of D

• But more subtypes are sound– If A has every field and method that B has (at appropriate

types), then subsume B to A– Interfaces help, but require explicit annotation

• And fewer subtypes could allow more code reuse…

Page 61: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 61

Non-subtyping example

Pt2 ≤ Pt1 is unsound here:

class Pt1 extends Object { int x; int get_x() { x } bool compare(Pt1 p){ p.get_x() == self.get_x() }}class Pt2 extends Pt1 { int y; int get_y() { y } bool compare(Pt2 p) { // override p.get_x() == self.get_x() && p.get_y() == self.get_y() }}

Page 62: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 62

What happened

• Could inherit code without being a subtype• Cannot always do this

– what if get_x called self.compare with a Pt1Possible solutions:– Re-typecheck get_x in subclass– Use a really fancy type system– Don’t override compare

• Moral: Not suggesting “subclassing not subtyping” is useful, but the concepts of inheritance and subtyping are orthogonal

Page 63: CSEP505: Programming Languages Lecture 9: Finish Concurrency; Start OOP Dan Grossman Winter 2009.

5 March 2009 CSE P505 Winter 2009 Dan Grossman 63

Now what?

• That’s basic class-based OOP– Note: Not all OOPLs use classes

(Javascript, Self, Cecil, …)

• Now some “fancy” stuff– Typechecking– Multiple inheritance; multiple interfaces– Static overloading– Multimethods– Revenge of bounded polymorphism