I don't think people appreciate the tradeoffs they're making with algebraic data types

09 March 2024

Specifically, people don’t appreciate the commitment they’re making by exposing an ADT’s implementation. This commitment is > Whenever this implementation changes in any way, I want every usage that matches on it to break

That’s a lot! This isn’t to say that ADTs with exposed implementations are bad. They’re perfectly fine if you either don’t care (the definition of Maybe is not going to change any time soon) or if breaking downstream code is something you want. E.g. if you’re writing a compiler, you want every function that consumes the AST to break until it handles the newly added cases.

But it means that ADTs are just not a good choice if want to be able to add new cases or change the implementation details of existing ones in the future.

And I wish functional programmers recognized this! It already starts with the most basic examples we give people.

data Shape
  = Circle Float Float Float
  | Rectangle Float Float Float Float

surface :: Shape -> Float
surface (Circle _ _ r) = pi * r ^ 2
surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
syntax highlighting by codehost

This particular one is from Learn You A Haskell, but there are many similar ones in many other beginner resources.

This is a terrible way to model a shape! Your program is almost certainly going to want to handle more shapes than just circles and rectangles at some point, but with this definition you can’t add more without breaking every consumer!

And it’s not like Haskell doesn’t have the tools to deal with this! This case could absolutely be defined more reasonably like this.

data Circle = Circle Float Float Float
data Rectangle = Rectangle Float Float Float Float

class Surface shape where
    surface :: shape -> Float

instance Surface Circle where
    surface (Circle _ _ r) = pi * r ^ 2
instance Surface Rectangle where
    surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
syntax highlighting by codehost

If this reminds you of object oriented programming, that’s because the OOP solution is just plain better here and I wish functional programmers would stop rejecting anything that reminds them of it.

Algebraic data types are great but they’re not a panacea.