Recall The dynamic evolution of a system Robust and Energy-Efficient Real-Time Systems with offset asynchronous calls synchronous call T T blocked Lecture 2: Components & the Timber language buffered?? Time 1 2 1 2 Also recall Components A frequently used term in software engineering The static structure of a system Not always well-defined Still a very appealing engineering concept (c.f. electronic components)?? Many similarities to objects: Data encapsulation Functional encapsulation Access via interfaces only However, the essence of components usually means Context-independence resuability mass production The blocks look like components in an electronic circuit diagram 3 4 3 4
A source of confusion More confusion Objects are unique run-time instances, they can't be reused A component must mean a class instead How is concurrent execution related to components? Is a component equivalent to a thread? What is the functional interface to a component? A set of procedures? What about synchronization? However, classes usually refer to other classes and definitions in scope, can't be used outside their context Maybe a component should mean a module What about underlying OS dependencies? Can components be distributed? (Well, they ought to) Then what is the protocol? Remote-procedure-calls? But modules usually contain global state variables should these be duplicated or not when components are mass-produced? So a module is just like a fancy object? How are component interfaces specified? What kind of types are allowed? Behavior specifications? Logic? Should timing properties be part of an interface? 5 6 5 6 Enter Timber (timber-lang.org) Timber and components A high-level programming language, built around the same programming model as TinyTimber: A split view: When we engineer systems we think of components as objects unique instances each with a private state What we really mean, though, are classes and not objects, and that's what we write as program code However, when it comes to reusability and massproduction, a Timber component is a module: Event-triggered execution = the classic object-oriented paradigm revisited In addition: Dynamic object creation Garbage-collecting memory management Strong static type system including subtyping, parametric polymorphism, dynamic overloading, Rich assortment of immutable & higher-order data Definitions of classes, functions and other constants Definitions of types But no mutable variables modules can be freely duplicated, shared, reused, cached, packaged as binaries 7 8 7 8
No declared global state Timber code vs. Timber at run-time State variables only exist within objects And only classes (i.e., object generators) written in code Classes may instantiate other classes, but only when they become instantiated themselves module A where module E where map f [] = [] map f (x:xs) = f x : map f xs myapp arg = class id x = x module F module B module D module G where counter = class val := 0 inc = action val := val + 1 read = request result val module C Immutable reusable Timber modules module K where anotherapp env = class result Counter {..} A: In the run-time system, on basis of a class name given as an argument to the compiler (default: root) Mutable unique Timber "worlds" root = E.myApp C.f. "Where does the function call-chain start in C? In the run-time system, based on a name convention" root = E.myApp 9 root = K.anotherApp 10 9 10 Run-time execution model Local state Method 2 Methods In parallel Object A Object B Method 1 External events module J module H where aaa = 123 Q: So how does this process start? Where is the root of the whole program instantiated? Mutually exclusive module I Local state Local state Method Asynchronous Messages Synch ronou s Asynchronous Method 3 Method 4 External reactions Method Finite sequences that Read and write local state Call other methods Create new objects Perform pure computations No indefinitely blocking operations, no infinite loops: objects sleep between temporary activity The classical OO intuition recast to a concurrent setting delayed 11 12 11 12
A simple event counter A simple event counter struct Counter where interface spec inc :: Action read :: Request Int counter :: Class Counter an object generator counter = class val := 0 asynchronous method local state inc = action val := val + 1 synchronous method read = request result val result Counter { inc = inc, read = read } automatically inferred type val :: Int inc read struct stuffing 13 14 13 14 Using the simple event counter Variant: a buffer Somewhere in the code: c = new counter c.inc c.inc v <- c.read if v == 2 then A type parameter object creation val :: [a] asynchronous method call insert synchronous method call, binds result to v 15 read struct Buffer a where insert :: a -> Action read :: Request [a] Method taking an argument List type List buffer = class construction val := [] insert x = action val := x : val read = request result val result Buffer {..} 16 15 16
An extended buffer class Alternative: a flush buffer Declared subtyping (type extension) val :: [a] struct CBuffer a < Buffer a where clear :: Action val :: [a] buffer2 = class val := [] insert x = action val := x : val read = request result val clear = action val := [] result CBuffer {..} insert struct FlushBuf a where insert :: a -> Action flush :: Action read clear insert 17 flush fbuf :: ([a]->action) -> Class (FlushBuf a) fbuf consumer = class val := [] insert x = action? val := x : val flush = action consumer val val := [] result FlushBuf {..} A required interface 18 17 18 In general method1 In general method1 methodn Provided interface Often a struct methodn Provided interfaces Required interfaces Can also be (a tuple of) individual methods Or any data structure containing methods Required interface Often a struct 19 20 19 20
On data structures Timber & Haskell As is syntactically obvious, Timber is a descendant of Haskell (www.haskell.org) All data are not objects In fact, Timber offers a wealth of immutable data: Lists User-defined structs User-defined unions (tagged alternatives) Tuples of any width Arrays (become mutable when part of object state) Functions (first-class citizens) First-class methods, classes and commands Shares purely functional basis, strong typing principle, overloading system, many syntactic details Differences in Timber: Objects, methods & classes not part of Haskell (which has a more traditional imperative top-level) Event-triggered concurrency & timing Standard strict expression evaluation (as opposed to Haskell's lazy semantics) Subtyping & user-defined structs Objects only for capturing the state of a system 21 22 21 22 Lists On syntax Basic constructors: [] (the empty list) x:xs (x put in front of list xs) Function calls don't need parentheses: length xs (call length with argument xs) sin (alpha+1.0) (call sin with argument alpha+1.0) min a b (call min with arguments a and b) Syntactic shorthand: [a,b,c,d] equivalent to a:b:c:d:[] Operators are just infix functions with symbolic names: a+b (call + with arguments a and b) (a+b) * sin y (call * with arguments a+b and sin y) Analysis via pattern-matching & recursion: length [] =0 length (x:xs) = 1 + length xs Odinary calls always bind harder than operators Concatenation (a standard operator): [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) Operators have their "usual" precedence Parentheses override any precedence 23 24 23 24
User-defined structs Introduced by declaration: struct Complex where re :: Float im :: Float User-defined unions Introduced by declaration: data Color = Red Blue Green Also declares selectors re and im Construction (just say Blue, for example) and analysis: case x of Red -> 0 Blue -> 1 Green -> 2 Makes Complex distinct from any other type Constructing struct terms: Complex { re = 2.1, im = 5.0 } { re = 2.1, im = 5.0 } Full form Or, by means of a defined function: ff Red = 0 ff Blue = 1 ff Green = 2 When selectors identify the type Accessing fields: x.re * x.re + x.im * x.im 25 26 25 26 User-defined unions Higher-order functions Constructors with arguments: data IntOrBool = Itag Int Btag Bool Mapping a function onto a list of arguments: map f [] = [] map f (x:xs) = f x : map f xs Term construction is just application: Itag 87 or Itag 0 or Btag False Example of use: successor n =n+1 ys = map successor [1,2,3] Analysis by pattern-matching: explain (Itag 0) = "zero" explain (Itag _) = "non-zero" explain (Btag True) = "true" explain (Btag False) = "false" Alternatively, using an anonymous function: ys = map (\n -> n+1) [1,2,3] 27 28 27 28
Overloading Polymorphic overloading Declaring overloaded equality operators: typeclass Eq a where (==),(/=) :: a -> a -> Bool Open use of overloaded operators: elem x [] = False elem x (y:ys) = x == y elem x ys Making them defined for types Int and Char: instance eqint :: Eq Int where (==) = priminteq (/=) = primintne What would be a proper type for elem? elem :: Int -> [Int] -> Bool Too specific elem :: Char -> [Char] -> Bool Too specific elem :: a -> [a] -> Bool Too general Solution borrowed from Haskell: add a class constraint elem :: a -> [a] -> Bool \\ Eq a instance eqchar :: Eq Char where a == b = ord a == ord b a /= b = ord a /= ord b (Note deviation from Haskell syntax, though) 29 30 29 30 Pure computations Compare with Commands f1 x = g x + g x f2 x = let y = g x in y + y Timber has four forms of bindings/assignments: x = exp x is defined equal to the value of exp In Timber, f1 and f2 are provably identical, for all g x = new exp In C, they would be different if g contains effects: int g (int v) { printf("hello"); return v*v; } x is defined equal to the result of instantiating class expression exp x <- exp x is defined equal to the result of executing method expression exp Timber distinguishes between effects and pure computations by means of types: x := exp x is modified to become equal to exp (x must be a state variable in scope) Int -> Int Int -> Request Int Pure functions from Int to Int Methods from Int to Int Only the first two forms may be recursive Form x <- exp may not occur in class bodies 31 32 31 32
Examples Easy mistakes Simple value bindings: incby4 = \v -> v+4 seven = incby4 3 Unintended recursive binding (rejected): meth x = action x = incby4 x (Solution: call local variable something else than x) Syntactic short-hand for function values: incby4 v = v+4 Confusing a method with its result: x = obj.meth arg y=x+x (Solution: write x <- obj.meth arg to capture result) Non-recursive mutation of state variable s: s := incby4 s Mutually recursive object instances: a = new classa b b = new classb a Use of instantiation as an expression: x = f (new myclass) (Solution: write y = new myclass as a separate binding) 33 34 33 34 A sonar example A sonar example sonar port alarm critical = class tm = new timer A built-in class for measuring A local object count := 0 time ping = action port.write beepon tm.reset after (millisec 2) stop after (sec 3) ping stop = action port.write beepoff Returns time since last reset echo = action diff <- tm.sample if critical diff then count := count + 1 A function parameter alarm count result { interrupt = echo, start = ping } alarm reset stop sample echo interrupt port ping start 35 36 35 36
The sonar program root The sonar program root The required interface (Here: an array of ports) root regs = class crit d = d < millisec 15 a = new alarm (regs comportaddr) s = new sonar (regs sonarportaddr) a crit result [ s.start, s.interrupt, a.ack ] regs sonar interrupts alarm A boolean function passed as a parameter The provided interface (Here: a list of interrupt handlers) 37 38 37 The root under POSIX The required interface (Here: a struct of OS services) root env = class a = new b result action env.stdin.installr a.keyhandler The provided interface (Here: just a startup method main) 39 39 38