Monadic Code In Haskell

@prophet

Here’s another point I’ve gotten wrong. The Haskell version, if you ignore the usual “10 lines of extensions, 25 lines of imports” stuff Haskell code often incorporates (and here to a very tiny degree), is actually very slightly smaller than the Python version.

@atravers

To clarify, I prefer restricted point-free, i.e, I think point-free can easily become unreadable when the chain gets too long, but when used in moderation it’s beneficial.

If, say, the problem is “get two file names on the input prompt, try to compare the files represented, and return success if they’re the same and failure if not”, my actual preference would be:

import System.IO (readFile')

main :: IO ()
main = do
    file1 <- getLine >>= readFile'
    file2 <- getLine >>= readFile'

    putStrLn
        $ if file1 == file2
            then "Success"
            else "Failure"

This is a blend of the readability of #1 and the concision of #2.

As a limit of where I think point-free starts falling apart:

apiGet :: IO ()
apiGet = do
    res <- getChar 
    	>>= parseRequest . \case
            '1' -> urls !! 0
            ___ -> urls !! 1
        >>= httpLBS

    traverse_ putStrLn $ responseBodyBlock res
  where
    urls =
      [ "https://jsonplaceholder.typicode.com/todos/1"
      , "https://api.example.com/data"
      ]

    responseBodyBlock resource =
      [ "Response body:"
      , unpack $ getResponseBody resource
      , "OK1"
      ]

Just a snippet, a refactor of someone else’s code. Someone suggested me to take out do entirely, and turn it into a purely monadic pipeline.

But for me, it’s obvious that, even if I’m point-freeing to avoid some unnecessary bindings, the “get data” and “print data” are two separate steps, and just directly pipelining the traverse would make it significantly harder for me to read.

If this were to be turned into a pure pipeline, it’d follow the #4 model, where the steps are clearly delineated on the top-level, then have their implementation specified in the where clause.

And I’m already uncomfortable with the pipeline; it is at the maximum level I can tolerate. Anything more, I’d stuff a name on it and push it down to the where clause.


To summarize, then, I like, but do not push for, a semi-point-free style often mediated by do-notation.