proarrow: Category theory with a central role for profunctors

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

A library for doing category theory in Haskell with profunctors, rather than functors, as the central abstraction. Every Haskell kind carries at most one category structure (chosen via CategoryOf), newtype wrappers on kinds give variant categories, and functors are encoded as representable profunctors. On top of this the library provides monoidal structure, (co)limits, adjunctions, Kan extensions, promonads, and a full profunctor-optics hierarchy. . Import Proarrow to get started; Proarrow.Core explains the design of the core abstractions in depth. The public sublibrary proarrow:testing provides generic law-checking properties for testing your own categories. Rendered documentation is available at https://sjoerdvisscher.github.io/proarrow/.


[Skip to Readme]

Properties

Versions 0.1, 0.1.0.0
Change log CHANGELOG.md
Dependencies base (>=4.20 && <5), containers (>=0.6 && <0.9), data-default (>=0.7 && <0.9), falsify (>=0.4 && <0.5), fin (>=0.3.2 && <1), proarrow, tasty (>=1.4 && <1.6), tasty-falsify (>=0.1 && <0.2), universe-base (>=1.1.4 && <1.2), vec (>=0.5.1 && <1) [details]
License BSD-3-Clause
Author Sjoerd Visscher
Maintainer sjoerd@w3future.com
Category Math, Categories
Home page https://github.com/sjoerdvisscher/proarrow
Bug tracker https://github.com/sjoerdvisscher/proarrow/issues
Uploaded by SjoerdVisscher at 2026-09-28T12:48:48Z

library proarrow

Modules

  • Proarrow
    • Proarrow.Adjunction
    • Category
      • Proarrow.Category.Enriched
        • Proarrow.Category.Enriched.Dagger
        • Proarrow.Category.Enriched.Finitary
          • Proarrow.Category.Enriched.Finitary.Sheaf
          • Proarrow.Category.Enriched.Finitary.Topos
        • Proarrow.Category.Enriched.Quantale
        • Proarrow.Category.Enriched.Thin
          • Proarrow.Category.Enriched.Thin.Composition
      • Instance
        • Proarrow.Category.Instance.Bool
        • Proarrow.Category.Instance.Collage
        • Proarrow.Category.Instance.Constraint
        • Proarrow.Category.Instance.Coproduct
        • Proarrow.Category.Instance.Cospan
        • Proarrow.Category.Instance.Cost
        • Proarrow.Category.Instance.Discrete
        • Proarrow.Category.Instance.Duploid
        • Proarrow.Category.Instance.Fam
        • Proarrow.Category.Instance.FinHask
        • Proarrow.Category.Instance.FinRel
        • Proarrow.Category.Instance.FinSet
        • Proarrow.Category.Instance.Free
        • Proarrow.Category.Instance.Graph
        • Proarrow.Category.Instance.Hask
        • Proarrow.Category.Instance.IntConstruction
        • Proarrow.Category.Instance.Kleisli
        • Proarrow.Category.Instance.Linear
        • Proarrow.Category.Instance.Mat
        • Proarrow.Category.Instance.Monoid
        • Proarrow.Category.Instance.Nat
        • Proarrow.Category.Instance.Opposite
        • Proarrow.Category.Instance.Ordinal
        • Proarrow.Category.Instance.Paths
        • Proarrow.Category.Instance.PointedHask
        • Proarrow.Category.Instance.Product
        • Proarrow.Category.Instance.Prof
        • Proarrow.Category.Instance.Rel
        • Proarrow.Category.Instance.Rep
        • Proarrow.Category.Instance.Simplex
        • Proarrow.Category.Instance.Span
        • Proarrow.Category.Instance.Sub
        • Proarrow.Category.Instance.Unit
        • Proarrow.Category.Instance.ZX
        • Proarrow.Category.Instance.Zero
      • Proarrow.Category.Internal
      • Proarrow.Category.Monoidal
        • Proarrow.Category.Monoidal.Action
        • Proarrow.Category.Monoidal.Applicative
        • Proarrow.Category.Monoidal.Cartesian
        • Proarrow.Category.Monoidal.Closed
        • Proarrow.Category.Monoidal.Coclosed
        • Proarrow.Category.Monoidal.CompactClosed
        • Proarrow.Category.Monoidal.CopyDiscard
        • Proarrow.Category.Monoidal.Distributive
        • Proarrow.Category.Monoidal.EndoProf
        • Proarrow.Category.Monoidal.Hypergraph
        • Proarrow.Category.Monoidal.Rev
        • Proarrow.Category.Monoidal.StarAutonomous
        • Proarrow.Category.Monoidal.Strength
        • Proarrow.Category.Monoidal.Strictified
      • Proarrow.Category.Promonoidal
      • Proarrow.Category.Sheaf
      • Proarrow.Category.Topos
    • Proarrow.Colimit
      • Proarrow.Colimit.BinaryCoproduct
      • Proarrow.Colimit.Coequalizer
      • Proarrow.Colimit.Copower
      • Proarrow.Colimit.Initial
      • Proarrow.Colimit.NaturalNumbers
      • Proarrow.Colimit.Pushout
    • Proarrow.Core
    • Proarrow.Functor
    • Proarrow.Limit
      • Proarrow.Limit.BinaryProduct
      • Proarrow.Limit.Equalizer
      • Proarrow.Limit.Power
      • Proarrow.Limit.Pullback
      • Proarrow.Limit.Terminal
    • Proarrow.Monoid
    • Proarrow.Object
    • Proarrow.Optic
      • Proarrow.Optic.Action
      • Proarrow.Optic.AffineFold
      • Proarrow.Optic.AffineTraversal
      • Proarrow.Optic.Day
      • Proarrow.Optic.Fold
      • Proarrow.Optic.Getter
      • Proarrow.Optic.Glass
      • Proarrow.Optic.Grate
      • Proarrow.Optic.Iso
      • Proarrow.Optic.Kaleidoscope
      • Proarrow.Optic.Lens
      • Proarrow.Optic.MonoidalLens
      • Proarrow.Optic.MonoidalTraversal
      • Proarrow.Optic.PowerGrate
      • Proarrow.Optic.Prism
      • Proarrow.Optic.Prod
      • Proarrow.Optic.Setter
      • Proarrow.Optic.Sum
      • Proarrow.Optic.Tracer
      • Proarrow.Optic.Traversal
    • Proarrow.Optics
    • Proarrow.Path
    • Profunctor
      • Proarrow.Profunctor.Cofree
      • Proarrow.Profunctor.Corepresentable
      • Proarrow.Profunctor.Free
      • Instance
        • Proarrow.Profunctor.Instance.Adj
        • Proarrow.Profunctor.Instance.Arrow
        • Proarrow.Profunctor.Instance.Cocone
        • Proarrow.Profunctor.Instance.Composition
        • Proarrow.Profunctor.Instance.Cone
        • Proarrow.Profunctor.Instance.Constant
        • Proarrow.Profunctor.Instance.Coproduct
        • Proarrow.Profunctor.Instance.Costar
        • Proarrow.Profunctor.Instance.Coyoneda
        • Proarrow.Profunctor.Instance.Day
        • Proarrow.Profunctor.Instance.Direp
        • Proarrow.Profunctor.Instance.Edges
        • Proarrow.Profunctor.Instance.Exponential
        • Proarrow.Profunctor.Instance.Fix
        • Proarrow.Profunctor.Instance.Fold
        • Proarrow.Profunctor.Instance.HaskValue
        • Proarrow.Profunctor.Instance.Identity
        • Proarrow.Profunctor.Instance.Initial
        • Proarrow.Profunctor.Instance.List
        • Proarrow.Profunctor.Instance.PastroTambara
        • Proarrow.Profunctor.Instance.Product
        • Proarrow.Profunctor.Instance.Ran
        • Proarrow.Profunctor.Instance.Rift
        • Proarrow.Profunctor.Instance.Sieve
        • Proarrow.Profunctor.Instance.Star
        • Proarrow.Profunctor.Instance.Terminal
        • Proarrow.Profunctor.Instance.Wrapped
        • Proarrow.Profunctor.Instance.Yoneda
      • Proarrow.Profunctor.Representable
    • Proarrow.Promonad
      • Proarrow.Promonad.Cont
      • Proarrow.Promonad.Reader
      • Proarrow.Promonad.State
      • Proarrow.Promonad.Writer
    • Proarrow.Squares
    • Tools
      • Proarrow.Tools.CCC
      • Proarrow.Tools.DPO
      • Diagrams
        • Proarrow.Tools.Diagrams.Dot
        • Proarrow.Tools.Diagrams.Svg
      • Proarrow.Tools.Laws
    • Proarrow.Universal

library proarrow:testing

Modules

  • Proarrow
    • Proarrow.Testing
      • Proarrow.Testing.Laws
        • Proarrow.Testing.Laws.Run

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for proarrow-0.1

[back to package description]

Haskell-CI

proarrow

A Haskell library for doing category theory with a central role for profunctors.

Core ideas

One category per kind

Kind-indexed categories makes life a lot easier, once you know what the kind is of a type, you know which category it belongs to.

Use newtype wrappers on kinds

Using kind-indexed categories means you cannot share objects between categories. Newtype wrappers fix this. For example, if you have a category for kind k, it's opposite category has kind OP k.

Kind j -> k -> Type is reserved for profunctors

If profunctors would have kind OP j -> k -> Type, then (->) wouldn't be a profunctor as is. This would require too many wrappers all over the place. So instead j -> k -> Type is reserved for profunctors. So for the category of bifunctors we do need a wrapper.

Use constraints to limit which objects are part of a category

You need this already when creating a category of functors, then each object needs a Functor constraint. It turns out this is powerful enough to limit the objects of any type of category.

These constraints can be observed from arrows

If you're not careful these objects constraints can become unweildy, requiring a long list of object constraints for each function. But if you have an arrow from a to b, that's proof enough that a and b are objects. So there are functions (//) and (\\) to observe the constraints.

Functors that don't land in Type are written as representable profunctors

Functors have kind j -> k, but you can't just make a datatype of any kind, it must always be of the shape j -> k -> ... -> Type. So for example you can't make an identity functor that works for any k. But functors are isomorphic to representable profunctors, with kind k -> j -> Type. (Note that the kinds swap!) So you can write an identity representable profunctor!

Generalize the category theory to work with profunctors

To make working with representable profunctors instead of functors easier, the category theory should work with profunctors where possible.

Example: defining your own category

A category is picked out by its kind, so a new category starts with a fresh kind -- here one with two objects, a Draft and a Live state, and a single non-identity arrow publishing the one as the other.

{-# LANGUAGE TypeData, TypeFamilies #-}
import Prelude hiding (id, (.))

import Proarrow.Core (CAT, CategoryOf (..), ObId (..), Profunctor (..), Promonad (..), dimapDefault)

type data STATE = Draft | Live

type Move :: CAT STATE
data Move a b where
  KeepDraft :: Move Draft Draft
  Publish :: Move Draft Live
  KeepLive :: Move Live Live

deriving instance Show (Move a b)

-- 'id' has to produce the identity *at whichever object it is asked for*, so being an
-- object is exactly the ability to supply that identity:
instance ObId Draft where objId = KeepDraft
instance ObId Live where objId = KeepLive

instance CategoryOf STATE where
  type (~>) = Move

instance Promonad Move where
  KeepDraft . KeepDraft = KeepDraft
  Publish . KeepDraft = Publish
  KeepLive . Publish = Publish
  KeepLive . KeepLive = KeepLive

instance Profunctor Move where
  dimap = dimapDefault
  r \\ KeepDraft = r
  r \\ Publish = r
  r \\ KeepLive = r

Beyond TypeData and TypeFamilies above, going further needs more extensions — a Proarrow.Testing.TestableType instance for Move a b, for instance, also needs UndecidableInstances. Rather than discovering them one failed build at a time, enable the set the library itself is built with: GHC2024 plus the default-extensions block in proarrow.cabal.

>>> KeepLive . Publish . id
Publish

The Ob family is where the object constraints from above come in, and the \\ method is how those constraints are observed from an arrow — matching on a constructor reveals which objects it runs between. Note what is absent: no type Ob, and no id. Ob defaults to ObId, and id defaults to objId, so the two instances above are the whole of the object structure. A category where every type of the kind is an object with no evidence needed says type Ob a = Any a instead — that is what Hask does. Where the objects carry no non-identity arrows at all, reach for Proarrow.Category.Instance.Discrete's DISCRETE (see test/Props/Paths.hs), and a one-object category needs no dispatch at all, being just a monoid — Proarrow.Category.Instance.Monoid.

And now the generic kind-machinery applies: OPPOSITE STATE is the opposite category, (STATE, STATE) the product category, STATE +-> STATE are profunctors on states, and so on. The Proarrow module exports the curated core vocabulary; Proarrow.Core explains the design in depth, and the Proarrow.Category.Instance.* modules contain many more worked examples of categories.

To property-test the laws of your own category, depend on the public sublibrary proarrow:testing: a Testable instance for your kind plus the law checks from Proarrow.Testing.Laws (testCategory, testMonoidal, ...) give it a test suite — proarrow's own tests are built from exactly these pieces.

Laws as code

A class's laws are written down next to the class, as ordinary proarrow code that works in any category with the structure: a Laws instance from Proarrow.Tools.Laws, keyed by the list of structures the laws mention. For example, from Proarrow.Category.Monoidal:

instance Laws '[Monoidal] where
  laws =
    [ ...
    , Law "associator naturality" \ @a @b @c @d mor -> do
        f <- mor @a @b "f"
        g <- mor @b @c "g"
        h <- mor @c @d "h"
        associator @_ @b @c @d . ((f ** g) ** h) === (f ** (g ** h)) . associator @_ @a @b @c
    , ...
    ]

A law binds the object variables it uses and asks the supply mor for named arbitrary arrows between them, then states its equation with ===. testLaws from Proarrow.Testing.Laws.Run checks each law as its own property. It draws random objects and arrows, runs the law in a category whose arrows also describe themselves, and on failure prints both sides as the code they were built from:

Failed swap naturality:
swap . (f ** g) = ...
(g ** f) . swap = ...

testMonoidal, testClosed and the other checks for proarrow's own classes are built this way, and a class of your own can be checked the same way: test/Examples/CustomLaws.hs walks through a complete one.