Want to see the full-length video right now for free?
Ian's background is in C++, C and Java, while Gabe has mostly used Ruby. Recently they've been using a lot of Haskell and Elm. In this video we'll show you the range and power of type systems of various languages, and explain why you might be interested in checking out a more strongly typed language.
This grid compares how languages fit with the properties of type systems:
Strong types | Weak types | |
---|---|---|
Dynamic type checking | Ruby, Python | PHP, JavaScript |
Static type checking | Elm, Haskell | N/A |
Ruby is strongly typed because 1 + "hello"
raises an error (while in
JavaScript the result is "1hello"
). Haskell and Elm do static type checking,
which means that they check that all of the types of variables and functions
align before the program ever runs. This can catch a lot of errors that aren't
possible to catch in e.g. Ruby. There's a (mostly true) saying in Haskell: "if
it compiles, it works".
In Ruby, we write a lot of tests in part because we don't have static type checking. If we had static type checking, some of those tests could go away because we'd have some more guarantees about how our software behaves. In Ruby, even as experienced developers, we're much less confident that new code will Just Work. In contrast, as new developers in Elm and Haskell, we've written code that works the first time.
We can write bugs in our tests, but it's much more rare to encounter a compiler bug. Any odd behavior is much more likely to be because of the programmer's error, and not the compiler's. Plus, fewer tests overall means fewer tests to maintain and keep up to date. With static typechecking, the types have to be up to date or the program won't run at all. Typechecking is also enforced: it's possible to write Ruby without writing any tests at all. In Haskell and Elm, the type signatures must match up.
There are entire classes of errors that we can avoid in languages like Haskell
or Elm. One of them is exhaustiveness: we're forced to cover every single
possible value of a type. For example, there's a Maybe type that might be empty
(Nothing
) or contain a value like 3 (Just 3
). We can't handle only the Just
x
case and not handle the Nothing
case because the language will raise a
warning.
Another case is a string status
column that might be published
,
unpublished
, or draft
. In Ruby, there's no language-level guarantee that
we're checking for all three cases -- we might only be checking if a post is
published
.
The more knowledge that the compiler has about functions and values, the more it
can help. For example, in Haskell, database IDs are not integers: they're a
distinct type like UserId
or PostId
. Because these are separate types, it's
impossible to use a user ID to fetch a post by accident, because the types don't
match up.
This saves some more code too: User.find
in Ruby is find
in Haskell, because
the type of the ID tells the language which database table to search.
With more type information, we can (hopefully) get better error messages. Haskell is noted for actually having fairly poor error messages, especially given how much it can infer about code. Elm, on the other hand, has maybe the best error messages of any widely-used programming language, and the types help a lot.