I don't think people appreciate the tradeoffs they're making with algebraic data types
09 March 2024Specifically, 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)
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)
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.