Note that it's polymorphic on the type of the return within the Maybe. It's very flexible, the polymorphic type can appear anywhere within the type signature.
Now you can write various functions that use "read" and they all remain return-type polymorphic. For example, you can write one that loops, requesting the user to repeat entry until parse-able data (of the wanted type) is given:
repeatReadingUntilValid :: Read r => IO r
repeatReadingUntilValid = do
line <- getLine
case read line of
Nothing -> do
putStrLn $ "Invalid input: " ++ show line
repeatReadingUntilValid
Just result ->
return result
That's just a silly example, because read isn't that interesting.
Another example is QuickCheck, which uses type-classes to auto-generate fuzz-testers for functions.
For example:
import Test.QuickCheck
pretty :: MyType -> String
pretty = .. pretty print my type here ..
unpretty :: String -> MyType
unpretty = .. parse the pretty printing of my type here ..
Now I can test that unpretty is indeed the inverse of pretty:
quickCheck (\x -> unpretty (pretty x) == x)
(for every x, the unpretty of pretty of x equals x).
I can generalize this property to:
isInverse f g x = f (g x) == x
And then use:
quickCheck (isInverse unpretty pretty)
Similarly you can define:
commutative f x y = x `f` y == y `f` x
associative f x y z = (x `f` y) `f` z ==
x `f` (y `f` z)
transitive f x y z = x `f` y && y `f` z ==> x `f` z
Which makes an important type of unit testing a breeze.
The type system is saving us from writing code here.
One thing is impossible, though: defining two methods that differ only in return type, something like
Most important point:
Something like
is possible in Haskell, but not in Python, and barely imaginable in Java.