简体   繁体   中英

Are there valid reasons to build monad transformer stacks on top of IO?

IO is tricky in Haskell. Threads, FFI, async exceptions, laziness, you name it.

And then we have MonadIO which allows layering monadic stacks with IO at the bottom. Since IO actions can do arbitrary things, what is the value of building monadic stacks on top of such shaky foundations?

Why does it exist? Why not build a custom monad IO if you really need to perform arbitrary side effects?

It's a common approach these days to use the ReaderT design pattern .

You are correct to be wary of putting other transformers on top of IO, and the above linked blog post explains some of those reasons.

However, ReaderT provides a nice little convenience when it comes to passing "common app config" around to all of your functions.

what is the value of building monadic stacks on top of such shaky foundations?

Transformers on top of IO might let you talk more conveniently about sequences of repeated actions (hence the various streaming libraries ).

They might also help with the required bookkeeping of externally allocated resources .

The "wildness" of IO is not a general objection to stacking transformers on top of it, since they can help you to avoid repetitive code and make the essential logic clearer.

Rather, the argument is that IO already provides some built-in functionality for handling errors ( exceptions ) and mutable references ( IORef s, MVars ...) so adding transformers for that already existing functionality might be overkill.

One argument for mutable references in particular is that state maintained by "pure" means vanishes when an exception pops up, and that might not be what you want. You can also access mutable references from multiple threads.

MonadIO can be useful when implementing functions for a type that is not an instance of MonadIO .

newtype FooMonad a
  = FooMonad (StateT Int IO a)
  deriving (Functor, Applicative, Monad)

doFoo :: FooMonad String
doFoo = FooMonad $ liftIO getLine

You can use the MonadIO instances of StateT Int and IO to define "primitive" FooMonad actions conveniently. Others using your module are limited to just the primitives you choose to export.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM