Organize for minimal imports.
To make it simple for clients to use the library (and to try to encourage a particular usage pattern), we should organize it so a user almost always needs only a single import per file. Here are the three use cases I see for different parts of a program:
when defining algebras
- algebra types
- Recursive/Corecursive/FunctorT/TraverseT types
- project/embed/map/traverse
when using algebras
- AlgebraOps
- folds/unfolds/refolds
- Possibly rec/Corecursive/etc instances
- Distributive laws
when creating recursive data
- fixpoint types & their rec/Corecursive/... Instances
For each of these cases, there should be two imports – one that provides everything via “ops” and one that provides package methods.
There is a fourth category – when defining pattern functors. This sometimes overlaps with the first category (you define algebras in the same file you define your pattern functors), but often doesn’t. I don’t think there’s much in it, but at least Delay, so instances for Delay[Equal, F], etc. can be defined in the functor’s companion object.