Video

Want to see the full-length video right now for free?

Sign In with GitHub for Free Access

Notes

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.

Type systems

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".

Are tests always necessary?

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.

Types are safer

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.

Exhaustiveness

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.

More available knowledge

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.

Errors

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.