The OP is contrasting between a 'validation' function with type e.g.
validateEmail :: String -> IO ()
and a 'parsing' function validateEmail :: String -> Either EmailError ValidEmail
The property encoded by the ValidEmail type is available throughout the rest of the program, which is not the case if you only validate.