Last time this discussion came up someone mentioned GHC’s style guide says to prefer the monomorphic version:
As a rule of thumb, when writing monomorphic code, prefer using a monomorphic function over a typeclass-overloaded one.
- A monomorphic function communicates information to the programmer, e.g. when reading
map f wibbles
one learns thatwibbles
is a list, as opposed to readingfmap f wibbles
where one needs to run a mental typechecker to figure out the type ofwibbles
and then infer thatfmap
uses the instance for lists.- Monomorphic functions lead to easier-to-understand type errors.
- It is easier to refactor code using monomorphic functions, as one can grep/search for a certain function name. Searching for
mapBag
is much, much more helpful than searching forfmap
.- Overloaded functions rely on typeclass specialisation to be performant, which isn’t always reliable and might require additional complexity, in the form of manual
SPECIALISE
annotations andRULES
pragmas.- Using an overloaded function may allow the program to continue to compile after a refactor that changes the type of the argument – but the new behaviour may be flat-out wrong. As Kerckhove puts it: type-class polymorphism erodes type safety.
Although I feel like some of these points are also arguments against polymorphism and type inference in general. And most of these points could be solved by better tooling (HLS already solves the first point).