Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Not as an "escape hatch" (that would be something more like `unsafePerformIO :: IO a -> a`), but as a principled way to compose (among other things) IO actions.

A Haskell program executes the IO action at `Main.main`, which must have type `IO ()`.

`putStrLn :: String -> IO ()` is a pure function - if you give it the same input, it always the same IO action as a result.



Monads are (for better or worse) contagious. So when one function calls a monad, then it needs to be included in the monad as well. It makes introducing memoisation to a file, randomisation, and memoisation not-to-a-file (without using lazy evaluation to express it) difficult. I don't know whether the alternatives (effect systems for instance) help with this.

I personally don't use functional languages because I find them too difficult given the needs and interests I have. I think about computations sequentially most of the time.


But then monads are a way to think about computations sequentially. If I write highly sequential code in a C-like language, in many cases most of the code is just boilerplate made necessary by the absence of native support for monads:

    int ret = doStepOne();
    if (ret == RESULT_OK) {
        ret = doStepTwo();
    }
    if (ret == RESULT_OK) {
        ret = doStepThree();
    }
    return ret;
would just be

    doStepOne() >>= doStepTwo() >>= doStepThree()
in a language with support for monads.


You can have support for monads without using them for IO.


Separating IO is kind of the killer feature, something like `rand()` is not an int unless you're reading dilbert[1] or xkcd[2]. Basic equations break down in the face of an Int-valued IO-action being conflated with an Int

[1] https://dilbert.com/search_results?terms=Random+Number+Gener... [2] https://xkcd.com/221/


Monads themselves aren't really contagious, it's the actions that would otherwise have side-effects that are, and also the fact they have to be executed in sequence.

This is also true for other things in other paradigms, such as async functions in javascript.

This is a good thing, however. In imperative programming, you have invisible temporal coupling. In pure-FP you have the same coupling, but it's exposed.


While I broadly agree with you, I think the post you're responding to has a point. You can't, in general, get a value back out of a monad, so if you call a monadic function, you may well have to return a monad. The obvious example is IO: there's no (safe) way get the `a` from `IO a`, so IO is kinda contagious.

Then again, there are lots of monads, such as `Maybe` and `List`, where you can get values out. These aren't contagious at all.

I agree with you that this is a good thing. Effects show up in the type signature - and it's all about those effects and managing them.


Yes, this is what I mean. IO is contagious for reasons unrelated to it being a Monad.


I think that applicatives and monads feel more contagious than they really are, because at first people tend to write functions that consume values of type `f a` too readily. This is because it takes some time to become comfortable with `fmap` and friends, so the new Haskell programmer often doesn't write as many pure functions.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: