Static types can really aid in designing software, especially if your type system has good facilities for composing those types. A case study Let’s say we want to define a binary tree. Each node in a binary tree consists of an element and 0–2 children. Here’s how we could define a tree of integers in Haskell: data Tree = Empty| Node Tree Int Tree This syntax may not be familiar, so let’s break it down. We are defining a custom data type called . The (read “or”) indicates that there are two variants. We are recursively defining a to be either or . Tree | Tree Empty Node is the base case of our recursive definition, and represents an empty tree. It doesn't have any other data associated with it. Empty is the recursive part of our definition, and consists of , an , and a (the left subtree, current element, and right subtree, respectively). Node Tree Int Tree We can create a tree with one element, a , as follows: 3 tree = Node Empty 3 Empty Adding a to the right looks like this: 4 tree' = Node Empty 3 (Node Empty 4 Empty) Algebraic Data Types is an example of an Algebraic Data Type (ADT). ADTs can further be classified as and types. Tree sum product Product types A product type is a combination of several values, or . above is an example of a product type. In this case, the fields are the left subtree, the current element, and the right subtree. (Think , a boolean .) fields Node this that and AND Classes in an OO language are also examples of product types. Sum types A sum type is a set of multiple cases. Each of the cases may have some data associated with it. above is a sum type, because it's either or . (Think , a boolean .) Tree Empty Node this that or OR Enums are the closest thing to sum types in an OO language. Enums can express variants, but can’t associate data with the different cases. Types as building blocks We can compose our types using sums and products, like building blocks, to create new types. This allows us to precisely and succinctly model our domain. Let’s look at another example to see how we might combine types together: data Result = Success| Error String This type allows us to model the result of a computation that might fail. In the case of an error, we will provide an error message. is a sum type ( or ), while is a product type. Result Success Error Error String Conclusion Not all static type systems are created equal. The Javas of the world provide product types in the form of classes, but don’t provide first-class support for sum types. This limits what you can express in your types. To play around with ADTs, take a look at OCaml, F#, Haskell, Rust, Swift, or even TypeScript. It will change the way you approach modeling your domain.