Monads for effects are familiar to the Haskell programmer; they were introduced by Wadler's "Monads for functional programming" and are the accepted way to work with side effects in a lazy language.

Note that managing side effects is necessary in a call-by-need language: calling print : String -> () twice with the same value would not execute the print procedure twice; output would not be printed twice.

Wadler's approach was brilliant and timely. Nearly all I/O in a typical Haskell program is done via the monadic interface. This is why monads are famously associated with Haskell: it is hard to imagine doing I/O in a lazy language without reinventing the monad.

Nevertheless, there remain some practical limitations to Haskell's IO type.

# Laziness & Streaming FFI

There are several unsatisfying tidbits w.r.t. C FFI:

• The unsafePerformIO function is justified, for instance, to compute $${n \choose k }$$. We need to free memory allocated during computation; however, the result can be re-used. There is no need to perform its effects each time as there would be with putStrLn, for instance.

The requirements here are idiosyncratic: one must perform all clumped effects together or not at all, and one can reuse the result without performing the effects again.

• As I have pointed out before, one would like to wrap a streaming C FFI and provide a streaming API based on laziness. In such situations, one would use unsafeIOToST or unsafeInterleaveIO.

The requirements here are different: one would like to stream ByteStrings, with allocations (a side effect) occuring only as-needed, based on forcing lazy values. Again, effects need to be clumped together.

However, unsafeInterleaveIO is pathological. Unlike unsafePerformIO, where it is easy to know when you want to use it, I do not know how to rigorously reason about my use of unsafeInterleaveIO. However, it is now the only way to wrap a C API with a Haskell API that would be the same as something written in pure Haskell.

As such, the problem of streaming from a C API in a lazy language remains unclarified.

# Philosophical Foundations

There are some philosophical objections to Haskell's IO type: As pointed out by Wadler in Linear types can change the world!, one can duplicate or discard the RealWorld:

kill : RealWorld -> ()dupl : RealWorld -> (RealWorld, RealWorld)

Moreover, it is not clear that all effecting functions need take place in the IO monad: perhaps one could use a CFFI token for interacting with an appropriate C API (say, for streaming decompression) and another for console output.

# Confusion

## mtl-Replacements

Some Haskell libraries (and even academic papers) claim to offer "effects" - but are really more like mtl replacements.

None address the C FFI cases above, and in any case GHC still only accepts FFI bindings through IO.

## Algebraic Effects

Some languages (ATS, Kitten, Mirth, Idris, PureScript) track effects at the type level, but they do not inform the above problems around laziness. In fact, ATS does not require a token to be passed around for effects and does not use monads at all.