Wait, what? Are we talking about the same language? The one with monads and zygohistomorphic prepromorphisms? Yes, I mean that Haskell.
For example, what does this type signature tell us:
x :: BoolThis type signature says that x is a boolean value ... and that's it! Type signatures are stronger in Haskell than other languages because they also tells us what values are not:
- x is not a time-varying value
- x is not nullable
- x is not a String being coerced to a truthy value
- x does not have any side effects
Imperative languages, object-oriented languages, and most other functional languages begin from a more complex baseline than Haskell. They all compete for which language provides the most built-in bells and whistles, because they all begin from the premise that more built-in features is better.
However, the problem is that you can't opt out of these features and you can't assume that any library function you call doesn't use all of them. This means that you must either rely on careful documentation like:
- "This function need not be reentrant. A function that is not required to be reentrant is not required to be thread-safe."
- "Throws: will not throw"
- "The array is changed every time the block is called, not after the iteration is over"
- "If x is not a Python int object, it has to define an __index__() method that returns an integer.
- "Great care must be exercised if mutable objects are map keys"
Haskell takes a different design tradeoff: you begin from a simpler baseline and explicitly declare what you add. If you want statefulness, you have to declare it in the type:
-- Read and write an Int state to compute a String stateful :: State Int StringIf you want side effects, you have to declare it in the type:
takeMedicine :: IO () -- Ask your doctor if this medicine is right for youIf you want a value to be nullable, you have to declare it in the type:
toBeOrNotToBe :: Maybe Be -- That is the questionNotice that there are some things that Haskell does not reflect in the types, like laziness and unchecked exceptions. Unsurprisingly, these are also two built-in features that people regularly complain about when using Haskell.
Technically, all these type signatures are optional because Haskell has type inference. If you hate pomp and circumstance and you just want to churn out code then by all means leave them out and the compiler will handle all the types behind the scenes for you.
However, you should add explicit type signatures when you share code with other people. These type signatures mentally prepare people who read your code because they place tight bounds on how much context is necessary to understand each function. The less context your code requires, the more easily others can reason about your code in isolation.