Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Large dependency footprint. Split into separate packages? #68

Open
dminuoso opened this issue Oct 15, 2020 · 6 comments
Open

Large dependency footprint. Split into separate packages? #68

dminuoso opened this issue Oct 15, 2020 · 6 comments

Comments

@dminuoso
Copy link

Right now the presence of the aeson and byte* dependencies makes for a sizable footprint of ip. This to me makes ip an uninteresting candidate for use in libraries that just need to carry IP or Mac data around, but may not even have to parse them (or it parses them directly off a wire protocol).

The typical choice seems to be, to provide a package ip-core that exposes only the basic types/bindings/instances, and then ip as a wholesale meal with orphan instances and parser utilities. This is similar to the yesod-ip package.

Aside from bumping a major version to satisfy PVP, this should have no other impact on users.

What do you think? I'd be willing to do the work to get it done.

@andrewthad
Copy link
Member

I'm am somewhat against this change because I don't think the benefit from this is worth the cost. In libraries where I need to represent, for example, IPv4 addresses, but I don't need any of the machinery from ip, I typically just use a Word32. But let's consider how this would need to be set up, just in case the tide ever changes. We would need:

  • ip-core (data types, instances for things from base, probably basic stuff like smart constructors and operations with masks)
  • ip-platform (parsers and builders for types from text and bytestring)
  • ip-aeson (orphan json typeclass instances, depends on ip-platform)
  • ip-bytes (stuff with the byte* libraries)
  • ip (Reexports everything include orphan json instances)

I think that's how it would get split up if we were going to do this. But at the moment, I'm not convinced that this is really worth doing.

@dminuoso
Copy link
Author

This library has 75 transitive dependencies, roughly ~50 of which are from aeson alone.

haskell-ip dependency footprint

This drives up compilation time, makes auditing transitive updates harder, increases build plan complications (as a nix user this regularly affecfts me), and has quite a hefty penalty on object size in the absence of split sections + stripping. And it might deter some users.

A simple IP library should not have some "batteries-included" type of footprint like a web framework.

@chessai
Copy link
Member

chessai commented Jun 27, 2022

This library has 75 transitive dependencies, roughly ~50 of which are from aeson alone.

haskell-ip dependency footprint

This drives up compilation time, makes auditing transitive updates harder, increases build plan complications (as a nix user this regularly affecfts me), and has quite a hefty penalty on object size in the absence of split sections + stripping. And it might deter some users.

A simple IP library should not have some "batteries-included" type of footprint like a web framework.

I've been writing rust for a while now and one thing their packages have, which I wish Haskell's did, is feature flags. Feature flags can be on or off by default. In some cases, feature flags can be used to distinguish between async runtimes (example). They're a very versatile feature with a high power-to-weight ratio and smooth UX. In Haskell we have CPP flags, which are more annoying to use, but get the job done. In short, I think these features should be behind CPP flags, and be on or off by default depending on what's reasonable. They're easy enough to override with cabal, stack, and nix. You won't get caching with nix this way, though, unless nixpkgs or haskell.nix start doing something with combinations of flags (doubtful), or unless you start hosting your own infra (which I have done many times).

Splitting stuff out into multiple packages achieves the same effect but with more burden on maintainers, IMO.

@dminuoso
Copy link
Author

dminuoso commented Jun 29, 2022

So this is an interesting proposition. The main downfall is that flags cannot be specified in .cabal files, they are cabal.project settings. Because of this aeson and byte* would have to be enabled by default. Also, any user who does disable these should probably make sure it builds with these flags enabled as well, but that's just for their sake.

Added benefit is that it has no impact on all existing users. Thoughts @andrewthad?

I'd be willing to implement this.

@chessai
Copy link
Member

chessai commented Jun 29, 2022

So this is an interesting proposition. The main downfall is that flags cannot be specified in .cabal files, they are cabal.project settings. Because of this aeson and byte* would have to be enabled by default. Also, any user who does disable these should probably make sure it builds with these flags enabled as well, but that's just for their sake.

Added benefit is that it has no impact on all existing users. Thoughts @andrewthad?

I'd be willing to implement this.

To my knowledge, cabal.project just creates the relevant arguments for Setup.hs, though without the cabal.project I believe that you must specify flags at the command line with eg --constraint='pkg +flag'.

However I don't really think this is much of a downside, cabal.projects are so useful, to the point I use them in most of my projects and I wish they were just part of .cabal, and stack and nix already support this option without additional files.

@andrewthad
Copy link
Member

I'm fine with a feature flag that removes the aeson dependency (and all related instances). If anyone wants to implement this, I would accept a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants