Like many software engineers who started in the early 2000s, learning OOP was the mainstream thing to do. In my specific case, I started as a C++ developer and had years of professional experience. A few years ago, I rediscovered Haskell and rekindled my passion for programming. I want to do my new projects mainly in Haskell.
However, I noticed a repeated and likely anti-pattern in how I do Haskell. And I suspect it is from OOP. I want to share it here and see if it resonates with anyone.
The impetus of software engineering is the maintainability of the code base. OOP design patterns are often based on a design interface in a specific arrangement or through an elaborated class hierarchy, touting that such a code base is more maintainable for reasons A, B, or C.
Such a habit directly influenced me to choose type classes (which are not the same as OOP classes, especially on sub-typings!) more often than needed. Here is my current intuition:
case Q "Are you releasing a library?" of
False -> A "Use ADT or GADT for your logic"
True -> case Q "Do you expect your users to extend the library's behavior?" of
False -> A "Use ADT or GADT for your logic"
True -> A "Pass a function as a parameter, or use type classes if more convenient"
The main reason is that being able to do pattern matching within your code base makes things way more simpler, and the opposite of using type classes for unknowns can evoke all sorts of demons such as OverlappingInstances, UndecidableIntances, etc.
If you are a library writer, and type classes or some sort of callback by passing “functions” as parameters is inevitable, that’s where I think even OOP is moving away from class hierarchy and subtyping. And I admit that because of the lack of subtyping, I thought it was a limitation when using Haskell.But now I rather think it’s a blessing.
I throw it out here for a discussion. I am happy to hear about your experience, too!