03-types
03-types
• We have no way to restrict the functions either, and could end up with
meaningless programs because of this. For example, consider
𝜆𝑓. 𝜆𝑥. 𝑓 𝑥 𝑥
This is a well defined program if the argument given to it is a function that returns
another function. However, if we give it a function that returns a non-function value
𝜆𝑓. 𝜆𝑥. 𝑓 𝑥 𝑥 𝜆𝑦. 𝑦
Invariants
•
condition that always remains true. For example,
a loop-invariant is a condition that is true at the beginning
as well as the end of each iteration of the loop; a for-loop
may have an inequality as its invariant, such as a condition
x ≤ 10.
*
in the 𝜆-calculus program 𝜆𝑛. , the following condition is an
+
invariant: 𝑛 must be a non-zero number.
• Design considerations:
1. How to write an invariant? That is, what is the ‘language’
of invariants?
2. Can (or should) invariants be inferred from a program, or
must they be provided by the programmer?
3. Can (or should) an invariant be checked statically (i.e.,
before the program is run) or dynamically (i.e., at
runtime)?
Invariants
An abstract data
type is a • We can think of an abstract data type as an
mathematical model interface, and a data structure as its concrete
of a data structure implementation.
and its operations.
class Airplane:
def fly(self):
print("Airplane flying")
class Whale:
def swim(self):
def lift_off(entity):
entity.fly()
*We will sometimes use the verb ‘assign’ when talking about data
types. To make sense of such statements, you should keep in mind
this idea of the data type as a descriptor.
Typed and untyped languages
• Programming languages are often classified according to some of the major
programming paradigms – procedural, functional, and object oriented.
• Within each paradigm, some languages are typed and others are untyped.
• It provides a set of rules for (a) type equivalence, (b) type compatibility,
and (c) type inference.*
• It provides the (sometimes implicit) context for the operations on a data type.
Example 1: the binary operator “+” means concatenation if the arguments are
strings, but arithmetic addition if the arguments are numeric.
Example 2: the unary operator “<<” in C++ is a 1-bit left-shift if the argument is an
integer, but if the argument is an output stream, it means writing to the stream.
Before understanding these terms, though, we will need to first understand how types are checked in various
languages. Thus, the next few slides divert to type checking and type errors.
Advantages of a type system
• Documentation/legibility – typed languages are easier to read and
understand since the code itself provides partial documentation of what a
variable actually means.
• This may be done at compile-time, called static type checking (and the
language is called a statically typed language).
Type equivalence asks the key question needed for the above rule:
When do two given expressions have equivalent types?
struct {
int a;
Type equivalence
int b;
} Structural equivalence
The exact definition of structural equivalence is a bit
murky, and varies from one language to another.
People have differed over questions like “what really is a
structure”, and “when should two structures be considered
equivalent”.
The format, or course, doesn’t matter. The two code
struct { int a, b; } bodies on the top are equivalent types.
A. The types are structurally equivalent, but the language uses name
equivalence.
In this case, the two structurally equivalent types have the same low-level
representation, and the conversion happens implicitly, with no additional code.
/* Java example */
/* implicit non-converting cast when Student is a subtype of Person */
Person p = new Student();
Type conversion
In statically typed languages, the values expected in a context must be of a
certain type, e.g., x := expression, both sides of the assignment must have
the same type.
B. The types have different sets of values, but the intersecting values have a
common representation.
Additional code must be executed at runtime to make sure that the value does,
indeed, lie at the intersection.
/* Java example */
/* If the low-level implementation of ‘p’ is identical to the type ‘Student’ */
Student s = (Student) p;
Type conversion
In statically typed languages, the values expected in a context must be of a
certain type, e.g., x := expression, both sides of the assignment must have
the same type.
/* Java example */
int k = (int) 3.14; // truncating a floating-point number
double d = 3; // converting cast with no loss of precision
Type compatibility
• Most languages require only that types be “compatible”, instead of
completely equivalent, e.g.,
in an assignment statement, the type of the right-hand side must be compatible
with the type of the left-hand side.
in an operation (say, +), the operands must be compatible with some common type
that supports the ‘+’ operation (e.g., integers, floats, doubles, strings, or maybe even
sets).
in a subroutine call, the types of
1. the arguments passed to the subroutine must be compatible with the types of
that subroutine’s formal parameters, and