A small case study: a pattern matcher in two pseudo-languages, first OO and then functional
In OOP, designing interface-first
interface IPatternMatcher {
bool Match(string toMatch)
}
The above is the only interface we need, but it doesn't really give us any clues about how to design our implementing types. You will probably want some composites, so you implement an OrPatternMatcher
class OrPatternMatcher : IPatternMatcher {
OrPatternMatcher(list<IPatternMatcher> submatchers);
bool Match(string toMatch) {
return _submatchers.Any(s => s.Match(toMatch));
}
}
But now you have started implementing functionality, without having finalised your hierarchy. It is kind of impossible to separate these two stages in OOP because your functions and types are so intertwined
In FP, designing type-first
type PatternMatcher =
| OrPatternMatcher : list<PatternMatcher>
| AndPatternMatcher : list<PatternMatcher>
| SubstringPatternMatcher : string
| RegexPatternMatcher : regex
Here, we have all the necessary types designed up-front, covering all the cases you can think of. The PatternMatcher
union type in FP is related to the IPatternMatcher
interface OOP, with the crucial difference that the union declares its type structure with no functions, whereas the interface declares its functions with no type structure.
It is now trivial to implement a match
function just by looking at the shape of the types you have laid out. If you have designed the types sensibly for your domain, you are already most of the way to having a working component. This is what it means to design your types first - to accurately model your domain without worrying about behaviour
In my opinion OOP interfaces and FP types are almost the opposite of each other, and the benefits gained from designing your FP types up-front are very different to those gained by programming to an interface.
As an additional afterthought, the phrase "design your interfaces first" is not the same as "program to an interface" and so the latter cannot be truly compared to "design your types first".
- "program to an interface" advocates the decoupling of components by not assuming implementation details
- "design your types first" is advice on modelling your domain sensibly before implementing behaviour
The two really aren't that comparable, as they relate to different situations