Type Systems

COS 320, February 24, 2005 – Rob Simmons

 

Question: What are type systems for programming languages good for?

A: Type checking…

A: Language-level errors: reducing programmer errors – like in C, where you cast weird things to other things.. you can end up unsure what you have

Memory safety – can’t dereference something that isn’t a pointer

Control-flow safety – can’t jump to code that isn’t code

Important for programming, important to safety – most attacks on computers have to do with C allowing buffer overruns

A: Application-level errors: making sure that a programmer can’t access protected variables/functions, or in ML can’t find any information about opaque types

Isolates places where errors can be coming from

Can enforce arbitrary predicates on data: for instance, the SML signature:

sig

   type sorted_list

   val sort : int list -> sorted_list

   val lookup : sorted_list -> int -> bool

   val insert : sorted_list -> int -> sortedlist

end

is acting to enforce an arbitrary predicate (that a list is sorted, in this instance) on data

A: Implementation independence properties: The example above, for instance, ensures that the underlying implementation can absolutely change

 

We are going to implement a strong type checker like ML or Java, instead of C

 

DEFINING TYPE SYSTEMS

Regular Expression => Lexing

Context-Free Grammar => Parsers

 

Is there an easy specification

 

Inductive Definitions => Type Systems

 

An inductive definition really has two parts

1) Specification of the form of judgments – A judgment is an assertion/claim, may or may not be true

2) A collection of inference rules – what allow you to conclude whether a judgment is true or false

 

 

 

 

 

 

There are two kinds of inference rules:

                                                                          J1, J2, J3

Axioms:          -----          Rules: --------

            J                                                  J

 

Axioms – “J is always true”

Rules – “If I can establish the truth of the judgments J1, J2, and J3, J is true”

 

Example of the “syntax of judgements

    |- E : bool

This means “Based on no outside evidence, E is a Boolean expression”

 

So here are some example axioms:

 

------------------- BT   ------------------ BF  

   |- true : bool               |- false : bool

 

Here are some rules that would go along with them:

 

 |- e1 : bool       |- e2 : bool                     |- e1: bool      |- e2 : bool

---------------------------------- B&      ---------------------------------- B||

       |- e1 & e2: bool                                  |- e1 || e2 : bool

 

Note – wherever something in a judgement is in italics (here or in Chapter 9 of Harper) it is a meta-variable – it can actually stand for anything. In the example on the next page we’ll see how this works – take a look at the ways the rule I called “B&” gets used

 

What about if statements?

 

 |- e1: bool      |- e2 : bool     |- e3 : bool

--------------------------------------------------- Bif

            |- if e1 then e2 else e3 : bool

 

So how do you determine if this is true?

 

|- (true & true) & false : bool

 

IMPORTANT NOTE: this is NOT dealing with the actual language – it is dealing with the ABSTRACT SYNTAX TREE – so when we write (true & true) & false, what we’re really dealing with is a tree

 

                        &

                     /       \

                 &         false

               /    \

            true true

Back to the point, we can try to establish the validity of the judgment we just mentioned either top down, or bottom up, (roughly) like parsers. This will happen bottom up.

 

Step 1:

               |- true & true : bool                                      |- false : bool

            ------------------------------------------------------------------------------- Bif

                        |- : (true & true) & false : bool

 

Step 2:

            ---------------------------------- B&                 -------------------- BF

               |- true & true : bool                                      |- false : bool

            ------------------------------------------------------------------------------- Bif

                        |- : (true & true) & false : bool

 

Step 3:

             |- true : bool        |- true : bool

            ------------------------------------ B&              -------------------- BF

               |- true & true : bool                                      |- false : bool

            ------------------------------------------------------------------------------- Bif

                        |- : (true & true) & false : bool

 

Step 4:

            ----------------BT --------------- BT

             |- true : bool        |- true : bool

            ------------------------------------ B&              -------------------- BF

               |- true & true : bool                                      |- false : bool

            ------------------------------------------------------------------------------- Bif

                        |- : (true & true) & false : bool

 

This last step is called the derivation or proof

 

Try something different – now have a judgment “|- E size n”

 

                                                          |- E1 size m    |- E2 size n

-------------------   -----------------     --------------------------------

  |- true size 1       |- false size 1        |- (E1 & E2) size (m + n)

 

This is the sizeof function

 

Back to the true/false/&… example – what we are really defining is a tiny typechecker

However, it is not expressive enough to handle a language with variables.

In order to do that, we have to add a context

 

This is where Chapter 9 of Harper comes in – defines a tiny language, MinML, that defines a tiny language with variables.

 

Check out the concrete syntax in 9.1.1, as I don’t want to write it out again.

 

The Typing Judgment

Γ |- e : τ

 

In context Γ, the expression e has type τ

 

Γ is a sort of map that maps variables to their types.

 

{} is the empty context (Harper and Dr. Walker used the “null set” symbol, but I can’t find it here…)

 

Here’s how it works:

 

(Γ[x : τ])(y)   =  τ if x = y, or else τ’ if Γ(y) = τ’ (this is said more clearly on page 46)

Obviously, if gamma never says anything about y, then this will fail

 

(Most of an) example typing derivation

It’s hopefully clear to see at this point how the one unfinished part of the derivation could go the rest of the way up to axioms

 

“Γ” is used as an abbreviation for “{}[f : int => int][x : int]” to save space

 

                                                                                  

------------9.1 -------------9.2                    --------------------------------9.12  --------------- 9.2

Γ |- x : int       Γ |- 0 : int                                      Γ |- (f (x – 1)) : int          Γ |- x : int

------------------------------ 9.8   ------------- 9.2  ---------------------------------------------- 9.12

Γ |- (x = 0) : bool                      Γ |- 1 : int                               Γ |- (f (x – 1)) * x : int

------------------------------------------------------------------------------------------------------ 9.10

                   {}[f : int => int][x : int] |- if (x = 0) then 1 else (f (x – 1)) * x : int

------------------------------------------------------------------------------------------------------ 9.11

           {} |- fun f (x : int) : int = if (x = 0) then 1 else (f (x – 1)) * x : int => int