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 corralling side effects is necessary with laziness: sharing gives us efficient evaluation, but sharing a side-effecting result may change how often its effects are performed.
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
unsafePerformIOfunction is justified, for instance, to compute \( {n \choose k } \). We need to free memory allocated during computation; however, the result is constant and can be shared. There is no need to perform its effects each time as there would be withputStrLn, 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
unsafeIOToSTorunsafeInterleaveIO.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,
unsafeInterleaveIOis pathological. 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.
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. ATS does not require a token to be passed around for effects and does not use monads at all.
