Achieving totality in functions
Note
This pertains to Haskell, but the concepts are valid in any functional language.
How to achieve totality? Common patterns:
- Using a default value, e.g. something like
mempty
ofMonoid
- Using
Maybe
,Either
,Validation
,These
, which are effects to model situations like these - Restricting input types (e.g.
NonEmpty
, or newtypes / refinement types) - Make abstractions smaller (example: don't define typeclass instances for types where some of the methods might be partial -
Num
andNatural
typeclass for example, asNum
has negative values as well) - Compiler warnings, static analysers, program verification (a bit more specific to FP languages and Haskell)
- Dependent types (types that depend on values) - allows you to pass in requirements for types at the type levels (e.g. this list will have 5 elements, this integer is not zero, these two matrices have compatible dimensions so that they can be multiplied)
Totality is hard - we don't have enough automated tools to enforce it for now, so we should incorporate it by following these best practices and bake it into our architecture by modeling things in a total way. Totality does not come for free.