Alex Rivera | Logout

Is Python's "with" monadic?

Asked 2011-08-20T09:53:31.020
38

Like many a foolhardy pioneer before me, I'm endeavoring to cross the trackless wasteland that is Understanding Monads.

I'm still staggering through, but I can't help noticing a certain monad-like quality about Python's with statement. Consider this fragment:

with open(input_filename, 'r') as f:
   for line in f:
       process(line)

Consider the open() call as the "unit" and the block itself as the "bind". The actual monad isn't exposed (uh, unless f is the monad), but the pattern is there. Isn't it? Or am I just mistaking all of FP for monadry? Or is it just 3 in the morning and anything seems plausible?

A related question: if we have monads, do we need exceptions?

In the above fragment, any failure in the I/O can be hidden from the code. Disk corruption, the absence of the named file, and an empty file can all be treated the same. So no need for a visible IO Exception.

Certainly, Scala's Option typeclass has eliminated the dreaded Null Pointer Exception. If you rethought numbers as Monads (with NaN and DivideByZero as the special cases)...

Like I said, 3 in the morning.

Edit
Report

1 Answer

9

Haskell has an equivalent of with for files, it's called withFile. This:

with open("file1", "w") as f:
    with open("file2", "r") as g:
        k = g.readline()
        f.write(k)

is equivalent to:

withFile "file1" WriteMode $ \f ->
  withFile "file2" ReadMode $ \g ->
    do k <- hGetLine g
       hPutStr f k

Now, withFile might look like something monadic. Its type is:

withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r

right side looks like (a -> m b) -> m b.

Another similarity: In Python you can skip as, and in Haskell you can use >> instead of >>= (or, a do block without <- arrow).

So I'll answer this question: is withFile monadic?

You could think that it can be written like this:

do f <- withFile "file1" WriteMode
   g <- withFile "file2" ReadMode
   k <- hGetLine g
   hPutStr f k

But this doesn't type check. And it cannot.

It's because in Haskell the IO monad is sequential: if you write

do x <- a
   y <- b
   c

after a is executed, b is executed and then c. There is no "backtrack" to clean a at the end or something like that. withFile, on the other hand, has to close the handle after the block is executed.

There is another monad, called continuation monad, which allows to do such things. However, you have now two monads, IO and continuations, and using effects of two monads at once requires using monad transformers.

import System.IO
import Control.Monad.Cont

k :: ContT r IO ()
k = do f <- ContT $ withFile "file1" WriteMode 
       g <- ContT $ withFile "fil
answered 2011-08-21T13:24:08.637

Your Answer