Skip to main content

Is the benefit of the IO monad pattern for handling side effects purely academic? [Resolved]

Sorry for yet another FP + side effects question, but I couldn't find an existing one which quite answered this for me.

My (limited) understanding of functional programming is that state/side effects should be minimised and kept separate from stateless logic.

I also gather Haskell’s approach to this, the IO monad, achieves this by wrapping stateful actions in a container, for later execution, considered outside the scope of the program itself.

I’m trying to understand this pattern, but actually to determine whether to use it in a Python project, so want to avoid Haskell specifics if poss.

Crude example incoming.

If my program converts an XML file to a JSON file:

def main():
    xml_data = read_file('input.xml')  # impure
    json_data = convert(xml_data)  # pure
    write_file('output.json', json_data) # impure

Isn’t the IO monad’s approach effectively to do this:

steps = list(
    read_file,
    convert,
    write_file,
)

then absolve itself of responsibility by not actually calling those steps, but letting the interpreter do it?

Or put another way, it’s like writing:

def main():  # pure
    def inner():  # impure
        xml_data = read_file('input.xml')
        json_data = convert(xml_data)
        write_file('output.json', json_data)
    return inner

then expecting someone else to call inner() and saying your job is done because main() is pure.

The whole program is going to end up contained in the IO monad, basically.

When the code is actually executed, everything after reading the file depends on that file’s state so will still suffer from the same state-related bugs as the imperative implementation, so have you actually gained anything, as a programmer who will maintain this?

I totally appreciate the benefit of reducing and isolating stateful behaviour, which is in fact why I structured the imperative version like that: gather inputs, do pure stuff, spit out outputs. Hopefully convert() can be completely pure and reap the benefits of cachability, threadsafety, etc.

I also appreciate that monadic types can be useful, especially in pipelines operating on comparable types, but don’t see why IO should use monads unless already in such a pipeline.

Is there some additional benefit to dealing with side effects the IO monad pattern brings, which I’m missing?


Question Credit: Stu Cox
Question Reference
Asked December 5, 2018
Posted Under: Programming
7 views
Your Answer
D:\Adnan\Candoerz\CandoProject\vQA