Principles of Programming Languages www.cs.bgu.ac.il/~ppl172 Lesson 4 - Type Checking Collaboration and Management Dana Fisman 1
Why declare types? Declaring types allows the compiler to detect errors at compile-time ootherwise errors will only be discovered at runtime oalways preferable to detect errors as early as possible in the development cycle provides code documentation for intended use 2
But it is annoying It makes programs longer, and simple programs more complex Typesecript alleviated these problems by making type annotations optional inferring types when possible (making type annotations implicit) This approach is called gradual typing 3
4 Runtime Errors Caused by Unexpected Values
Runtime Errors Caused by Unexpected Values Compilation error: 5
Type checking Type annotations are introduced to allow type checking Type checking is a process that analyzes a given program and verifies that such incompatibities between variables or function parameters and the values to which they are bound at runtime are impossible for all possible executions of the program. Type checking is preformed at compilation time Compilers implementing type checking are a powerful verification tool 6
7 Another example
Why give up on type checking? Conciseness without type annotations: with type annotations: 8
Why give up on type checking? Conciseness is a weak argument Adding type annotations is a form of documentation which clarifies the programmer s intent may result in Adding type declarations prevents this 9
Why give up on type checking? Easy adjustment to incremental changes 10 Suppose we want to add address to person In an untyped language we can patch this incrementally In typed-checked languages this would require in-depth modifications.
Why give up on type checking? More relevant in interactive programming - Programmer interacts with the interpreter constantly - Doesn t undergo compilation anyway - Not using types annotations allows fast experimentations Counter argument: Once prototyping stage is over, type annotations should be added 11
Types in Typescript Type declarations are optional. oif they are present, they are checked, otherwise no check is performed. o This means that regular Javascript with no type annotations at all are valid Typescript expressions. The Typescript compiler performs two tasks: oit translates a Typescript program into a Javascript program oit checks that the program satisfies all the type declarations specified in the program. Type annotations can be implicit and inferred by the Typescript compiler. 12
13 Compiling Typescript to Javascript
Type annotation - where? With variables declarations Let varname : <vartype> = <varexpr>; In function signature (<Type1>,,<Typek>) => <returntype> funcdef with or without the <varexpr> in other syntactic variants as well (as we shall see later) 14
15 Atomic Type Expressions
Array Type Expressions An homogenous array of elements of types <Type> is defined as follows: <Type>[] 16
Map Type Expressions An map type expression is defined as follows: { <key1> : <Type1>, <key2> : <Type2>,, <keyn> : <Typen>} } 17
Map Type Expressions This declaration states that s should be assigned a map value that has at least the keys name, cs, age with their proper types s can be assigned a map value that has more keys Not OK OK Not OK OK 18
Named Type Expressions Type expressions can be given names: o Expressions created with map by using keyword interface o Expressions created with array by using keyword type 19
Embedded Type Expressions Map expressions can be embedded into each other 20
Map Type Relationships Recall: a type is a set of values. Which type contains more elements? o mapa or mapab? o Answer: mapa o We say that mapab is a sub-type of mapa 21
Map Type Relationships How can we define the map type that contains all map types? o interface mapall {} What is the relationship between o mapab and mapac? o They are distinct, none contains the other. o They have a non-empty intersection. o What type is their intersection? 22
Function Types A function type has the following form (<Type1>,,<Typek>) => <returntype> It provides the type of all parameters and the type of the return value This is called the function signature 23 The function type can be embedded with function definition in various different ways
Function Types Untyped functions 24
Function Types Specifying parameter values and return value Instead of (p1, p2,, pk) Write (p1:<type1>,,pk:<typek>) : <returntype> 25
Function Types Specifying the function value Instead of funcname = funcdef Write funcname: (p1:<type1>,,pk:<typek>) => <returntype> = funcdef 26
Function Types Name of parameters can appear in the function type value (the function signature) but are not mandatory and the parameters in the function body can be named differently 27
Recursive Types How can we define a binary tree? 28
Recursive Types What is the corresponding typesctipt value? 29
Recursive Types We need the definition to be recursive 30
Recursive Types We need the definition to be recursive 31
Recursive Types Does not pass type checking Missing left, right Compilation Error: 32
Recursive Types We can use? to denote that a key is optional How is it different than not including left? at all 33
Generic Types We have our nice (recursive) binary tree Now we want to compute functions on it: Print all leaves Some all leaves Calculate number of nodes Calculate depth of the tree We nicely implement them Now we need a binary tree where the value is another type. Should we compute all functions a new? 34
Generic Types Generic types allows to write complex types that use type variables 35
36 Generic Types
37 Closure
Closure 38 function g depends on the value of b which is not passed to it as a parameter b is defined outside the body of the function the function g captures the value of b The value of function g is called a closure Because it closes the captured value together with the function definition
39 Closure
How is this useful? What does the function concat return? concat is a function that returns a function What does the returned function do? 40
How is this useful? What does the returned function do? It receives data and returns a string computed using the value of data. The returned string also uses header, though header is not passed as a parameter. 41
How is this useful? Function concattoname closes on concat by capturing header s value with My name is 42
Closures and their types Value Type header data 43
Closures and their types Value Type header string data string header + + data +. string 44
Closures and their types Value Type header string data string header + + data +. string concat s input 45
Closures and their types Value Type header string data string header + + data +. string concat s input string 46
Closures and their types Value Type header string data string header + + data +. string concat s input string concat s output 47
Closures and their types Value Type header string data string header + + data +. string concat s input string concat s output string => string 48
Closures and their types Value Type header string data string header + + data +. string concat s input string concat s output string => string concat 49
Closures and their types Value Type header string data string header + + data +. string concat s input string concat s output string => string concat string => (string => string) 50
Type of Closures What is the type of the closure concattoname? string => string The fact that it depends on an outside parameter is not reflected in its type It is an internal aspect of closure 51
Type Compatibility The process of type checking analyze the given program and checks that there are no type discrepancies. Or that types are compatible Type compatibility is not a symmetric relation 52
Can be bonded to When variable of type <V> can be bounded to an expression Cof type <E> we denote it <V> <E> The type checking rules determine when <V> They guarantee that variable defined with type annotation <V> will never be instantiated with a value outside it is value-type C <E> when <E> is a subtype of <V> They need to be checked in o let expressions o assignments 53 o passing parameters to functions
binding atomic types When variable of type <V> can be bounded to an expression Cof type <E> we denote it <V> <E> An atomic type can be bounded only to the same atoms type o If <V> is number then <E> should be number o If <V> is string then <E> should be string o If <V> is boolean then <E> should be boolean 54
binding array types When variable of type <V> can be bounded to an expression Cof type <E> we denote it <V> <E> An array type <V>[] can be bounded C only to an array type <E>[] such that <V> <E> 55
binding map types When variable of type <V> can be bounded to an expression Cof type <E> we denote it <V> <E> <V> A map type {k1: <V1>,, kn : <Vn> } can be bounded Conly to a map type {k1: <E1>,, kn : <En>, } such that <V1> C <E1> <E> <V2> <E2> C <Vn> <En> <E> is a subtype of <V> 56
binding function types When variable of type <V> can be bounded to an expression Cof type <E> we denote it <V> <E> A function type (<V1>, <Vn>) => <V> can be bounded only to an function type (<E1>, <En>) => <E> such that C Same order <V> <E> C <E1> C <V1> <E2> Reverse <V2> order C <En> <Vn> 57
Implicit & Explicit Type Annotations Typescript can infer the value of a type this is called implicit type annotation 58
Implicit & Explicit Type Annotations Typesctipt's : typeof() returns a type annotation Here it returns { name: string, age: number, city : string } Javascript s typeof() returns a string denoting whether the var is compound, or what is its primitive type 59
Summary - Types Types are useful in programming languages: o allow the verification of type correctness at compile time instead of failing at runtime o as a form of documentation of programs Types in Typescript are optional. othe Typescript compiler performs type correctness check on the specified types o and compiles the code to untyped Javascript. 60
Summary - Types Typescript type annotations are added in the following places: After variable declaration let var : <typeannotation>; As part of function signature function fname ( Param1 : <typeannotation>, Param2 : <typeannotation>, ) : <typeannotation> { } 61
Summary - Type Language Typescript provides a type language to write type annotations. Primitive type expressions are number, boolean and string Array type expressions are of the form where T can be any type expression. T[] Map type expressions are of the form where T1, T2, can be any type expression. { key1 : T1, key2 : T2, } Map type expressions can be given a name in the form: interface <name> <map-type-expression> 62
Summary - Type Language Type expressions can be embedded into each other to specify the type of complex values. Types can be implicit and inferred by the Typescript compiler in some cases. Recursive types such as trees can be defined using optional properties in named map types. Generic types can be defined using type variables in type expressions. 63
Summary - Function Types Function types specify the type of expected parameters and the return types. ( x1 : T1, x2: T2, ) => T This is called the function signature. Closures may capture variable bindings but these do not appear in their type only the parameters and return value do. 64
Summary - Type Compatibility Type compatibility is checked in let bindings, in parameters passed to functions, and in assignments <E> is a subtype of <V> C When variable of type <V> can be bounded to an expression of type <E> we denote it <V> <E> It should guarantee that variable declared with annotation T var can be bound to a value or variable of type T val 65
Summary - Type Compatibility Primitive types are compatible when they are equal. Array types are compatible when the element types are compatible. Map type x is compatible with map type y when y has at least the same members as x. Function types are compatible when the parameter lists are compatible and the return types are compatible: o The names of the parameters are ignored in the comparison. o Where returned values should be compatible in the same order, and input variables should be compatible in reverse order 66