Unit 2. Introducing types

Nearly every programming language supports some idea of types. Types are important because they define the kinds of computation allowed on certain data. Take, for example, the text "hello" and the number 6. Say you want to do something like add them together:

"hello" + 6

Even someone with no prior programming experience would find this question interesting, because it’s not clear what to do. The two most obvious answers are to

To arrive at either option, you need a way to keep track of the type of your data, as well as the type of data your computation is expecting. Typically, we call the value of "hello" a String, and the value of 6 an Int. Regardless of your choice of programming language, you need to know the types you’re dealing with so you can either throw an error when they don’t match or do some sort of automatic conversion by knowing how to transform types. Even if you don’t think about types much in your programming language of choice, they are an important part of programming.

Languages such as Ruby, Python, and JavaScript use dynamic typing. In a dynamic type system, all the decisions like the one we made with "hello" and 6 happen during runtime. The benefit of dynamic typing for the programmer is a generally more flexible language and no need to manually keep track of types. The danger of dynamic typing is that errors happen only at runtime. For example, say you have the following expression in Python:

def call_on_xmas():
  "santa gets a "+10

This code will cause an error because Python requires 10 to be converted to a string before adding it to the string literal. As you can guess by the function name, this function won’t be called until Christmas! If this mistake snuck into a production system, it could mean a frustrating Christmas debugging a rare problem. The solution has been to incorporate extensive unit testing to ensure that bugs like this can’t slip in. This somewhat negates the benefit of not having to annotate types as you code.

Languages such as Java, C++, and C# use static typing. With static typing, problems such as "hello" + 6 are resolved during compilation. If a type error occurs, the program won’t compile. The obvious benefit of static typing is that an entire class of bugs can’t make it into running programs. The downside, traditionally, is that statically typed languages require the programmer to add many type annotations. Type signatures are required for every function/method, and all variables must have their type declared.

Haskell is a statically typed programming language, but it certainly doesn’t look like a statically typed language in the examples you’ve seen so far. All of your variables and functions have made no references to types at all. This is because Haskell makes heavy use of type inference. The Haskell compiler is smart, and can figure out what types you’re using based on the way the functions and variables are used.

Haskell’s type system is extremely powerful, and is at least as fundamental to making Haskell unique as its adherence to pure functional programming. In this unit, you’ll be introduced to the basics of Haskell’s type system. You’ll learn how to model data and define your own types and type classes.