Chapter 7:: Data Types 1 2 Data Types • most programming languages include a notion of types for expressions, variables, and values • two main purposes of types – imply a context for operations Programming Language Pragmatics • programmer does not have to state explicitly Michael L. Scott • example: a + b means mathematical addition if a and b are integers – limit the set of operations that can be performed • prevents the programmer from making mistakes • type checking cannot prevent all meaningless operations • it catches enough of them to be useful

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

3 4 Type Systems Type Systems • bits in memory can be interpreted in different • a type system consists of ways – a way to define types and associate them with language – instructions constructs • constructs that must have values include constants, variables, – addresses record fields, parameters, literal constants, subroutines, and – characters complex expressions containing these – integers, floats, etc. – rules for type equivalence, type compatibility, and type inference • bits themselves are untyped, but high-level • type equivalence: when the types of two values are the same languages associate types with values • type compatibility: when a value of a given type can be used in a – useful for operator context particular context • type inference: type of an expression based on the types of its parts – allows error checking

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

5 6 Type Checking Type Checking • type checking ensures a program obeys the • examples language’s type compatibility rules – Common Lisp is strongly typed, but not – type clash: violation of type rules statically typed • a language is strongly typed if it prohibits an – Ada is statically typed operation from performing on an object it does – Pascal is almost statically typed not support – Java is strongly typed, with a non-trivial • a language is statically typed if it is strongly mix of things that can be checked typed and type checking can be performed at statically and things that have to be compile time checked dynamically – few languages fall completely in this category – questions about its value Copyright © 2005 Elsevier Copyright © 2005 Elsevier

1 7 8 Type Checking Polymorphism • a language is dynamically typed if type • polymorphism results when the compiler checking is performed at run time finds that it doesn't need to know certain – late binding things – List and Smalltalk – allows a single body of code to work with objects of – scripting languages, such as Python and Ruby multiple types – with dynamic typing, arbitrary operations can be applied to arbitrary objects • implicit parametric polymorphism: types can be thought of to be implied unspecified parameters • incurs significant run-time costs • for example, ML’s type inference/unification scheme

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

9 10 Polymorphism Data Types • subtype polymorphism in object-oriented • we all have developed an intuitive notion of languages what types are; what's behind the intuition? – collection of values from a domain (the – a variable of the base type can refer to an object of the denotational approach) derived type – internal structure of data, described down to the – explicit parametric polymorphism, or generics level of a small set of fundamental types (the • ++, Eiffel, Java structural approach) • useful for container classes – collection of well-defined operations that can be List where T is left unspecified applied to objects of that type (the abstraction approach)

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

11 12 Data Types Classification of Types • specified in different ways • most languages provide a set of built-in – Fortran: spelling of variable names types – Lisp: infer types at compile time – integer – List, Smalltalk: track at run time – boolean – C, C++: declared at compile time • often single character with 1 as true and 0 as false • C: no explicit boolean; 0 is false, anything else is true – char • traditionally one byte • ASCII, Unicode – real

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

2 13 14 Classification of Types Classification of Types • numeric types • enumeration types – C and Fortran distinguish between different – facilitate readability lengths of integers – allow compiler to catch errors – C, C++, C#, Modula-2: signed and unsigned • Pascal example integers

– differences in real precision cause unwanted behavior across machine platforms – orders, so comparisons OK

– some languages provide complex, rational, or

decimal types

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

15 16 Classification of Types Classification of Types • alternatively, can be declared as constants • subrange types – contiguous subset of discrete base type – same as C

– helps to document code – most store the actual values rather than ordinal locations

• needs two bytes

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

17 18 Classification of Types Classification of Types • composite types • composite types (cont.) – records – sets • Cobol • discrete • fields of possibly different type • like enumerations and subranges • similar to tuples – pointers – variant records (unions) • good for recursive data types • multiple field types, but only one valid at a given – lists time • head element and following elements • variable length, no indexing – arrays – files • aggregate of same type • mass storage, outside program memory • strings • linked to physical devices (e.g., sequential access) Copyright © 2005 Elsevier Copyright © 2005 Elsevier

3 19 20 Orthogonality Orthogonality • orthogonality is a useful goal in the design • for example of a language, particularly its type system – Pascal is more orthogonal than Fortran, – a collection of features is orthogonal if there are (because it allows arrays of anything, for no restrictions on the ways in which the instance), but it does not permit variant records features can be combined (analogy as arbitrary fields of other records (for instance) to vectors) • orthogonality is nice primarily because it makes a language easy to understand, easy to use, and easy to reason about

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

21 22 Type Checking Type Checking • a type system has rules for • type compatibility / type equivalence – type equivalence (when are the types of two – compatibility is the more useful concept, values the same?) because it tells you what you can do – type compatibility (when can a value of type A – terms are often used interchangeably (incorrectly) be used in a context that expects type B?) – type inference (what is the type of an expression, given the types of the operands?)

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

23 24 Type Checking Type Checking • format does not matter • two major approaches: structural struct { int a, b; } is the same as equivalence and name equivalence struct { – name equivalence is based on declarations int a, b; – structural equivalence is based on some notion } of meaning behind those declarations • want them to be the same as – name equivalence is more fashionable these struct { days int a; int b;

Copyright © 2005 Elsevier} Copyright © 2005 Elsevier

4 25 26 Type Checking Type Checking • at least two common variants on name • structural equivalence depends on simple equivalence comparison of type descriptions – differences between all these approaches boils down to where the line is drawn between – substitute out all names important and unimportant differences between – expand all the way to built-in types type descriptions • original types are equivalent if the expanded – in all three schemes, we begin by putting every type description in a standard form that takes type descriptions are the same care of "obviously unimportant" distinctions like those above

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

27 28 Type Checking Type Checking • example in Ada illustrating type conversion • coercion – when an expression of one type is used in a (1..100) (integer) context where a different type is expected, one normally gets a type error – but what about var a : integer; b, c : real; ... c := a + b;

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

29 30 Type Checking Type Checking • coercion • C has lots of coercion, too, but with simpler – many languages allow such statements, and rules: coerce an expression to be of the proper type – all floats in expressions become doubles – coercion can be based just on types of – short int and char become int in operands, or can take into account expected expressions type from surrounding context as well – if necessary, precision is removed when – Fortran has lots of coercion, all based on assigning into LHS operand type

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

5 31 32 Type Checking Type Checking • example in C, which does a bit of coercion • in effect, coercion rules are a relaxation of type checking – recent thought is that this is probably a bad idea – languages such as Modula-2 and Ada do not permit coercions – C++, however, goes hog-wild with them – they're one of the hardest parts of the language to understand

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

33 34 Type Checking Type Checking • make sure you understand the difference • universal reference types between – a reference that can point to anything – type conversions (explicit) • C, C+: void • Clu: any – type coercions (implicit) • Modula2: address – sometimes the word 'cast' is used for • Java: Object conversions (e.g., C) • C#: object

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

Records (Structures) and Records (Structures) and 35 36 Variants (Unions) Variants (Unions)

• records • C – allow related heterogeneous types to be stored together

– C, C++, and Common Lisp: structure • C++: class with globally visible members • Pascal – Fortran: types

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

6 Records (Structures) and Records (Structures) and 37 38 Variants (Unions) Variants (Unions)

• each record component is known as a field • most languages allow records to be nested – most languages use dot notation to refer to fields

– Fortran: copper%name

• alternatively

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

Records (Structures) and Records (Structures) and 39 40 Variants (Unions) Variants (Unions)

• both reference • records – malachite.element_yielded.atomic_number – usually laid out contiguously • in C, struct tags appear in struct definitions as • addresses are computed as base + offset – possible holes for alignment reasons

– smart compilers may re-arrange fields to minimize holes (C compilers promise not to) – in C, need typedef – implementation problems are caused by records • typedef struct … containing dynamic arrays – in C++, struct tag is type name

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

Records (Structures) and Records (Structures) and 41 42 Variants (Unions) Variants (Unions)

• records • records – example – some languages, such as Pascal, allow the data to – holes for alignment be packed • compiler should optimize for space over speed

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

7 Records (Structures) and Records (Structures) and 43 44 Variants (Unions) Variants (Unions)

• records • records – packed layout – some languages, such as Ada, allow record assignment • my_element := copper

– or record tests for equality

• if my_element = copper then – most other languages do not – less space, but system takes longer to access elements

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

Records (Structures) and Records (Structures) and 45 46 Variants (Unions) Variants (Unions)

• records • records – record equality can be done field by field – holes in records waste space – to save time, a block compare function can be • packing helps, but with performance penalties used • smart compilers rearrange fields automatically (not C compilers) • programmer unaware • compares bytes • some systems-level applications may depend on non-rearranged • problems? fields – holes need to be filled with 0’s to allow block comparisons to work correctly

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

Records (Structures) and Records (Structures) and 47 48 Variants (Unions) Variants (Unions) • unions (variant records) • Pascal – space was extremely limited in early days of programming – overlay space – cause problems for type checking • can be used for automatically re-interpreting bits without casts

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

8 Records (Structures) and Records (Structures) and 49 50 Variants (Unions) Variants (Unions)

• variant part must be at end • Modula-2: variant part not required to be at end

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

51 52 Arrays Arrays

• arrays are the most common and important • index composite data types – most languages require the index to be an integer • unlike records, which group related fields of – or at least discrete type – non-discrete type disparate types, arrays are usually homogeneous • associative array • semantically, they can be thought of as a • Python: dictionary mapping from an index type to a component or • require hash table for lookup element type

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

53 54 Arrays Arrays

• syntax and operation • declaration – index usually inside square brackets [ ] – C – some languages use parentheses ( ) – Fortran

– Pascal

– Ada

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

9 55 56 Arrays Arrays

• multidimensional arrays • a slice or section is a rectangular portion of an array

– can be accessed as matrix [3][4] – sometimes as matrix [3, 4] – Ada: matrix (3, 4)

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

57 58 Arrays Arrays

• dimensions, bounds, and allocation • slicing – global lifetime, static shape — if the shape of an array is – some languages allow operations on arrays known at compile time, and if the array can exist • Ada: lexicographic ordering: if A < B throughout the execution of the program, then the compiler • Fortran: more than 60 intrinsic functions can allocate space for the array in static global memory – arithmetic operations, logic functions, bit manipulation, trigonometric functions – local lifetime, static shape — if the shape of the array is known at compile time, but the array should not exist throughout the execution of the program, then space can be

allocated in the subroutine’s stack frame at run time.

– local lifetime, shape bound at elaboration time

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

59 60 Arrays Arrays

• dope vector • stack allocation – collection of information about an array – conformant arrays • dimensions • parameters to subroutines • bounds (lower and upper) • dimensions not known – record • dope vector passed to help • offset to each field – good for runtime checks – can enhance efficiency by avoiding computations – origin: “having the dope on (something)”

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

10 61 62 Arrays Arrays

• heap allocation – dynamic arrays – dimensions not known until runtime – dope vector can be maintained dynamically

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

63 64 Arrays Arrays

• arrays are stored in contiguous memory locations • for multidimensional arrays, an ordering of dimensions must be specified – column-major - only in Fortran – row-major • used by everybody else – important for element access

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

65 66 Arrays Arrays

• to access elements in a single row • two layout strategies for arrays – column-major – contiguous elements – row pointers • cache may not be big enough to store next row item • row pointers – row-major – an option in C • effective – allows rows to be put anywhere - nice for big arrays on – reversed for column support, but row support machines with segmentation problems seems to be used more often – avoids multiplication – nice for matrices whose rows are of different lengths • e.g. an array of strings – requires extra space for the pointers Copyright © 2005 Elsevier Copyright © 2005 Elsevier

11 67 68 Arrays Arrays

• address calculation – can compute some portions before execution – declaration

– constants

– address

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

69 70 Arrays Arrays

• we can compute the address with fewer operations

– the calculations in square brackets is compile-time constant that depends only on the type of A

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

71 72 Strings Sets • strings are really just arrays of characters • possible implementations • they are often special-cased, to give them – bitsets are what usually get built into programming flexibility (like polymorphism or dynamic languages sizing) that is not available for arrays in – intersection, union, membership, etc. can be general implemented efficiently with bitwise logical instructions – easier to provide these operations for strings – some languages place limits on the sizes of sets to than for arrays in general because strings are make it easier for the implementer one-dimensional and (more importantly) non- circular • for 32-bit int’s, bitset size is 500MB • for 64-bit int’s, bitset size enormous Copyright © 2005 Elsevier Copyright © 2005 Elsevier

12 73 74 Pointers and Recursive Types Pointers and Recursive Types • pointers serve two purposes • operations on pointers – efficient (and sometimes intuitive) access to – allocation and deallocation of objects in the heap elaborated objects (as in C) – dereferencing of pointers to access the objects to – dynamic creation of linked data structures which they point (recursive types), in conjunction with a heap – assignment from one pointer to another storage manager • operations behave differently depending on • several languages (e.g., Pascal) restrict whether the language employs a reference pointers to accessing objects in the heap model or value model for names – functional languages use reference model

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

75 76 Pointers and Recursive Types Pointers and Recursive Types • reference model – ML example

– example tree

– block storage from heap

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

77 78 Pointers and Recursive Types Pointers and Recursive Types • reference model – Lisp example

– each level of parentheses brackets the elements of a list • head and remainder of list • each node is denoted as a construct cell or atom

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

13 79 80 Pointers and Recursive Types Pointers and Recursive Types • references in functional languages are acyclic • mutually recursive types – circular references typically found only in – ML example: symbol table and syntax tree nodes imperative languages declared together • often need mutually recursive types – example: symbol table and syntax tree nodes need to refer to each other – if declared one at a time, not able to refer to each other

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

81 82 Pointers and Recursive Types Pointers and Recursive Types • tree types in value model languages • tree types in value model languages (cont.) – Pascal – C

• for mutually recursive types – Ada – Pascal: forward references – Ada and C: incomplete data types

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

83 84 Pointers and Recursive Types Pointers and Recursive Types • allocation of space from the heap – Pascal

– Ada

– C

• returns void* – C++, Java, C#

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

14 85 86 Pointers and Recursive Types Pointers and Recursive Types • accessing objects pointed to by references • most languages blur the distinction between – Pascal l-values and r-values by implicitly dereferencing variables on right-hand side of – C assignment

• or for structs

– Ada • depends on context

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

87 88 Pointers and Recursive Types Pointers and Recursive Types • C pointers and arrays • subscript operator [ ]

– equivalent to

– all of the following are valid

– order does not matter

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

89 90 Pointers and Recursive Types Pointers and Recursive Types • C pointers and arrays closely linked • declaring an array requires all dimensions int *a == int a[] except the last int **a == int *a[] – invalid • some subtle differences int a[][]; – a declaration allocates an array if it specifies a size int (*a)[]; in the first dimension – C declaration rule: read right as far as you can – otherwise it allocates a pointer (subject to parentheses), then left, then out a level int **a, int *a[]; // pointer to pointer to int and repeat int *a[n]; // n-element array of row pointers int *a[n]; // n-element array of pointers to int a[n][m]; // 2-d array // integer int (*a)[n]; // pointer to n-element array Copyright © 2005 Elsevier Copyright © 2005 Elsevier // of integers

15 91 92 Pointers and Recursive Types Pointers and Recursive Types

• when a heap-allocated object is no longer • some languages require heap space to be live, space must be reclaimed reclaimed by the programmer – stack objects reclaimed automatically – Pascal – heap space can be reclaimed automatically with garbage collection – C

– C++

– if not reclaimed, results in memory leaks Copyright © 2005 Elsevier Copyright © 2005 Elsevier

93 94 Pointers and Recursive Types Pointers and Recursive Types

• dangling reference • dangling reference examples – live pointer that no longer points to a valid – subroutine returns pointer to local variable object – active pointer to space that has been reclaimed – only in languages that have explicit • even if the pointer were changed to NULL, other pointers deallocation might still refer to this space • changes may read or write bits that are parts of other objects

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

95 96 Pointers and Recursive Types Pointers and Recursive Types • explicit reclaiming can be a burden on • several types of garbage collection programmers and a source of memory leaks – reference counts • alternative: garbage collection – mark-and-sweep – automatic reclaiming of heap space – pointer reversal – required for functional languages – stop-and-copy – found in Java, Modula-3, C# – others – difficult to implement – convenient for programmers: no dangling pointers – can be slow

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

16 97 98 Pointers and Recursive Types Pointers and Recursive Types • reference counts • reference counts (cont.) – one way to designate an object as garbage is when – reference count also decremented upon subroutine no pointers to it exist return if local pointers involved – place a reference count in each object that keeps – system must run recursively, so that any pointers track of the number of pointers to it in reclaimed space are accounted for – when object is created, reference count is set to 1 – pointers must otherwise be initialized to null – when pointers are assigned or re-assigned, count – works well with strings, which have no circular is incremented/decremented dependencies – when count reaches 0, space can be reclaimed

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

99 100 Pointers and Recursive Types Pointers and Recursive Types • the heap is a chain of nodes (the free_list) • reference counts may miss circular structures • each node has a reference count (RC) • for an assignment, like q = p, garbage can occur

Source: Tucker & Noonan (2007) Copyright © 2005 Elsevier

101 102 Pointers and Recursive Types Pointers and Recursive Types • disadvantages • some garbage collection schemes use tracing - failure to detect inaccessible circular chains - storage overhead created by appending an integer reference – instead of counting the number or references to an count to every node in the heap object - performance overhead whenever a pointer is assigned or a heap block is allocated or deallocated – find all good space by following valid pointers that are active – work recursively through the heap, starting from external pointers

Source: Tucker & Noonan (2007) Copyright © 2005 Elsevier

17 103 104 Pointers and Recursive Types Pointers and Recursive Types

• mark-and-sweep • triggered by q=new node() and free_list = null. – 3 main steps executed when space is low • all accessible nodes are marked 1 1. all blocks in the heap marked with 0 2. follows pointers outside the heap and recursively explores all heap space; each newly discovered block marked with 1 • if already marked with 1, can stop following that chain 3. final walk through heap to remove every block still marked with 0

Copyright © 2005 Elsevier Source: Tucker & Noonan (2007)

105 106 Pointers and Recursive Types Pointers and Recursive Types • now free_list is restored and • advantages over reference counting • the assignment q=new node() can proceed – reclaims all garbage in the heap – only invoked when the heap is full • issues with mark-and-sweep – system must know beginning and ending points of blocks • each block must contain a size – collector must be able to find pointers contained in each block – recursive nature requires stack space Source: Tucker & Noonan (2007) Copyright © 2005– Elseviercan be time-intensive

107 108 Pointers and Recursive Types Pointers and Recursive Types • pointer reversal • heap exploration using pointer reversal – to avoid large stack during recursive search, use pointers already in heap space to work back up the chain – such pointers oriented in the wrong direction – can be reversed to point backward in the “stack” – collector keeps track of current block and the block from whence it came

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

18 109 110 Pointers and Recursive Types Pointers and Recursive Types • stop-and-copy (aka copy collection) • triggered by q=new node() and free_list outside the – heap space divided into two halves active half: • only one is active at any given time – when one half nearly full, collector recursively explores heap, copying all valid blocks to other half – defragments while copying – pointers to blocks changed during copy – when finished, only useful blocks in heap; other half is no longer used – on next collection, halves are reversed

Copyright © 2005 Elsevier Source: Tucker & Noonan (2007)

111 112 Pointers and Recursive Types Pointers and Recursive Types • accessible nodes copied to other half • issues with stop-and-copy – the accessible nodes are packed, orphans are returned to – only half of the heap can be used at any one time the free_list, and the two halves reverse roles – incurs additional overhead – can be time-intensive

Source: Tucker & Noonan (2007) Copyright © 2005 Elsevier

113 114 Pointers and Recursive Types Pointers and Recursive Types • other techniques • summary – generational collection • modern algorithms are more elaborate • since most dynamically allocated blocks are short-lived, newer ones • most are hybrids/refinements of the above three stored in half the heap • in Java, garbage collection is built-in • this half is checked first if garbage collection necessary • runs as a low-priority thread • during garbage collection, valid blocks transferred to long-term heap • also, System.gc may be called by the program • system must examine long-term heap if more space needed, and to • functional languages have garbage collection built-in reduce memory leaks • C/C++ default garbage collection to the programmer – conservative collection • if blocks in the heap do not state explicitly the location of pointers, any space that “looks like” a pointer can be considered such and used with mark-and-sweep • may leave some useless blocks, but guaranteed not to delete currently used blocks Copyright © 2005 Elsevier Source: Tucker & Noonan (2007)

19 115 116 Lists Lists • a list is defined recursively as either the • list operations tend to generate garbage empty list or a pair consisting of an object – useful to have automatic garbage collection (which may be either a list or an atom) and • some languages (e.g., ML) require lists to another (shorter) list contain items of the same type – ideally suited to programming in functional and • other languages (e.g., Python, Lisp) allow logic languages, which rely on recursion lists to contain items of differing types • Lisp: a program is a list, and can extend itself at run time by constructing a list and executing it – implemented as chain of two pointers: one to element and one to next block – lists can also be used in imperative programs

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

117 118 Lists Lists • since everything in Lisp is a list, • list comprehension (e.g., Haskell, Python) differentiation required between lists to be – Python example: all odd numbers less than 100 evaluated and constant lists [i*i for i in range (1, 100) if i % 2 == 1] • Lisp list constructors

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

119 120 Files and Input/Output Equality Testing and Assignment • input/output (I/O) facilities allow a program to • when an expression is encountered testing for communicate with the outside world equality, how should it be interpreted? – interactive I/O generally implies communication with human – for primitive types, easily defined users or physical devices – files generally refer to off-line storage implemented by the – example: s = t for character strings operating system • are s and t aliases? • occupy the same storage that is bit-wise identical over its full • files may be further categorized into length? – temporary • contain the same sequence of characters? – persistent • would appear the same if printed? – shallow vs. deep comparison

Copyright © 2005 Elsevier Copyright © 2005 Elsevier

20 121 Equality Testing and Assignment • Scheme has distinct equality-testing functions for each

– behavior

• deep assignments rare in any language

Copyright © 2005 Elsevier

21