4 ms·
your example went from readable to complete nonsense really quickly. Mind rewriting do y <- f x fy or annotating into something that m
by zalmoxes 10y ago
your 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.