People love to complain about handling err, but how is it so much different from handing Maybe and Result in functional languages?
Unless you have very specific requirements for that one line, you don't go writing code like:
case f x of
Nothing -> Nothing
Just y -> case f y of
Nothing -> Nothing
Just z -> z
You write: do
y <- f x
fy f <$> f <$> x f x >>= f
But I think those operators tend to confuse people that don't know the language, while do notation is very intuitive (although opaque).By the way, is this the Civilization III armor? I never knew what model they got the image from. It is British... I always expected it to be German for some reason.
Mind rewriting
do
y <- f x
fy
or annotating into something that makes sense?say with an example like response, err := http.Get(someURL)
How would this example look like in haskell if error handling is abstracted away?
resp, err := http.Get(someURL)
if err != nil {
return nil, err
}
res, err := DoStuff(resp)
if err != nil {
return nil, err
}
return res
In Haskelly-Go, you'd do: do
resp <- http.Get(someURL)
DoStuff(res)
The types of both http.Get and DoStuff would be monadic, and the result of the do block would also be monadic. When you eventually get to the place where the error is significant, you handle it there when "unwrapping" your value from the monad. resp, err := http.Get(someURL)
if err != nil {
return nil, err
}
return DoStuff(resp) do
a <- operation1
operation2 a -- This one returns nothing
b <- operation3 a
c <- operation4 b
operation5 c
In Go, you'll keep repeating that 'if err != nil {' everywhere. And things get way more interesting with more complex monads, but error handling is simple. from math import sqrt
def f(a):
if (a >= 0):
return (True, sqrt(a))
else:
return (False, None)
def do(a):
(ok, y) = f(a)
if ok:
(ok2, y2) = f(y)
if ok2:
return (True, y2)
else:
return (False, None)
else:
return (False, None)
# example
for x in [2, -2]:
(ok, y) = do(x)
if ok:
print("Result: {}".format(y))
else:
print("Calculation failed.")
# Output
>>> Result: 1.189207115002721
>>> Calculation failed.
This sort of thing is super awkward in Python, but is a common/natural pattern in Haskell (note: I realize this is not how you'd do this in Python, but I'm trying to make it equivalent to the Haskell). This does the exact same thing as the Python program above and is a more complete version of what the OP posted: import Control.Monad (mapM_)
{-
Maybe Double represents a computation that either
returns a Double or fails. In this case, fail
when passed a negative number. Note: this is not
the same as throwing an exception.
-}
f :: Double -> Maybe Double
f x
| x >= 0 = Just (sqrt x)
| otherwise = Nothing
-- Fleshed out version of OP's snippet
eval :: Double -> Maybe Double
eval x = do
y <- f x
f y
{-
do notation desugars to monadic binding; in this
case eval can be desugared to the equivalent definition:
eval x = f x >>= f
-}
-- Here's why this is convenient, because you can pattern match
printIf :: Show a => Maybe a -> IO ()
printIf (Just x) = putStrLn $ "Result: " ++ show x
printIf Nothing = putStrLn "Calculation failed."
-- This is basically the equivalent of the Python for loop
main :: IO ()
main = mapM_ (printIf . eval) [2, -2]
I know Haskell syntax is strange looking if you're unfamiliar with it, but it's mostly geared towards making certain patterns (like this one) convenient.