129 lines
6.1 KiB
Markdown
129 lines
6.1 KiB
Markdown
|
My
|
||
|
[previous blog post](https://www.snoyman.com/blog/2018/01/drop-conduits-finalizers)
|
||
|
discussed a possible upcoming breaking change to the conduit library:
|
||
|
dropping finalizers. This is one of a number of other breaking changes
|
||
|
I have planned. Another one is switching over from `MonadBaseControl`
|
||
|
to `MonadUnliftIO`, for reasons I've
|
||
|
[discussed](https://www.fpcomplete.com/blog/2017/06/tale-of-two-brackets)
|
||
|
[at](https://www.fpcomplete.com/blog/2017/06/readert-design-pattern)
|
||
|
[length](https://www.fpcomplete.com/blog/2017/07/announcing-new-unliftio-library)
|
||
|
[before](https://www.fpcomplete.com/blog/2017/07/the-rio-monad) and
|
||
|
[spoken](https://www.snoyman.com/reveal/monad-transformer-state)
|
||
|
[about](https://www.youtube.com/watch?v=KZIN9f9rI34) too.
|
||
|
|
||
|
Beyond this change, I have a number of others planned out as well,
|
||
|
some more solidly than others. I've started a document
|
||
|
[describing some of these](https://github.com/snoyberg/codename-karka/#refactor-michaels-existing-libraries-to-match),
|
||
|
and I wanted to bring up one point in this design space for some user
|
||
|
feedback: conduit dependency trees.
|
||
|
|
||
|
## Today
|
||
|
|
||
|
The situation today is that we have a dependency graph that looks
|
||
|
something like the following:
|
||
|
|
||
|
* `resourcet` is at the base of the hierarchy, and defines some
|
||
|
non-conduit-specific types and functions used throughout the conduit
|
||
|
ecosystem. It currently depends on a number of packages, like
|
||
|
`monad-control`, but that number will naturally drop as we move over
|
||
|
to `MonadUnliftIO` exclusively.
|
||
|
* `conduit` is designed to provide basic conduit functionality with
|
||
|
fewer dependencies. It _does_ depend on `resourcet`, and packages
|
||
|
like `monad-control`. But it does not depend on `bytestring`,
|
||
|
`text`, or `vector`, even though these are almost always wanted with
|
||
|
conduit. It provides the `Data.Conduit.List` set of combinators,
|
||
|
which are not the best ones out there.
|
||
|
* `conduit-extra` adds _lots_ of dependencies, including things like
|
||
|
`attoparsec`, and provides a nicer set of helpers around
|
||
|
`bytestring` and `text`.
|
||
|
* And finally, at the top of the tree (or our tree for today), we've
|
||
|
got `conduit-combinators`, which provides the combinators I actually
|
||
|
recommend people use in the `Data.Conduit.Combinator` module. This
|
||
|
has lots of dependencies, since it inherits from `conduit-extra` and
|
||
|
also adds in some extra things like `mwc-random`.
|
||
|
|
||
|
Benefits:
|
||
|
|
||
|
* You can use `resourcet` without touching the conduit ecosystem at all
|
||
|
* You can use `conduit` without pulling in lots of resources
|
||
|
* `Data.Conduit.Combinators` is fully loaded
|
||
|
|
||
|
Downsides:
|
||
|
|
||
|
* The current dependency footprint even at the base is higher than I'd
|
||
|
like, though that's getting fixed soon regardless.
|
||
|
* The `conduit` package is not super useful on its own due to lack of
|
||
|
`bytestring`, `text`, and `vector` support.
|
||
|
* To get the functionality you want in either `conduit-extra` or
|
||
|
`conduit-combinators`, you end up with a _much_ larger dependency
|
||
|
footprint.
|
||
|
|
||
|
## Plans for the next version
|
||
|
|
||
|
I have a number of different ideas in mind. I'll start off with the
|
||
|
most conservative plan, and mention some variants below.
|
||
|
|
||
|
* As already mentioned, `resourcet` drops a bunch of
|
||
|
dependencies. Nothing too interesting there.
|
||
|
* `conduit` adds a dependency on `bytestring`, `text`, and `vector` as
|
||
|
basic libraries everyone should be using anyway. We move over
|
||
|
`Data.Conduit.Combinators` and provide most of its functionality in
|
||
|
`conduit` itself, and start recommending against
|
||
|
`Data.Conduit.List`, `Data.Conduit.Binary`, and `Data.Conduit.Text`.
|
||
|
* `conduit-extra` basically remains as-is
|
||
|
* `conduit-combinators` retains the extra functionality not present in
|
||
|
the new `conduit`
|
||
|
|
||
|
Benefits:
|
||
|
|
||
|
* The `conduit` package now provides most of the functionality you'll
|
||
|
want on a day-to-day basis
|
||
|
* The dependency footprint for the `Data.Conduit.Combinators` module
|
||
|
is much reduced
|
||
|
* We can finally get away from the not-well-named functions in
|
||
|
`Data.Conduit.List`
|
||
|
|
||
|
There aren't necessarily _downsides_ to this approach, as I think it's
|
||
|
simply better than what we have today already. But I want to list out
|
||
|
the alternatives, which will make clear some things that could be
|
||
|
possibly better still.
|
||
|
|
||
|
* What do we do with the `mono-traversable` package? It's currently a
|
||
|
dependency of `conduit-combinators`, and the simplest path forward
|
||
|
for the above is to make `conduit` depend on
|
||
|
`mono-traversable`. However, this is a slightly heavier dependency
|
||
|
footprint, requiring adding in `unordered-containers` and
|
||
|
`vector-algorithms`. Alternatives:
|
||
|
* Strip down `mono-traversable` to have less deps
|
||
|
* Redefine parts of `mono-traversable` needed for `conduit` in
|
||
|
`conduit` itself
|
||
|
* Going crazy: _really_ move `mono-traversable` into `conduit` and
|
||
|
swap the dependency tree around
|
||
|
* My inclination: minimize `mono-traversable`'s dependencies a bit
|
||
|
more (like dropping the `split` package, and _maybe_
|
||
|
`vector-algorithms`) and make it a dependency of `conduit`.
|
||
|
* Do we really need `conduit-combinators` as well as `conduit-extra`?
|
||
|
It's just adding a few extra pieces of functionality over
|
||
|
`conduit-extra`, and perhaps those should be folded into
|
||
|
`conduit-extra` itself.
|
||
|
* Some people may not like the heavier dep footprint of `conduit`
|
||
|
now. Should we split off a `conduit-core` package providing the core
|
||
|
data types, functions, and operators, and have `conduit` depend on
|
||
|
that?
|
||
|
* It feels almost silly to have the `ResourceT` data type live in a
|
||
|
separate package.
|
||
|
* If we have `conduit-core`, that could be a logical place to put
|
||
|
it, since it won't have any extra dependencies versus the
|
||
|
`resourcet` package itself, and then we can turn `resourcet`
|
||
|
into a backwards compatibility layer.
|
||
|
* Or it may be logical to place `ResourceT` in the `unliftio-core`
|
||
|
package, since both concepts help with resource cleanup in monad
|
||
|
transformers. The former is necessary for continuation-based
|
||
|
monads, while the latter (`MonadUnliftIO`) works for simpler
|
||
|
monads.
|
||
|
|
||
|
If people have feedback, I'm happy to hear about it. I've spent an
|
||
|
unfortunate amount of time bouncing around between these different
|
||
|
options, so hopefully writing it all down and hearing some outside
|
||
|
opinions can help move this forward.
|