Heath Carroll Bill Hanczaryk Rich Porter
A Theory of Type Polymorphism in Programming ◦ Robin Milner (1977) Milner credited with introducing the idea of type polymorphism ◦ The programming language he developed, ML, was the first language to use polymorphic type inference and type-safe exception handling.
Introduce the design of polymorphic type inference Present three programming languages that utilize different degrees of typing ◦ ML – Bill Hanczaryk ◦ C# – Heath Carroll ◦ Python – Rich Porter
Type Polymorphism – A feature of a program that allows different data types to be handled essentially the same way. ◦ Polymorphic Function – A function that can evaluate values of different data types. ◦ Polymorphic Data – A data type with only a generalized type. Static Typing - type checking at compile-time. Dynamic Typing – type checking at run-time. Strong Typing - In order to mix types, you must use an explicit conversion. Weak Typing - mixing types without an explicit conversion is okay.
ML Typing ◦ static – type checking at compile time static ◦ strong –type mixing must be done explicitly strong ◦ inferred -> Next Slide inferred Even though type inferred, basic types still exist ◦ int, real, string, bool, and char. ML created (by Milner) to specifically utilize the Hindley-Milner type inference algorithm to automatically infer the types of expressions
Type Inference - The ability of a programming language to automatically detect the data type of a variable. A compiler is often able to infer the type of a variable without explicit type annotations having been given. Why is it useful? ◦ A programmer does not have to explicitly define type annotations while still maintaining about the same level of type safety. ◦ Types often clutter programs and slow down programmer efficiency.
Example -fun f(x) = 2+x; >val it = fn : int -> int How does it work? ◦ + has two types: int*int -> int, real*real -> real ◦ 2 : int has only one type ◦ This implies + : int*int -> int ◦ From context, need x: int ◦ Therefore f(x:int) = 2+x has type int -> int
Standard type checking ◦ int f(int x) { return x+1; }; ◦ int g(int y) { return f(y+1)*2;}; Type inference ◦ f(x) { return x+1; }; ◦ g(y) { return f(y+1)*2;}; Look at code without type information and figure out what types could have been declared.
According to the Wikipedia C# is : ◦ static – compile time type checking static ◦ dynamic – runtime type checking dynamic ◦ strong – any type mixing must be done explicitly strong ◦ safe – type mismatches throw errors, find a way to work (ie adding an int to a string in javascript) safe ◦ nominative - two variables are type-compatible if and only if their declarations name the same type nominative Basically Java using C syntax, boxing, and garbage collection, but without global variables.
How on earth can a language be both at the same time? Don’t they contradict each other? ◦ Sort of. It’s just redundant to type check at both compile time and runtime. ◦ But C# isn’t fully dynamic, it’s dynamic only when you’d want it to be, literally. C# has a new static type: Namely the dynamic type.
dynamic d1 = new Foo(); dynamic d2 = new Bar(); string s; d1.M(s, d2, 3, null); Q: What does the compiler do when it comes across this function M()?
A: Nothing! It allows the program to figure it out at runtime. (ok, so maybe it does something…) The compiler sorts the function call to M(), so that at runtime M() can be invoked with the arguments passed in. Which it does as follows:
1. Reflection is used to obtain the actual runtime types of the two objects, d1 and d2, that did not have a static type (or rather had the static type dynamic). The result is Foo for d1 and Bar for d2. Reflection - provides objects (of type Type) that encapsulate assemblies, modules and types. You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties. If you are using attributes in your code, Reflection enables you to access them.Type
2. Method lookup and overload resolution is performed on the type Foo with the call M(string,Bar,3,null) using ordinary C# semantics. Overload resolution – The Dynamic Runtime Library is responsible for figuring out which method to actually use if more than one is found. ◦ Betterness rules apply.
3. If the method is found it is invoked; otherwise a runtime exception is thrown
M(string s, int i = 1); M(object o); M(int i, string s = “Hello”); M(int i); Given the functions above are all applicable, which one or ones are most suitable and which one gets the call?
Given these overloads, we can see the working of the rules above. M(string,int) is not applicable because 5 doesn’t convert to string. M(int,string) is applicable because its second parameter is optional, and so, obviously are M(object) and M(int). M(int,string) and M(int) are both better than M(object) because the conversion from 5 to int is better than the conversion from 5 to object. Finally M(int) is better than M(int,string) because no optional arguments are omitted.
Strong ◦ No implicit type conversions Duck ◦ “When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck” Dynamic ◦ No initialization/declaration ◦ Type “determined” at run time
Cannot add int to string ◦ ‘a’ + 3 → error ◦ ‘a’ + ‘3’ → ‘a3’ Cannot add string to list ◦ ‘at’ + [‘bad’,‘cat’] → error ◦ [‘at’] + [‘bad’, ‘cat’] → [‘at’,‘bad’,‘cat’] Interesting ◦ 3*3 → 9 ◦ ‘3’*3 → ‘333’ ◦ ‘3’*’3’ → error
Not concerned with type inference Only interested in the aspects that are used rather than the object itself.
Values carry type not variables
Type is determined at run time
Normal type inference is not necessary with duck typing Python Type Inference Applications: ◦ Optimized Interpreter ◦ Compile Python → Another Language (Perl, C, …) Further Reading ◦ “Psyco”Psyco ◦ “Type Inference for Python”Type Inference for Python ◦ “Aggressive Type Inference”Aggressive Type Inference