4 ms·
People love to complain about handling err, but how is it so much different from handing Maybe and Result in functional languages?
by zalmoxes 10y ago
People love to complain about handling err, but how is it so much different from handing Maybe and Result in functional languages?
- JoshTriplett 10y agoThose languages have patterns that make Result easy to handle, without introducing exception handling. For instance, Rust has "try!", and is introducing the new "?" syntax, to turn an error Result into an early return, and make code with robust error handling feel like straight-line code.
- marcosdumay 10y agoIt is so much different because with monads you get the error handling automatically most of the time. 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
- kruipen 10y agoOr: f <$> f <$> x
- marcosdumay 10y agoYes. Or even: 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).
- douche 10y agoIs the '<$>' operator called the tank operator? Because it looks like this to me: https://en.wikipedia.org/wiki/Mark_IV_tank https://en.wikipedia.org/wiki/Mark_IV_tank
- marcosdumay 10y agoThe operation is called "map". In Haskell it is the same as the "fmap" function, for historical reasons. 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.
- zalmoxes 10y agoyour example went from readable to complete nonsense really quickly. 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?
- haldean 10y agoLet's say you're doing something with the result of http.Get, because otherwise it makes no sense. In Go, you'd have something like: 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.
- greenbast 10y agoThe Go code in your example could actually be written: resp, err := http.Get(someURL) if err != nil { return nil, err } return DoStuff(resp)
- marcosdumay 10y agoYeah, but if you had 5 operations in Haskell, you'd write: 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.
- gh02t 10y agoDo-notation is sequenced evaluation, where the result of each statement is passed as the argument to the next (a simplification, but good enough). In this case there are two sequential operations and if either of them fails then it automatically propagates and results in the whole sequence returning a value that indicates failure. In Python, an (approximate) analog would be like this: 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.