Overall gist, sometimes when you're looking around waiting for someone else to step up, you may realize it needs to be you.
Introduction to Types
What's this?
Consider
1617972849
1900965984
1.02223624099482e+30
`pNq
恰乱
ld h,b; ld (hl),b; ld c,(hl); ld (hl),c
bra pc+0x70; nop
call pc+0x81c139c4
01100000011100000100111001110001
What do these things have in common?
But most similarly, they all the same bit-sequence (hex 0x 60 70 4E 71) interpreted in different ways!
Big-Endian Integer
Little-Endian Integer
IEEE 32-bit Floating Point
ASCII/UTF-8
UTF-16
Z80 Assembly (8-bit instructions)
m68k Assembly (16-bit instructions)
x86 (variable-length instructions)
The same data can mean different things, depending on the type.
Types
What is a type?
An abstraction.
A definition of data and what can/cannot be done with it.
Something that defines the representation of data (what to put in memory and how).
A set of allowed values.
A set of fundamental operators for these values.
A label for humans.
A context for the data
A composition of other types.
What are types for?
They help us catch programming errors.
They allow compilers to generate more efficient code.
They allow us to write code that is more expressive.
They provide an interpretation for information.
They provide restrictions on what we can do with information.
Allow code optimization / generation
What is a type system?
A system of rules that govern types.
A system providing the behaviors of types with each other
What does it mean for a program to be "well-typed"?
A well-typed program will have no detected conflicts such as trying to assign a string value to an integer variable or trying to apply a function to an argument which is not of a type that it can handle.
Let's consider the variable x.
Is the following well-typed? let x = 1 in x+1
Yes! In this example, x is getting the value 1, which is an integer. We are then incrementing x by the value 1 using the + operator, which is defined.
What about this? let x = true in x+1
Nope! It doesn't make sense to set x equal to a value that is of type boolean and then try to increment a bool by an integer. This introduces a conflict.
Classifying Type Systems
We say that languages that catch the error at compile time are statically typed and that languages which catch the error at runtime are dynamically typed.
How Types Influence Meaning
Consider the example
"one" + 2
What could it mean? From that, extrapolate some of the distinguishing characteristics of different type systems.
Some possible results include:
Error: Examples:
Unknown operation for those types
Ambiguous whether string should be cast to integer or vice-versa
Note that errors can be either static (compile-time) or dynamic (run-time)
"one2" - cast the 2 to a string and append
("one", 2) - put the string and the integer in a tuple
"qne" - add two to the first character's ASCII code
"qpg" - add two to all characters' ASCII codes
"e" - move the pointer to the string two characters forward
3 - cast the string to an integer and add
2 - cast the string to an integer, fail because that cast doesn't generally work, yield the default integer (0) and add.
"onetwo" - cast the integer to a string and append
Notably, the above errors can both be generated at compile or runtime, depending on the language.
Let's Make a Type System
Types Recap
What do type systems give us?
Static type checking; find errors earlier.
Tell us what operations are valid for given data.
Efficient memory allocation
Allow the compiler to optimize code!
Insight: typing is essentially a system for formal reasoning about your code...
Formalities
Showing that an expression is well-typed is, essentially, a proof.
Thus, we can define typing rules as proof rules (see the type rules section for an example). Using the form of logic inference rules we learned in CS 81:
\[
\infer
{ Conclusion }
{ Premise(s) }
\]
Note:
\[
\infer
{\text{CS131 is fun!}}
{}
\]
This means the conclusion is inherently true (since it requires no premises). -- MorganFrisby - 12 Mar 2019 (Also known as a tautology -- OwenGillespie - 9 May 2019)
Type Rules for a Simple Language
Imagine a programming language with abstract syntax somewhat similar to Assignment 1 (Lambda calculus with some extra functionality, but not all the functionality of Haskell). It has two primitive datatypes: int and bool; and composite types for tuples, lists and functions.
This syntax is similar to Haskell, but much more simplified. If there are things that look Haskell-y, then they probably behave in the same way.
Type Rules
\[
\infer
{\fe{\econst{\n}}{\tint}}
{}
\]
\[
\infer
{\fe{\econst{\trueval}}{\tbool}}
{}
\]
\[
\infer
{\fe{\econst{\falseval}}{\tbool}}
{}
\]
\[
\infer
{\fe{(\eplus{\e_1}{\e_2})}{\tint}}
{\fe{\e_1}{\tint} \AND {\fe{\e_2}{\tint}}}
\]
(where the top premises are combined by an implicit AND)
\[
\infer
{ \fe{ (\elt{\e_1} {\e_2}) } {\tbool} }
{\fe{\e_1}{\tint} \AND {\fe{\e_2}{\tint}}}
\]
Providing Context: Type Environments
Just like value environments used in evaluation... but more mathy...
Note that we can not have rules like
\[
\infer
{\fe{\econst{\x}}{\tbool}}
{}
\]
or
\[
\infer
{\fe{\econst{\x}}{\tint}}
{}
\]
because that would imply that all values of x would have to satisfy those rules, or else our proof system would have false premises. Instead, we turn to environments to represent allowable deductions for variables' types.
Type environments:
Map names to types — Γ(var)
Can be partially/fully specified — Γ, var :: ty
Provide context for predicates ... — Γ ⊢ exp :: ty (meaning, "In the context of Gamma, exp is of type ty)
i.e., We want to be able to say:
\[
\wfe{\x :: \tint}{\x \;+\; \econst{1}}{\tint}
\]
or
\[
\G, \wfe{\x :: \tint}{\x \;+\; \econst{1}}{\tint}
\]
In English, we could read this as, "A type environment Γ with x being of type int entails x+1 being of type int."
Revised Type Rules
We will rewrite the above type rules using Gamma, our mathy environment for types:
\[
\infer
{\wfe{\G}{\var}{\G(var)}}
{\var\in\mathop{\mathrm{dom}}(\G)}
\]
NOTE: $\mathop{\mathrm{dom}}(\G)$ means the domain of Γ.
Why is it necessary to include $\mathop{\mathrm{dom}}(\G)$?
If we didn't include $\mathop{\mathrm{dom}}(\G)$, then there could be a premise that constrains the type of a variable that isn't stored, which doesn't make sense.