Write some more for types series' intro.
This commit is contained in:
parent
8e550dc982
commit
d2e8f809d0
|
@ -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
|
"efficient". Once again, approximations prevail. What we have is a fast and
|
||||||
practical way of catching our mistakes.
|
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;
|
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
|
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
|
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
|
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
|
you wrote isn't complete nonsense. And yet, most Haskell programmers
|
||||||
(myself included) _do_ write the types for all of their functions, by hand.
|
(myself included) _do_ write the types for all of their functions, by hand.
|
||||||
How come?
|
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).
|
||||||
|
|
Loading…
Reference in New Issue
Block a user