I'm splitting off the `Managed`

type from the `mvc`

library into its own stand-alone library. I've wanted to use this type outside of `mvc`

for some time now, because it's an incredibly useful `Applicative`

that I find myself reaching for in my own code whenever I need to acquire resources.

If you're not familiar with the `Managed`

type, it's simple:

```
-- The real implementation uses smart constructors
newtype Managed a =
Managed { with :: forall r . (a -> IO r) -> IO r }
-- It's a `Functor`/`Applicative`/`Monad`
instance Functor Managed where ...
instance Applicative Managed where ...
instance Monad Managed where ...
-- ... and also implements `MonadIO`
instance MonadIO Managed where ...
```

Here's an example of mixing the `Managed`

monad with `pipes`

to copy one file to another:

```
import Control.Monad.Managed
import System.IO
import Pipes
import qualified Pipes.Prelude as Pipes
main = runManaged $ do
hIn <- managed (withFile "in.txt" ReadMode)
hOut <- managed (withFile "out.txt" WriteMode)
liftIO $ runEffect $
Pipes.fromHandle hIn >-> Pipes.toHandle hOut
```

However, this is not much more concise than the equivalent callback-based version. The real value of the `Managed`

type is its `Applicative`

instance, which you can use to lift operations from values that it wraps.

#### Equational reasoning

My previous post on equational reasoning at scale describes how you can use `Applicative`

s to automatically extend `Monoid`

s while preserving the `Monoid`

operations. The `Managed`

`Applicative`

is no different and provides the following type class instance that automatically lifts `Monoid`

operations:

`instance Monoid a => Monoid (Managed a)`

Therefore, you can treat the `Managed`

`Applicative`

as yet another useful building block in your `Monoid`

tool box.

However, `Applicative`

s can do more than extend `Monoid`

s; they can extend `Category`

s, too. Given any `Category`

, if you extend it with an `Applicative`

you can automatically derive a new `Category`

. Here's the general solution:

```
import Control.Applicative
import Control.Category
import Prelude hiding ((.), id)
newtype Extend f c a b = Extend (f (c a b))
instance (Applicative f, Category c)
=> Category (Extend f c) where
id = Extend (pure id)
Extend f . Extend g = Extend (liftA2 (.) f g)
```

So let's take advantage of this fact to extend one of the `pipes`

categories with simple resource management. All we have to do is wrap the pull-based `pipes`

category in a bona-fide `Category`

instance:

```
import Pipes
newtype Pull m a b = Pull (Pipe a b m ())
instance Monad m => Category (Pull m) where
id = Pull cat
Pull p1 . Pull p2 = Pull (p1 <-< p2)
```

Now we can automatically define resource-managed pipes by `Extend`

ing them with the `Managed`

`Applicative`

:

```
import Control.Monad.Managed
import qualified Pipes.Prelude as Pipes
import System.IO
fromFile :: FilePath -> Extend Managed (Pull IO) () String
fromFile filePath = Extend $ do
handle <- managed (withFile filePath ReadMode)
return (Pull (Pipes.fromHandle handle))
toFile :: FilePath -> Extend Managed (Pull IO) String X
toFile filePath = Extend $ do
handle <- managed (withFile filePath WriteMode)
return (Pull (Pipes.toHandle handle))
```

All we need is a way to run `Extend`

ed pipes and then we're good to go:

```
runPipeline :: Extend Managed (Pull IO) () X -> IO ()
runPipeline (Extend mp) = runManaged $ do
Pull p <- mp
liftIO $ runEffect (return () >~ p)
```

If we compose and run these `Extend`

ed pipes they just "do the right thing":

```
main :: IO ()
main = runPipeline (fromFile "in.txt" >>> toFile "out.txt")
```

Let's check it out:

```
$ cat in.txt
1
2
3
$ ./example
$ cat out.txt
1
2
3
```

We can even reuse existing pipes, too:

```
reuse :: Monad m => Pipe a b m () -> Extend Managed (Pull m) a b
reuse = Extend . pure . Pull
main = runPipeline $
fromFile "in.txt" >>> reuse (Pipes.take 2) >>> toFile "out.txt"
```

... and `reuse`

does the right thing:

```
$ ./example
$ cat out.txt
1
2
```

What does it mean for `reuse`

to "do the right thing"? Well, we can specify the correctness conditions for `reuse`

as the following functor laws:

```
reuse (p1 >-> p2) = reuse p1 >>> reuse p2
reuse cat = id
```

These two laws enforce that `reuse`

is "well-behaved" in a rigorous sense.

This is just one example of how you can use the `Managed`

type to extend an existing `Category`

. As an exercise, try to take other categories and extend them this way and see what surprising new connectable components you can create.

#### Conclusion

Experts will recognize that `Managed`

is a special case of `Codensity`

or `ContT`

. The reason for defining a separate type is:

- simpler inferred types,
- additional type class instances, and:
- a more beginner-friendly name.

`Managed`

is closely related in spirit to the `Resource`

monad, which is now part of `resourcet`

. The main difference between the two is:

`Resource`

preserves the open and close operations`Managed`

works for arbitrary callbacks, even unrelated to resources

This is why I view the them as complementary `Monad`

s.

Like all `Applicative`

s, the `Managed`

type is deceptively simple. This type does not do much in isolation, but it grows in power the more you compose it with other `Applicative`

s to generate new `Applicative`

s.

This comment has been removed by the author.

ReplyDelete