Languages and Compilers (SProg og Overs - PowerPoint PPT Presentation

About This Presentation
Title:

Languages and Compilers (SProg og Overs

Description:

Type checking Types Primitive Types Arrays Products and ... Types Subtyping in Java Subtyping in Java Point and ... For non-primitive types: if ... – PowerPoint PPT presentation

Number of Views:47
Avg rating:3.0/5.0
Slides: 43
Provided by: csAauDkb3
Category:

less

Transcript and Presenter's Notes

Title: Languages and Compilers (SProg og Overs


1
Languages and Compilers(SProg og Oversættere)
  • Bent Thomsen
  • Department of Computer Science
  • Aalborg University

With acknowledgement to Simon Gay and Elizabeth
White whos slides this lecture is based on.
2
Types
  • Watt Brown may leave you with the impression
    that types in languages are simple and type
    checking is a minor part of the compiler
  • However, type system design and type checking
    and/or inferencing algorithms is one of the
    hottest topics in programming language research
    at present!
  • Types
  • Provides a precise criterion for safety and
    sanity of a design.
  • Features correspond to types.
  • Close connections with logics and semantics.

3
Type checking
Triangle is statically typed all type errors are
detected at compile-time. Most modern languages
have a large emphasis on static type
checking. (But object-oriented programming
requires some runtime type checking e.g. Java
has a lot of compile-time type checking but it is
still necessary for some potential runtime type
errors to be detected by the runtime system.)
Scripting languages such as Perl and Python are
exceptions, having little or no static type
checking.
Type checking involves calculating or inferring
the types of expressions (by using information
about the types of their components) and checking
that these types are what they should be (e.g.
the condition in an if statement must have type
Boolean).
4
Types
Each type is associated with a set of values.
Example the set of values of type Boolean is
false, true. The set of values of type Integer
is a finite set such as -maxint maxint ,
not the mathematical set of integers.
Operations are naturally associated with types
for example, makes sense for integers but not
for booleans (even though, ultimately,
both integers and booleans are represented by bit
patterns). The purpose of type checking is to
protect the programmer by detecting errors of
this kind. Type information is also good
documentation.
It is useful to consider the structure of types
and type constructors, independently of the form
which they take in particular languages.
5
Primitive Types
Basic data types, (more or less) easily
understood as sets of possible values. E.g.
(Java) boolean, int, float, double.
Strings are sometimes viewed as primitive types,
but are more often a special case of a structured
type. E.g. in Ada a string is an array
of characters in Java a string is an object.
Some languages (e.g. Pascal, Ada) provide
enumerated types which are user-defined sets of
values. E.g. type Colour (Red, Blue, Green)
This is different from enum types in C, which
create abbreviations for a range of integers
enum red, blue, green yields red 0, blue
1, green 2, so that redblue blue etc.
6
Arrays
An array is a collection of values, all of the
same type, indexed by a range of integers (or
sometimes a range within an enumerated type).
In Ada a array (1..50) of Float In Java
float a
Most languages check at runtime that array
indices are within the bounds of the array
a(51) is an error. (In C you get the contents of
the memory location just after the end of the
array!)
If the bounds of an array are viewed as part of
its type, then array bounds checking can be
viewed as typechecking, but it is impossible
to do it statically consider a(f(1)) for an
arbitrary function f.
Static typechecking is a compromise between
expressiveness and computational feasibility.
7
Products and Records
If T and U are types, then T ? U (written (T
U) in SML) is the type whose values are pairs
(t,u) where t has type T and u has type
U. Mathematically this corresponds to the
cartesian product of sets. More generally we have
tuple types with any number of components.
The components can be extracted by means of
projection functions.
Product types more often appear as record types,
which attach a label or field name to each
component. Example (Ada)
type T is record x Integer y Float end
record
8
Products and Records
If v is a value of type T then v contains an
Integer and a Float. Writing v.x and v.y can be
more readable than fst(v) and snd(v).
type T is record x Integer y Float end
record
Record types are mathematically equivalent
to products.
An object can be thought of as a record in which
some fields are functions, and a class definition
as a record type definition in which some fields
have function types. Object-oriented languages
also provide inheritance, leading to subtyping
relationships between object types.
9
Variant Records
In Pascal, the value of one field of a record can
determine the presence or absence of other
fields. Example
type T record x integer
case b boolean of
false (y integer)
true (z boolean) end
It is not possible for static type checking to
eliminate all type errors from programs which
use variant records in Pascal the compiler
cannot check consistency between the tag field
and the data which is stored in the record. The
following code passes the type checker in Pascal
var r T, a integer begin r.x 1 r.b
true r.z false a r.y 5 end
10
Variant Records in Ada
Ada handles variant records safely. Instead of a
tag field, the type definition has a parameter,
which is set when a particular record is created
and then cannot be changed.
type T(b Boolean) is record x Integer
case b is when False gt y Integer
when True gt z Boolean end case end
record declare r T(True), a Integer begin
r.x 1 r.z False a r.y 5 end
r does not have field y, and never will
this type error can be detected statically
11
Disjoint Unions
The mathematical concept underlying variant
record types is the disjoint union. A value of
type TU is either a value of type T or a value
of type U, tagged to indicate which type it
belongs to
TU left(x) x ? T ? right(x) x ? U
SML and other functional languages support
disjoint unions by means of algebraic datatypes,
e.g.
datatype X Alpha String Numeric Int
The constructors Alpha and Numeric can be used as
functions to build values of type X, and
pattern-matching can be used on a value of type X
to extract a String or an Int as appropriate.
An enumerated type is a disjoint union of copies
of the unit type (which has just one value).
Algebraic datatypes unify enumerations and
disjoint unions (and recursive types) into a
convenient programming feature.
12
Variant Records and Disjoint Unions
The Ada type
type T(b Boolean) is record x Integer
case b is when False gt y Integer
when True gt z Boolean end case end record
can be interpreted as
(Integer ? Integer) (Integer ? Boolean)
where the Boolean parameter b plays the role of
the left or right tag.
13
Functions
In a language which allows functions to be
treated as values, we need to be able to describe
the type of a function, independently of
its definition.
In Ada, defining
function f(x Float) return Integer is
produces a function f whose type is
function (x Float) return Integer
the name of the parameter is insignificant (it is
a bound name) so this is the same type as
function (y Float) return Integer
Float ? Int
In SML this type is written
14
Functions and Procedures
A function with several parameters can be viewed
as a function with one parameter which has a
product type
function (x Float, y Integer) return Integer
Float ? Int ? Int
In Ada, procedure types are different from
function types
procedure (x Float, y Integer)
whereas in Java a procedure is simply a function
whose result type is void. In SML, a function
with no interesting result could be given a type
such as Int ? ( ) where ( ) is the empty
product type (also known as the unit type)
although in a purely functional language there is
no point in defining such a function.
15
Structural and Name Equivalence
At various points during type checking, it is
necessary to check that two types are the same.
What does this mean?
structural equivalence two types are the same if
they have the same structure e.g. arrays of the
same size and type, records with the same fields.
name equivalence two types are the same if they
have the same name.
type A array 1..10 of Integer type B array
1..10 of Integer function f(x A) return
Integer is var b B
Example if we define
then f(b) is correct in a language which uses
structural equivalence, but incorrect in a
language which uses name equivalence.
16
Structural and Name Equivalence
Different languages take different approaches,
and some use both kinds.
Ada uses name equivalence. Triangle uses
structural equivalence. Haskell uses structural
equivalence for types defined by type (these are
viewed as new names for existing types) and name
equivalence for types defined by data (these are
algebraic datatypes they are genuinely new
types).
Structural equivalence is sometimes convenient
for programming, but does not protect the
programmer against incorrect use of values
whose types accidentally have the same structure
but are logically distinct.
Name equivalence is easier to implement in
general, especially in a language with recursive
types (this is not an issue in Triangle).
17
Recursive Types
Example
a list is either empty, or consists of a value
(the head) and a list (the tail)
SML
datatype List Nil Cons
(Int List)
Cons 2 (Cons 3 (Cons 4 Nil))
represents 2,3,4
List Unit (Int ? List)
Abstractly
18
Recursive Types
Ada
type ListCell type List is access ListCell type
ListCell is record head Integer
tail List end record
so that the name ListCell is known here
this is a pointer (i.e. a memory address)
In SML, the implementation uses pointers, but the
programmer does not have to think in terms of
pointers.
In Ada we use an explicit null pointer null to
stand for the empty list.
19
Recursive Types
Java
class List int head List tail
The Java definition does not mention pointers,
but in the same way as Ada, we use the explicit
null pointer null to represent the empty list.
20
Equivalence of Recursive Types
In the presence of recursive types, defining
structural equivalence is more difficult.
List Unit (Int ? List)
We expect
and
NewList Unit (Int ? NewList)
to be equivalent, but complications arise from
the (reasonable) requirement that
List Unit (Int ? List)
and
NewList Unit (Int ? (Unit (Int ? NewList)))
should be equivalent.
It is usual for languages to avoid this issue by
using name equivalence for recursive types.
21
Other Practical Type System Issues
  • Implicit versus explicit type conversions
  • Explicit ? user indicates (Ada, SML)
  • Implicit ? built-in (C int/char) -- coercions
  • Overloading meaning based on context
  • Built-in
  • Extracting meaning parameters/context
  • Polymorphism
  • Subtyping

22
Polymorphism
  • Polymorphism describes the situation in which a
    particular operator or
  • function can be applied to values of several
    different types. There is a
  • fundamental distinction between
  • ad hoc polymorphism, usually called overloading,
    in which a single name refers to a number of
    unrelated operations. Example
  • parametric polymorphism, in which the same
    computation can be applied to a range of
    different types which have structural
    similarities. Example reversing a list.

Most languages have some support for overloading.
Parametric polymorphism is familiar from
functional programming, but less common (or less
well developed) in imperative languages.
23
Subtyping
The interpretation of a type as a set of values,
and the fact that one set may be a subset of
another set, make it natural to think about
when a value of one type may be considered to be
a value of another type.
Example the set of integers is a subset of the
set of real numbers. Correspondingly, we might
like to consider the type Integer to be a subtype
of the type Float. This is often written Integer
lt Float.
Different languages provide subtyping in
different ways, including (in some cases) not at
all. In object-oriented languages,
subtyping arises from inheritance between classes.
24
Subtyping for Product Types
The rule is
if A lt T and B lt U then A ? B lt T ? U
This rule, and corresponding rules for other
structured types, can be worked out by following
the principle
T lt U means that whenever a value of type U is
expected, it is safe to use a value of type T
instead.
  • What can we do with a value v of type T ? U ?
  • use fst(v) , which is a value of type T
  • use snd(v) , which is a value of type U

If w is a value of type A ? B then fst(w) has
type A and can be used instead of fst(v).
Similarly snd(w) can be used instead of
snd(v). Therefore w can be used where v is
expected.
25
Subtyping for Function Types
Suppose we have f A ? B and g T ? U and we
want to use f in place of g.
It must be possible for the result of f to be
used in place of the result of g , so we must
have B lt U.
It must be possible for a value which could be a
parameter of g to be given as a parameter to f ,
so we must have T lt A.
Therefore
if T lt A and B lt U then A ? B lt T ? U
Compare this with the rule for product types, and
notice the contravariance the condition on
subtyping between A and T is the other way around.
26
Subtyping in Java
  • Instead of defining subtyping, the specification
    of Java says when
  • conversion between types is allowed, in two
    situations
  • assignments x e where the declared type of x
    is U and the type of the expression e is T
  • method calls where the type of a formal
    parameter is U and the type of the
    corresponding actual parameter is T.

In most cases, saying that type T can be
converted to type U means that T lt U
(exceptions e.g. byte x 10 is OK even though
10 int and it is not true that int lt byte )
Conversions between primitive types are as
expected, e.g. int lt float.
  • For non-primitive types
  • if class T extends class U then T lt U
    (inheritance)
  • if T lt U then T lt U (rule for arrays)

27
Subtyping in Java
Conversions which can be seen to be incorrect at
compile-time generate compile-time type errors.
Some conversions cannot be seen to be incorrect
until runtime. Therefore runtime type checks are
introduced, so that conversion errors can
generate exceptions instead of executing
erroneous code.
Example
class Point int x, y class ColouredPoint
extends Point int colour
A Point object has fields x, y. A ColouredPoint
object has fields x, y, colour. Java specifies
that ColouredPoint lt Point, and this
makes sense a ColouredPoint can be used as if it
were a Point, if we forget about the colour
field.
28
Point and ColouredPoint
Point pvec new Point5
ColouredPoint cpvec new ColouredPoint5
pvec
cpvec
CP
CP
CP
CP
CP
P
P
P
P
P
29
Point and ColouredPoint
Point pvec new Point5
ColouredPoint cpvec new ColouredPoint5
pvec cpvec
pvec now refers to an array of ColouredPoints OK
because ColouredPoint lt Point
pvec
cpvec
CP
CP
CP
CP
CP
P
P
P
P
P
30
Point and ColouredPoint
Point pvec new Point5
ColouredPoint cpvec new ColouredPoint5
pvec cpvec
pvec now refers to an array of ColouredPoints OK
because ColouredPoint lt Point
pvec0 new Point( )
OK at compile-time, but throws an exception at
runtime
pvec
cpvec
CP
CP
CP
CP
CP
P
P
P
P
P
31
Point and ColouredPoint
Point pvec new Point5
ColouredPoint cpvec new ColouredPoint5
pvec cpvec
pvec now refers to an array of ColouredPoints OK
because ColouredPoint lt Point
compile-time error because it is not the
case that Point lt ColouredPoint
cpvec pvec
BUT its obviously OK at runtime because pvec
actually refers to a ColouredPoint
pvec
cpvec
CP
CP
CP
CP
CP
P
P
P
P
P
32
Point and ColouredPoint
Point pvec new Point5
ColouredPoint cpvec new ColouredPoint5
pvec cpvec
pvec now refers to an array of ColouredPoints OK
because ColouredPoint lt Point
cpvec (ColouredPoint)pvec
introduces a runtime check that the elements of
pvec are actually ColouredPoints
pvec
cpvec
CP
CP
CP
CP
CP
P
P
P
P
P
33
Subtyping Arrays in Java
The rule
if T lt U then T lt U
is not consistent with the principle that
T lt U means that whenever a value of type U is
expected, it is safe to use a value of type T
instead
because one of the operations possible on a U
array is to put a U into one of its elements,
but this is not safe for a T array.
The array subtyping rule in Java is unsafe, which
is why runtime type checks are needed, but it has
been included for programming convenience.
34
Subtyping and Polymorphism
abstract class Shape abstract float area( )

the idea is to define several classes of
shape, all of which define the area function
class Square extends Shape float side
float area( ) return (side side)
Square lt Shape
class Circle extends Shape float radius
float area( ) return ( PI radius radius)
Circle lt Shape
35
Subtyping and Polymorphism
float totalarea(Shape s) float t 0.0
for (int i 0 i lt s.length i) t t
si.area( ) return t
totalarea can be applied to any array whose
elements are subtypes of Shape. (This is why we
want Square lt Shape etc.)
This is an example of a concept called bounded
polymorphism.
36
Formalizing Type Systems
  • The Triangle type system is extremely simple
  • Thus its typing rules are easy to understand from
    a verbal description in English
  • Languages with more complex type systems, such as
    SML, has a type system with formalized type rules
  • Mathematical characterizations of the type system
  • Type soundness theorems
  • Some languages with complex type rules, like
    Java, ought to have had a formal type system
    before implementation!
  • But a lot of effort has been put into creating
    formal typing rules for Java

37
How to go about formalizing Type systems
  • Very similar to formalizing language semantic
    with structural operational semantics
  • Assertions made with respect to the typing
    environment.
  • Judgment G - t, where t is an assertion, G is
    a static typing environment and the free
    variables of t are declared in G
  • Judgments can be regarded as valid or invalid.

38
Type Rules
  • Type rules assert the validity of judgments on
    the basis of other judgments.
  • General Form
  • (name)
  • G1 - t 1 Gn - t n
  • G - t
  • If all of Gi - ti hold, then G - t must hold.

39
Example Type Rules
  • (addition)
  • - E1 int, G - E2 int
  • G - E1 E2 int
  • (conditional)
  • G - E bool, G - S1 T, G - S2 T
  • G - if E then S1 else S2 T
  • (function call)
  • G - F T1 ? T2, G - E T1
  • G - F(E) T2

40
Very simple example
  • Consider inferring the type of 1 F(11) where
    we know 1 int and F int ? int
  • 1 1 int by addition rule
  • F(11) int by function call rules
  • 1 F(1 1) int by addition rule

41
Type Derivations
  • A derivation is a tree of judgments where each
    judgment is obtained from the ones immediately
    above by some type rule of the system.
  • Type inference the discovery of a derivation
    for an expression
  • Implementing type checking or type inferencing
    based on a formal type system is an (relatively)
    easy task of implementing a set of recursive
    functions.

42
Connection with Semantics
  • Type system is sometimes called static semantics
  • Static semantics the well-formed programs
  • Dynamic semantics the execution model
  • Safety theorem types predict behavior.
  • Types describe the states of an abstract machine
    model.
  • Execution behavior must cohere with these
    descriptions.
  • Thus a type is a specification and a type checker
    is a theorem prover.
  • Type checking is the most successful formal
    method!
  • In principal there are no limits.
  • In practice there is no end in sight.
  • Examples
  • Using types for low-level languages, say inside a
    compiler.
  • Extending the expressiveness of type systems for
    high-level languages.
Write a Comment
User Comments (0)
About PowerShow.com