Request for comment: functor-monad package

Good end-of-the-year everyone!

I’d like to introduce a category-theory-inspired library I’m making, and I want some feedbacks on it.

  • Haddock
    • The linked Haddock is not on Hackage. It’s locally built and manually uploaded by myself. It might go outdated in the future. (I know I should set up CI though…)
  • Repository (GitHub)

I’m particularly interested in how you think about these points I’m uncertain about.

  • Usefulness. I’m yet to find a substantial use of the type classes in my library, FFunctor or FMonad, in a practical programming task. Should I put it on Hackage despite not knowing the actual value to other users?
  • Documentation. The current documentation should have many errors and unfriendly explanations. In addition to my English ability, the math-y nature of this library make it hard for me to write good documentation. Any feedback on documentation is double welcome.

But any kind of comments are welcome!

3 Likes

Usefulness. I’m yet to find a substantial use of the type classes in my library, FFunctor or FMonad, in a practical programming task. Should I put it on Hackage despite not knowing the actual value to other users?

One package that wasn’t mentioned in the README was hkd: "higher-kinded data", which provides everything you do sans FMonad and a bit more for various SOP manipulations. I can’t speak to whether FMonad would be useful, but I haven’t seen many useful things in the wild that fit that abstraction. I’m interested to see if people have instances lying around that are actually nice to work with. I am a bit invested in this because I abandoned a particular sandbox a while ago (GitHub - emilypi/higher-functors: Higher-order Functors - feel free to crib anything and everything you care about) that was an attempt to see whether the higher order hierarchy was a useful concept in general, and I had to pull back on account of most “useful” things I worked with needing to be (k -> Type) -> Type as opposed to the full (k -> Type) -> k -> Type.

I’m stoked that we came up with the saem type signatures and laws for basically everything tho :grinning_face_with_smiling_eyes:

Documentation. The current documentation should have many errors and unfriendly explanations. In addition to my English ability, the math-y nature of this library make it hard for me to write good documentation. Any feedback on documentation is double welcome.

Currently, pretty good.

3 Likes

I believe you could generalize the kind of FFunctor and FMonad to:

((k -> Type) -> (k -> Type)) -> Constraint

But I think FStrong can’t be generalized in this way.

I do have a use case for k ~ Type -> Type; I’m hoping to write an ICFP’24 paper about it.

3 Likes

you could generalize

I could do, but once I thought that this less-general definition is fine because it has its merit. But thinking again may worth.

Citing README:

Comparison against similar type classes

(…)

  • From index-core: IFunctor, IMonad

    They are endofunctors on the category of type constructors of kind k -> Type and polymorphic functions t :: forall (x :: k). f x -> g x.

    While any instance of FFunctor from this package can be faithfully represented as a IFunctor, some instances can’t be an instance of IFunctor as is.
    Most notably, Free can’t be an instance of IFunctor directly,
    because Free needs Functor h to be able to implement fmapI, the method of IFunctor.

    class IFunctor ff where
      fmapI :: (g ~> h) -> (ff g ~> ff h)
    

    There exists a workaround: you can use another representation of Free f which doesn’t require Functor f to be a Functor itself,
    for example Program from operational package.

    This package avoids the neccesity of the workaround by admitting the restriction that the parameter of FFunctor must always be a Functor.
    Therefore, FFunctor gives up instances which don’t take Functor parameter, for example, a type constructor F with kind F :: (Nat -> Type) -> Nat -> Type.

Saying the same thing quickly, Any FFunctor ff can be encoded as IFunctor (ff ∘ Coyoneda). Many instances can have a more direct IFunctor ff instance, but some (like Free) can’t avoid the Coyoneda indirection.

My another justification of being less general is that a library focusing on a specific structure in detail is no worse than a library for generic things. Anyway, I think the “correct” direction for the ultimate generalization is to have the functor class between arbitrary categories (or higher-categories or …)

One of that specific detail is, like you said, FStrong ff. It makes sense only for a FFunctor ff. Not just because of its kind, but also for the fact Day f g makes sense only for Functor f and Functor g. A hypothetical “take-Functor-and-return-Contravariant-functor” with kind (Type -> Type) -> Type -> Type can well be IFunctor but it wants another class using other tensor products, not FStrong.

Or I’m just getting too fixated on the first idea I got.

3 Likes

Thank you, I’ll add the reference to hkd (and other libraries for (k -> Type) -> Type). I’ll read your code too!

Currently, pretty good.

I’m relieved

This looks interesting, but I don’t see the decisive advantage over mmorph yet. Maybe look for common usage patterns of mmorph and try to translate them to situations where your functor is not a monad. Although you’d have to prove then that mmorph is not applicable in these situations.

Yes, why not? It increases discoverability, and might eventually lead someone with a use case to your library.

I find your documentation great for a new package, and wish that other libraries also had this quality of documentation. For example, the tables comparing Functor vs. FFunctor and similar are great.

2 Likes