types are sets

This week is a bit of a stopgap article, since the planned one fell through, so please excuse the rough edges. Since we never shied away from using somewhat non-standard or uncommon definitions, please do keep in mind (now, and in the future) that those are not truths universally acknowledged.
That said, here is a slightly controversial definition:
A type is a (possibly infinite) set of values. A type system in turn is a set of types.
It's not a bad definition though, so let's run with it and see what happens (with the caveat that we need to know what our values are). There is, however, one major downside of this otherwise very nice and very simple definition: we cannot do a whole lot with a type defined this way.
There is one thing that we can do though: we can test membership. The question ‘is value val of type T?’ turns out to be a very important one. It also readily generalizes to ‘is any value that might appear here of type T?’ – this is important, because we often do not know the exact value we are interested in (this information is only available at runtime). If you point at a variable, or more generally any expression, the set of values it can take is hard to pin down1.
Before we proceed: why do we need to know whether some value, or expression, belongs to some type? Because we can use this information to argue about correctness of a program; in particular, we can talk about operations which only work for certain types of values. For instance, we could say that ‘modulo’ (remainder after division) is only defined for integers (this is a statement about types: ‘integers’ is a set of values, let's call this set int).
In languages where functions are values, we can also say things like ‘set of all functions that accept an int and return an int’ (we could call this type int → int). Clearly, this is a set of values, or in other words, a type. If we know that f is a function that belongs to int → int and we know that not_a_number is a value that is not of type int, we also know that f( not_a_number ) is wrong.
1
The question whether a given variable can take a given value is, in general, undecidable (this should be quite obvious). We can also formulate the same observation this way: the exact set of values that a variable may take is not recursive (again, in general).

practicalities and classification

Of course, treating types and type systems as just sets is not very practical. We will use these terms with the understanding that we need computational machinery to decide questions about types and how values relate to types, that we perhaps need syntax for annotations, and so on. But do keep the above ‘core definition’ in mind, even when we use the terms somewhat freely.
With that in mind, our definitions are sufficient to tentatively define the basic type system categories:
And the other axis:
This latter classification seems a little puzzling. In practice, it is not binary: essentially all actual type systems are ‘weak’ in this sense, because they will allow the programmer to lie about types. However, the context in which an answer can be wrong is important – conventional ‘weak’ type systems can get the answer wrong even if the programmer didn't lie (but it is a little hard to give precise meaning to ‘didn't lie’, so we will leave it be).
Type systems that are both dynamic and weak are actually pretty rare, because they usually cannot erase3 type information, and hence can use runtime type information to provide exact answers.
2
There is obvious tension between the claim that this is, in general, an undecidable question, yet we claim that a static type system can provide an answer. This is because static type systems quite severely restrict what is considered a valid program.
3
Types are said to be erased if, at runtime, no information about types is attached to values. This is common in statically-typed languages, since they can resolve any questions about types at compile/analysis time. Maintaining type information at runtime can be quite costly, but in this case would provide little benefit.

type system properties

There are a few more interesting properties of type systems worth discussing:
Are types disjoint? If yes (each value belongs to exactly one type), the system is monomorphic. This is uncommon. Almost all systems allow some forms of polymorphism (values can belong to more than one type):
Can users define their own types? (The universal answer here seems to be ‘yes’.) Given this is possible, how are new types constructed? There are two main categories: Annotations: in which cases does the user need to explicitly mention types? Answers vary wildly depending on many factors: There is a lot more to say about type theory – we talked about some of it last week – and we will surely return to it in the future.