diff --git a/content/blog/00_types_intro.md b/content/blog/00_types_intro.md index d4ba237..17452c5 100644 --- a/content/blog/00_types_intro.md +++ b/content/blog/00_types_intro.md @@ -76,14 +76,67 @@ complexity in terms of program size. This is a huge jump, taking us from "undeci "efficient". Once again, approximations prevail. What we have is a fast and practical way of catching our mistakes. -### Types for Documentation and Thought +### Types for Documentation To me, type systems aren't only about checking if your program is correct; they're also about documentation. This way of viewing programs comes to me from experience with Haskell. In Haskell, the compiler is usually able to check your program without you ever having to write down any types. You -can write your programs, like `length xs + sum xs`, feel assured that what +can write your programs, like `length xs + sum xs`, and feel assured that what you wrote isn't complete nonsense. And yet, most Haskell programmers (myself included) _do_ write the types for all of their functions, by hand. How come? -The answer is that types can tell you what your code does. +The answer is that types can tell you what your code does. Even nontrivial functions +can often have types that fit on a single line (and that are quite easy to read). +For example, given a function `typecheck`, you could try figure out what it returns by reading +the code, or by meditating on its name. Or, you could read its type: + +```Haskell +typecheck :: Expr -> Either Error Type +``` + +This can be read "given an expression, `typecheck` returns either an error or a type". +Now you know what to expect when calling the function, what arguments it needs, +and you have some idea of what it does. You get all this without ever looking +at the code. As another example, the function `partition` has the following type: + +```Haskell +partition :: (a -> Bool) -> [a] -> ([a], [a]) +``` + +This one reads, "given a function that returns true or false, and a list, `partition` returns two lists". +It's not too hard to infer that `partition` divides the given list into two, one list +for items that made the input function true, and one for those that made it false. I say this +without having read a word of `partition`'s documentation, and having never seen its code. +The type is enough to convey that information. + +There's more. Types, when viewed in this way, don't just help you understand code you already +have. They can also be used to _find_ code that does what you want. This is the idea behind +[Hoogle](https://hoogle.haskell.org/). Here's an example. + +Suppose I have a list of people's middle names. A middle name is just a string (`String` in Haskell). +Not everyone has a middle name, though. To represent a value that may or may not be present, +we can use the `Maybe` type constructor. A list of people's middle names, then, would have +the type `[Maybe String]`, a list of (potentially absent) strings. Now, suppose I wanted to take +this list, and filter out all of the absent middle names, resulting in just a list of strings, `[String]`. +Do I have to do this myself, or is there a function for it? I put a type signature into Hoogle: + +```Haskell +[Maybe String] -> [String] +``` + +Hoogle responds with a function that fits the search, in the `Data.Maybe` module: + +```Haskell +catMaybes : [Maybe a] -> [a] +``` + +Looks like I don't have to implement it myself after all! When working with Haskell, +I consult Hoogle a lot of figure out the names of existing functions that match my needs. + +I singled out Haskell in this section, but types in a variety of other languages +make for great documentation. When I wrote Rust code, the types were very useful to +understanding how to use a crate; check out the +[documentation page for `Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html), for instance. +Documentation of Elm packages always lists functions' types (see [documentation for `Parser`](https://package.elm-lang.org/packages/elm/parser/latest/Parser), for example). Even C++ type signatures +listed by Doxygen can be quite useful; I, for one, got a lot out of the [LLVM documentation](https://llvm.org/doxygen/classllvm_1_1IRBuilderBase.html).