Here are some example variations of Main.hs which fail to compile, demonstrating that this technique is helping the program be more correct:
This version of main fails to compile simply because I haven't named the numeratorInt.
import Div
import Nonzero
import Text.Read
import Named
main = do
numeratorString <- getLine
denominatorString <- getLine
case (,) <$> readMaybe numeratorString <*> readMaybe denominatorString :: Maybe (Double, Double) of
Nothing -> error "Both must be valid numbers."
Just (numeratorInt, Name denominatorInt) ->
case checkNonzero denominatorInt of
Nothing -> error "Denominator must be non-zero."
Just denominatorNonZero ->
let result = divide denominatorNonZero numeratorInt denominatorInt
in putStrLn ("Result: " ++ show result)Couldn't match expected type ‘Named x0 Double’ with actual type ‘Double’
Here is a version where I got the arguments to divide the wrong way round:
import Div
import Nonzero
import Text.Read
import Named
main = do
numeratorString <- getLine
denominatorString <- getLine
case (,) <$> readMaybe numeratorString <*> readMaybe denominatorString :: Maybe (Double, Double) of
Nothing -> error "Both must be valid numbers."
Just (Name numeratorInt, Name denominatorInt) ->
case checkNonzero denominatorInt of
Nothing -> error "Denominator must be non-zero."
Just denominatorNonZero ->
let result = divide denominatorNonZero denominatorInt numeratorInt
in putStrLn ("Result: " ++ show result)• Couldn't match type ‘n’ with ‘n1’
The denominatorNonZero proof refers to denominatorInt by a
generate name type (n1), and numeratorInt's name (n) is
different.