News for hackers. Stuff that matters.
Re:If monads are the solution, what is the problem? by tathougies on Monday July 30, 2018 @04:43PM (#17647842)
In Haskell, the 'if (err) { ... }' parts could be factored out into a new bind operator for a new monad stack. Then, you don't have to write these checks every single time. The do notation is automatically converted into the corresponding monadic code, and the algebraic monad laws guarantee that the meaning of the code is preserved.

For example, for the `if (err) {...}` cases, this is just the `ExceptT` monad transformer.

With regard to understandability, using error throwing code in Haskell is really easy. Implementing it is slightly more difficult, but so is implementing a C compiler. I mean, we shouldn't judge the utility of a thing based on whether the implementation of the thing could be understood by a child. By that measure, children would be incapable of using facebook, because they could not build it.

Re:If monads are the solution, what is the problem? by xamuel on Monday July 30, 2018 @05:58PM (#17648494)
So what would the above code actually look like, in your world?

>you don't have to write these checks every single time

But in real-world applications, the way you react to errors would differ from function to function. Some functions would print a notification to a logfile and return early. Some would terminate the program and send an email to an admin. Some would activate a red "offline" indicator somewhere in a program.

Re:If monads are the solution, what is the problem? by tathougies on Monday July 30, 2018 @07:06PM (#17649013)
In haskell? I'd construct an API that could be used like the following. This looks like the code we write at the place i work at:

     runService=
       s <- note SocketConnectionError $ socket AF_INET ...
       note CouldNotListen $ listen s 5
       note CouldNotBind $ bind s ...
       
       forever $ do
          client <- note CouldNotAccept $ accept s
          response <- serveRequest client
          ...
The signatures of socket, bind, etc, would look like (for example):

    socket :: ExceptT SocketError IO Int
    listen :: Int -> Int -> ExceptT ListenError IO ()
Then, at top level

    main = runExceptT runService >>=
           either handleError pure

    handleError (SocketConnectionError rsn) = <try again, maybe>
    handleError ...
The key here is that error handling is factored out. The 'runService' function expresses concisely all the steps taken, and the entire concern of error handling is factored out into the 'handleError' function, whose behavior I can actually change based on what I may want. In a C implementation, changing error handling strategies, would mean passing some kind of flag in (you could rebuild exceptions using setjmp/longjmp, but meh).