Example: Haskell algebraic data types (1) Type declaration: data Number = Exact Int Inexact Float Set of values: Each Number value consists of a tag, together with either an Integer variant (if the tag is Exact) or a Float variant (if the tag is Inexact). Number = Exact Integer + Inexact Float viz: Cardinality: Exact( 2) Exact( 1) Exact 0 Exact 1 Exact 2 Inexact( 1.0) Inexact 0.0 Inexact 1.0 #Number = #Integer + #Float 2-1
Example: Haskell algebraic data types (2) Application code: pi = Inexact 3.1416 rounded :: Number -> Integer rounded num = case num of projection Exact i -> i (by pattern Inexact r -> round r matching) disjoint-union construction tag test 2-2
Example: Ada discriminated records (1) Type declarations: type Accuracy is (exact, inexact); type Number (acc: Accuracy := exact) is record case acc of when exact => ival: Integer; when inexact => rval: Float; end case; end record; Each Number value consists of a tag field named acc, together with either an Integer variant field named ival (if the tag is exact) or a Float variant field named rval (if the tag is inexact). 2-3
Example: Ada discriminated records (2) Set of values: Number = exact Integer + inexact Float viz: Cardinality: exact( 2) exact( 1) exact 0 exact 1 exact 2 inexact( 1.0) inexact 0.0 inexact 1.0 #Number = #Integer + #Float 2-4
Example: Ada discriminated records (3) Type declarations: type Form is (pointy, circular, rectangular); type Figure (f: Form := pointy) is record x, y: Float; case f is when pointy => null; when circular => r: Float; when rectangular => w, h: Float; end case; end record; Each Figure value consists of a tag field named f, together with a pair of Float fields named x and y, together with either an empty variant or a Float variant field named r or a pair of Float variant fields named w and h. 2-5
Example: Ada discriminated records (4) Set of values: Figure = pointy(float Float) + circular(float Float Float) + rectangular(float Float Float Float) e.g.: pointy(1.0, 2.0) circular(0.0, 0.0, 5.0) rectangular(1.5, 2.0, 3.0, 4.0) represents the point (1, 2) represents a circle of radius 5 centered at (0, 0) represents a 3 4 rectangle centered at (1.5, 2) 2-6
Example: Ada discriminated records (5) Application code: discriminated-record box: Figure := construction (rectangular, 1.5, 2.0, 3.0, 4.0); function area (fig: Figure) return Float is begin case fig.f is when pointy => return 0.0; tag test when circular => return 3.1416 * fig.r**2; when rectangular => return fig.w * fig.h; end case; end; projection 2-7
Example: Java objects (1) Type declarations: class Point { private float x, y; // methods } class Circle extends Point { private float r; // methods } class Rectangle extends Point { private float w, h; // methods } inherits x and y from Point inherits x and y from Point 2-8
Example: Java objects (2) Set of objects in this program: Point(Float Float) + Circle(Float Float Float) + Rectangle(Float Float Float Float) + The set of objects is open-ended. It is augmented by any further class declarations. 2-9
Example: Java objects (3) Methods: class Point { public float area() { return 0.0; } } class Circle extends Point { public float area() { return 3.1416 * r * r; } } class Rectangle extends Point { public float area() { return w * h; } } overrides Point s area() method overrides Point s area() method 2-10
Example: Java objects (4) Application code: Rectangle box = new Rectangle(1.5, 2.0, 3.0, 4.0); float a1 = box.area(); Point it = ; float a2 = it.area(); it can refer to a Point, Circle, or Rectangle object calls the appropriate area() method 2-11
Recursive types A recursive type is one defined in terms of itself. Examples of recursive types: lists trees 2-12
Lists (1) A list is a sequence of 0 or more component values. The length of a list is its number of components. The empty list has no components. A non-empty list consists of a head (its first component) and a tail (all but its first component). A list is homogeneous if all its components are of the same type. Otherwise it is heterogeneous. 2-13
Lists (2) Typical list operations: length emptiness test head selection tail selection concatenation. 2-14
Lists (3) For example, an integer-list may be defined recursively to be either empty or a pair consisting of an integer (its head) and a further integer-list (its tail): Integer-List = nil Unit + cons(integer Integer-List) or Integer-List = { nil } { cons(i, l) i Integer; l Integer-List } where Unit is a type with only one (empty) value. Solution: Integer-List = { nil } { cons(i, nil) i Integer } { cons(i, cons(j, nil)) i, j Integer } { cons(i, cons(j, cons(k, nil))) i, j, k Integer } 2-15
Example: Haskell lists Type declaration for integer-lists: data IntList = Nil Cons Int IntList Some IntList constructions: Nil Cons 2 (Cons 3 (Cons 5 (Cons 7 Nil))) Actually, Haskell has built-in list types: [Int] [String] [[Int]] recursive Some list constructions: [] [2,3,5,7] ["cat","dog"] [[1],[2,3]] 2-16
Example: Ada lists Type declarations for integer-lists: type IntNode; type IntList is access IntNode; type IntNode is record head: Integer; tail: IntList; end record; An IntList construction: new IntNode'(2, new IntNode'(3, new IntNode'(5, new IntNode'(7, null))) mutually recursive 2-17
Example: Java lists (1) Class declarations for integer-lists: class IntList { public int head; public IntList tail; recursive public IntList (int h, IntList t) { head = h; tail = t; } } An integer-list construction: new IntList(2, new IntList(3, new IntList(5, new IntList(7, null))))); 2-18
Example: Java lists (2) Class declarations for object-lists: class List { } public Object head; public List tail; public List (Object h, IntList t) { head = h; tail = t; } Note that List objects are heterogeneous lists (since head can refer to an object of any class). By contrast, IntList objects are homogeneous lists. 2-19
Strings A string is a sequence of 0 or more characters. Some PLs (ML, Python) treat strings as primitive. Haskell treats strings as lists of characters. Strings are thus equipped with general list operations (length, head selection, tail selection, concatenation, ). Ada treats strings as arrays of characters. Strings are thus equipped with general array operations (length, indexing, slicing, concatenation, ). Java treats strings as objects, of class String. 2-20
Type systems A PL s type system groups values into types: to enable programmers to describe data effectively to help prevent type errors. A type error occurs if a program performs a nonsensical operation such as multiplying a string by a boolean. Possession of a type system distinguishes high-level PLs from low-level languages (such as assembly languages). In the latter, the only types are bytes and words, so nonsensical operations cannot be prevented. 2-21
Static vs dynamic typing (1) Before any operation is performed, its operands must be type-checked to prevent a type error. E.g.: mod operation: check that both operands are integers and operation: check that both operands are booleans indexing operation: check that the left operand is an array, and that the right operand is a value of the array s index type. 2-22
Static vs dynamic typing (2) In a statically typed PL: all variables and expressions have fixed types (either stated by the programmer or inferred by the compiler) all operands are type-checked at compile-time. Most PLs are statically typed, including Ada, C, C++, Java, Haskell. 2-23
Static vs dynamic typing (3) In a dynamically typed PL: values have fixed types, but variables and expressions do not operands must be type-checked when they are computed at runtime. Some PLs and many scripting languages are dynamically typed, including Smalltalk, Lisp, Prolog, Perl, Python. 2-24
Example: Ada static typing Ada function definition: Call: function is_even (n: Integer) return Boolean is begin return (n mod 2 = 0); end; p: Integer; if is_even(p+1) The compiler doesn t know the value of n. But, knowing that n s type is Integer, it infers that the type of n mod 2 = 0 will be Boolean. The compiler doesn t know the value of p. But, knowing that p s type is Integer, it infers that the type of p+1 will be Integer. Even without knowing the values of variables and parameters, the Ada compiler can guarantee that no type errors will happen at run-time. 2-25
Example: Python dynamic typing (1) Python function definition: def even (n): return (n % 2 == 0) The type of n is unknown. So the % (mod) operation must be protected by a runtime type check. The types of variables and parameters are not declared, and cannot be inferred by the Python compiler. So run-time type checks are needed to detect type errors. 2-26
Example: Python dynamic typing (2) Python function definition: def respond (prompt): # Print prompt and return the user s response, # as an integer if possible, otherwise as a string. try: response = raw_input(prompt) return int(response) except ValueError: return response Application code: m = respond("month? ") if m == "Jan": m = 1 elif m == "Feb": m = 2 yields a string converts the string to an integer, or throws ValueError if impossible 2-27
Static vs dynamic typing (4) Pros and cons of static and dynamic typing: Static typing is more efficient. Dynamic typing requires run-time type checks (which make the program run slower), and forces all values to be tagged (to make the type checks possible). Static typing requires only compile-time type checks, and does not force values to be tagged. Static typing is more secure: the compiler can guarantee that the object program contains no type errors. Dynamic typing provides no such security. Dynamic typing is more flexible. This is needed by some applications where the types of the data are not known in advance. 2-28
Type completeness (1) In principle, a value of any type can be: assigned composed with other values (as components of composite values) passed as an argument (to a procedure or function) returned as a function result. But some (mainly older) PLs restrict which of these operations are applicable to certain types of values. First-class values are values that are not restricted in which operations can be applied to them. 2-29
Type completeness (2) C: primitive structure array function can be assigned??? can be composed?? can be argument??? can be function result??? Pascal: primitive record array function can be assigned? can be composed? can be argument? can be function result??? 2-30
Type completeness (3) Ada: primitive record array function can be assigned?? can be composed?? can be argument?? can be function result?? Haskell: primitive tuple list function can be composed? can be argument? can be function result? 2-31
Example: type completeness (1) Ada function and application code: type Complex is record x, y: Float; end record; function sum (c1, c2: Complex) return Complex is begin return (c1.x+c2.x, c1.y+c2.y); end; -- Print the complex sum of p, q, and r: put(sum(sum(p, q), r)); 2-32
Example: type completeness (2) What if Ada function results were restricted to primitive values? procedure add (c1, c2: in Complex; c3: out Complex) is begin c3 := (c1.x+c2.x, c1.y+c2.y); end; -- Print the complex sum of p, q, and r: declare t1, t2: Complex; begin add(p, q, t1); add(t1, r, t2); put(t2); end; 2-33
Type Completeness Principle Some PLs are more class-conscious than others: C and Pascal are very class-conscious. Ada is moderately class-conscious. Haskell is not class-conscious at all (all values are first-class). PL designers should bear in mind the Type Completeness Principle: No operation should be arbitrarily restricted in the types of its operands. Examples: Restricting function results to be primitive is arbitrary. Restricting the operands of and to be booleans is reasonable. 2-34
Orthogonality The type completeness principle is an instance of the PL characteristic of orthogonality Orthogonality (Sebesta): a relative small number of primitives can be combined in a relative small number of ways to build all control and data structures every possible combination of primitives is legal and meaningful meaning of a feature is independent of its context in the program (cf. compositionality) important design principle, also outside PL design 2-35
Exercise Find examples of orthogonality in your favourite PL find examples of lack of orthogonality in your less favourite PLs 2-36