jcpetruzza / barbies

BSD 3-Clause "New" or "Revised" License
92 stars 15 forks source link

Higher-order functors are defined in barbies, parameterized-utils, rank2classes, category-extras... #12

Open langston-barrett opened 5 years ago

langston-barrett commented 5 years ago

It looks like this library has some typeclasses that are also defined in parameterized-utils. I wonder if we could reduce the duplication of effort.

In particular:

jcpetruzza commented 5 years ago

They are indeed the same classes; and I just noticed that I could end up introducing equivalents of Data.Parameterized.{FunctorFC. TraversableFC} as well (see the proposed Functor2B and Traversable2B in https://github.com/jcpetruzza/barbies/issues/13#issuecomment-486418604).

I agree that it could be preferable to reduce duplication, if possible. Did you have anything in mind? From my point of view, of course, the simplest thing would be for parameterized-utils to re-export the definitions from barbies. Because of the slightly different naming convention this is a change that would induce a lot of breakage on users of parameterized-utils, so I'd understand if you this were not your favourite plan :slightly_smiling_face:.

But because of the default instances in FunctorB and TraversableB, I cannot just replace FunctorB with FunctorF, etc., so the alternative would be to move all the generic definitions to parameterized-utils and reexport FunctorF from barbies. Apart from the breakage due to naming, there'd be the issue of dependencies: currently barbies depends only on bifunctors (and since most was moved to base in ghc 8, I'm planning to drop that dependency as well). Depending on parameterized-utils would bring along lens, etc. and I'd rather keep the library lightweight.

langston-barrett commented 5 years ago

@jcpetruzza Thanks for the thoughtful reply! I guess I didn't have anything in mind, for the reasons you outline :smile:

The default instances look quite helpful, we do something somewhat similar for GADTs with template haskell.

It's worth noting that at least the Functor(B|F) class seems broadly appealing, it's defined in at least one more package: compdata (as HFunctor), and discussed in at least one blog post

I would think that the most generally useful solution would probably be to create a small package (e.g. higher-order-functors) and have both parameterized-utils and barbie depend on it. That package could contain both the default instances of barbies and some of the GADT TH stuff from parameterized-utils.

jcpetruzza commented 5 years ago

Thanks for the pointers! I think that HFunctor in compdata is actually a different beast, but the blog post you mention links to other packages that do define exactly the same notion, most notably rank2classes and conkin.

Now, all of conkin, rank2classes and barbies are a possible version of the proposed higher-order-functors package: they begin by defining a "higher-order functor" type-class and then build a hierarchy of sub-classes from there (Traversable, Applicative, etc). They are exploring the design space in various ways, making different trade-offs... and they agree only on Functor! (e.g., rank2classes and barbies agree on Traversable, but the latter has no Foldable class; rank2classes and conkin agree on Applicative, but the latter has no Apply, etc.).

So, let's say we were to start higher-order-functors package with Functor only, as you propose. Some things to keep in mind:

  1. To prevent a xkcd-927, all package maintainers should be on board beforehand, so some coordination would be needed.
  2. Because of this, there is a risk of death by bikeshedding :smile:
  3. Ultimately, Functor is not enough, people will end up depending on one package or the other for the rest of the hierarchy and I suspect that then we'd be more or less back to where we are now?
langston-barrett commented 5 years ago

@jcpetruzza I just noticed that there's some conceptual duplication between FunctorF and FunctorFC in the first place, see issue 41 on parameterized-utils.

lspitzner commented 4 years ago

So..

all licensed under BSD (2/3 clause).

I am to blame for the third in that list.

rank2classes came first, but for some reason now has a strange lower bound and no upper bound on base, and its repo is shared with some other packages.. but it does have the most comprehensive collection of rank2 classes. And the most sensible name for this kind of package, to be honest.

langston-barrett commented 3 years ago

For posterity, here are even more instances of higher-order functors

masaeedu commented 2 years ago

Not only are the Functor(B|F) classes duplicated, we also have duplication between Functor(T|FC). For the latter, there's heavy duplication with https://hackage.haskell.org/package/sop-core-0.5.0.1/docs/Data-SOP-Classes.html as well.

masaeedu commented 2 years ago

@jcpetruzza @langston-barrett I'd be super interested in working on factoring out a common core here, but as @jcpetruzza mentioned, a high level plan for something that would work in both libraries is needed.

But because of the default instances in FunctorB and TraversableB, I cannot just replace FunctorB with FunctorF, etc., so the alternative would be to move all the generic definitions to parameterized-utils and reexport FunctorF from barbies.

Another alternative would be to drop the defaults, create a newtype like GBarbie :: (k -> Type) -> Type, and attach all the generically derived instances for <shared-higher-order-functor-classes-library> to that. The user for their part can derive instances for their HKDs via that newtype. Would that approach make any sense? It would of course be a tremendous breakage, but there's ways to mitigate the scope of the problem with naming tricks.

jcpetruzza commented 2 years ago

@masaeedu Having a newtype wrapper to use with deriving-via would be nice, independently of the larger issue. I think I ran into some issues when I tried it some time ago, but I don't remember the details. If you make it work, that'd be great :)

masaeedu commented 2 years ago

@jcpetruzza Might it have been the TraversableB typeclass? Traversable has issues being derived via a newtype, and I would anticipate so does TraversableB. That said, if you're ok with tweaking the definition of TraversableB to make it weaker (as seen in the linked answer), we can do that and add a newtype wrapper via which you can get all the relevant instances.