Haskell is able to infer the type of all expressions1.
A simple example you can run in the repl:
A more complex example:
Haskell's inference about the type of the whole program is shown here, via the Haskell Language Server, which offers to autocomplete it for you. (If you don't have the Haskell Language Server, do the same in the repl with
One can also mouse over any expression in the program:
(If you don't have the Haskell Language Server, do the same by replacing the expression in question with an
The expressive types and automatic inference let you develop a complicated program top-down, by starting with the type signature of the whole program and filling in gaps incrementally. For example:
main :: IO ()
main = runInputT defaultSettings $ flip evalStateT initBoard $ forever $ do
line <- lift requestLine
let instruction = parseInput line
board <- get
let result = evaluate instruction board
case result of
Right (BoardUpdate update) -> modify update
Right DisplayBoard -> lift $ outputStr $ T.unpack $ display board
Left err -> lift $ outputStr err
evaluate = undefined
display = undefined
undefined stands for a piece of code that is yet to be written. The compiler will typecheck this program, even with the
undefineds left in, so you can be sure that it is consistent, before proceeding to fill in the gaps.
More importantly, it will infer the type of each
undefined, like so:
Especially for more complex programs, the compiler's understanding of the types of unwritten parts of your program can be invaluable.
Type based refactoring¶
Because types are expressive in Haskell, and type errors are static, refactoring a codebase can be performed with the help of the compiler:
Here, if we changed the definition of
type Position = (Double, Double, Double), we would be shown a list of compiler errors (in the
Problems tab in VSCode's terminal), which we could then fix one-by-one until the code compiles again.
There are exceptions, but they involve advanced techniques. ↩