Reversible effects as inverse arrows

Reversible computing models settings in which all processes can be reversed. Applications include low-power computing, quantum computing, and robotics. It is unclear how to represent side-effects in this setting, because conventional methods need not respect reversibility. We model reversible effects by adapting Hughes' arrows to dagger arrows and inverse arrows. This captures several fundamental reversible effects, including serialization and mutable store computations. Whereas arrows are monoids in the category of profunctors, dagger arrows are involutive monoids in the category of profunctors, and inverse arrows satisfy certain additional properties. These semantics inform the design of functional reversible programs supporting side-effects.


Introduction
Reversible computing studies settings in which all processes can be reversed: programs can be run backwards as well as forwards. Its history goes back at least as far as 1961, when Landauer formulated his physical principle that logically irreversible manipulation of information costs work. This sparked the interest in developing This paper aims to inform design principles of sound reversible programming languages. The main contribution is to match desirable programming concepts to precise category theoretic constructions. As such, it is written from a theoretical perspective. To make examples more concrete for readers with a more practical background, we adopt the syntax of a typed first-order reversible functional programming language with type classes. We begin with preliminaries on reversible base categories (in Section 2).

Dagger categories and inverse categories
This section introduces the categories we work with to model pure computations: dagger categories and inverse categories. Each has a clear notion of reversing morphisms. Regard morphisms in these base categories as pure, ineffectful maps.

Definition 2.1
A dagger category is a category equipped with a dagger : a contravariant endofunctor C → C satisfying f † † = f for morphisms f and X † = X for objects X. A morphism f in a dagger category is: • positive if f = g † • g for some morphism g; A dagger functor is a functor between dagger categories that preserves the dagger, i.e. a functor F with F (f † ) = F (f ) † . A (symmetric) monoidal dagger category is a monoidal category equipped with a dagger making the coherence isomorphisms α X,Y,Z : X ⊗ (Y ⊗ Z) → (X ⊗ Y ) ⊗ Z ρ X : X ⊗ I → X λ X : I ⊗ X → X (and σ X,Y : X ⊗ Y → Y ⊗ X in the symmetric case) unitary and satisfying (f ⊗ g) † = f † ⊗ g † for morphisms f and g. We will sometimes suppress coherence isomorphisms for readability.
Any groupoid is a dagger category under f † = f −1 . Another example of a dagger category is Rel, whose objects are sets, and whose morphisms X → Y are relations R ⊆ X × Y , with composition S • R = {(x, z) | ∃y ∈ Y : (x, y) ∈ R, (y, z) ∈ S}. The dagger is R † = {(y, x) | (x, y) ∈ R}. It is a monoidal dagger category under either Cartesian product or disjoint union. Definition 2.2 A (monoidal) inverse category is a (monoidal) dagger category of partial isometries where positive maps commute: for all maps f : X → Y and g : X → Z.
Every groupoid is an inverse category. Another example of an inverse category is PInj, whose objects are sets, and morphisms X → Y are partial injections: R ⊆ X × Y such that for each x ∈ X there exists at most one y ∈ Y with (x, y) ∈ R, and for each y ∈ Y there exists at most one x ∈ X with (x, y) ∈ R. It is a monoidal inverse category under either Cartesian product or disjoint union. [11] if it is a symmetric monoidal dagger category with a natural transformation Δ X : X → X ⊗ X making the following diagrams commute:

Definition 2.3 A dagger category is said to have inverse products
These diagrams express cocommutativity, coassociativity, speciality and the Frobenius law.
Another useful monoidal product, here on inverse categories, is a disjointness tensor, defined in the following way (see [11]): Definition 2.4 An inverse category is said to have a disjointness tensor if it is equipped with a symmetric monoidal tensor product − ⊕ − such that its unit 0 is a zero object, and the canonical quasi-injections For example, PInj has inverse products Δ X : , and a disjointness tensor where X ⊕ Y is given by the tagged disjoint union of X and Y (the unit of which is ∅).
Inverse categories can also be seen as certain instances of restriction categories. Informally, a restriction category models partially defined morphisms, by assigning to each f : A → B a morphismf : A → A that is the identity on the domain of definition of f and undefined otherwise. For more details, see [6].

Definition 2.5
A restriction category is a category equipped with an operation that assigns to each f : A → B a morphismf : A → A such that: A restriction functor is a functor F between restriction categories with F (f ) = F (f ). A monoidal restriction category is a restriction category with a monoidal structure for which ⊗ : C × C → C is a restriction functor.
A morphism f in a restriction category is a partial isomorphism if there is a morphism g such that g • f =f and f • g =ḡ. Given a restriction category C, define Inv(C) to be the wide subcategory of C having all partial isomorphisms of C as its morphisms.
An example of a monoidal restriction category is PFn, whose objects are sets, and whose morphisms X → Y are partial functions: R ⊆ X × Y such that for each x ∈ X there is at most one y ∈ Y with (x, y) ∈ R. The restrictionR is given by Remark 2.6 Inverse categories could equivalently be defined as either categories in which every morphism f satisfies f = f •g •f and g = g •f •g for a unique morphism g, or as restriction categories in which all morphisms are partial isomorphisms [6,Theorem 2.20]. It follows that functors between inverse categories automatically preserve daggers and that Inv(C) is an inverse category.
It follows, in turn, that an inverse category with inverse products is a monoidal inverse category: because X ⊗− and −⊗Y are endofunctors on an inverse category, they preserve daggers, so that by bifunctoriality − ⊗ − does as well:

Arrows as an interface for reversible effects
Arrows are a standard way to encapsulate computational side-effects in a functional (irreversible) programming language [16,17]. This section extends the definition to reversible settings, namely to dagger arrows and inverse arrows. We argue that these notions are "right", by exhibiting a large list of fundamental reversible side-effects that they model. We start by recalling irreversible arrows.
that satisfy the following laws: where we use the functional programming convention to write A X Y for A(X, Y ) and X → Y for hom(X, Y ) The multiplicative fragment consists of above data except first, satisfying all laws except those mentioning first; we call this a weak arrow.

Definition 3.2
A dagger arrow is an arrow on a monoidal dagger category with an additional operation inv : A X Y → A Y X satisfying the following laws: arr(f † ) = inv(arr f ) (11) inv(first a) = first(inv a) (12) A inverse arrow is a dagger arrow on a monoidal inverse category such that: The multiplicative fragment consists of above data except first, satisfying all laws except those mentioning first.
Like the arrow laws (1)- (8), in a programming language with inverse arrows, the burden is on the programmer to guarantee (9)- (14) for their implementation. If that is done, the language guarantees arrow inversion.

Remark 3.4
Now follows a long list of examples of inverse arrows, described in a typed first-order reversible functional pseudocode with type classes, inspired by Theseus [20,19], the revised version of Rfun (briefly described in [22]), and Haskell.
Type classes are a form of interface polymorphism: A type class is defined by a class specification containing the signatures of functions that a given type must implement in order to be a member of that type class (often, type class membership also informally requires the programmer to ensure that certain equations are required of their implementations). For example, the Functor type class (in Haskell) is given by the class specification with the additional informal requirements that fmap id = id and fmap (g While higher-order reversible functional programming is fraught, aspects of this can be mimicked by means of parametrized functions. A parametrized function is a function that takes parts of its input statically (i.e., no later than at compile time), in turn lifting the first-order requirement on these inputs. To separate static and dynamic inputs from one another, two distinct function types are used: a → b denotes that a must be given statically, and a ↔ b (where a and b are first-order types) denotes that a is passed dynamically. As the notation suggests, functions of type a ↔ b are reversible. For example, a parametrized variant of the reversible map function can be defined as a function map : ). Thus, map itself is not a reversible function, but given statically any reversible function Given this distinction between static and dynamic inputs, the signature of arr becomes (X ↔ Y ) → A X Y . We will see later that Arrows on C can be modelled categorically as monoids in the functor category [C op × C, Set] [18]. Definition 3.1 uses the original signature, because this distinction is not present in the irreversible case. Fortunately, the semantics of arrows remain the same whether or not this distinction is made.

Example 3.5 (Pure functions)
A trivial example of an arrow is the identity arrow hom(−, +) which adds no computational side-effects at all. This arrow is not as boring as it may look at first. If the identity arrow is an inverse arrow, then the programming language in question is both invertible and closed under program inversion: any program p has a semantic inverse p † (satisfying certain equations), and the semantic inverse coincides with the semantics inv(p) of another program inv(p). As such, inv must be a sound and complete program inverter (see also [23]) on pure functions; not a trivial matter at all. [19] explicitly expose creation and erasure of information as effects. This type-and-effect system captures irreversible computation inside a pure reversible setting.

Example 3.6 (Information effects) James and Sabry's information effects
We describe the languages from [19] categorically, as there is no space for syntactic details. Start with the free dagger category (C, ×, 1) with finite products (and hence coproducts), where products distribute over coproducts by a unitary map. Objects interpret types of the reversible language Π of bijections, and morphisms interpret terms. The category C is a monoidal inverse category.
The category C carries an arrow, where A(X, Y ) is the disjoint union of hom(X × H, Y × G) where G and H range over all objects, and morphisms X × H → Y × G and X × H → Y × G are identified when they are equal up to coherence isomorphisms. This is an inverse arrow, where inv(a) is simply a † . It supports the following additional operations: James and Sabry show how a simply-typed first order functional irreversible language translates into a reversible one by using this inverse arrow to build implicit communication with a global heap H and garbage dump G. Such arrows can not be used as-is in inverse categories, however, as canonical examples (such as PInj) fail to be monoidal closed. To get around this, note that it follows from monoidal closure that hom(X, S is an equivalent arrow that does not depend on closure. With this is mind, we define the reversible state arrow with a store of type S: This satisfies the inverse arrow laws. To access the state, we use reversible duplication of values (categorically, this requires the monoidal product to have a natural diagonal Δ X : X → X ⊗ X, as inverse products do). Syntactically, this corresponds to the following arrow: The inverse to this arrow is assert : RState S (X ⊗ S ) X , which asserts that the current state is precisely what is given in its second input component; if this fails, the result is undefined. For changing the state, while we cannot destructively update it reversibly, we can reversibly update it by a given reversible function with signature S ↔ S. This gives: This is analogous to how variable assignment works in the reversible programming language Janus [35]: Since destructive updating is not permitted, state is updated by means of built-in reversible update operators, e.g., updating a variable by adding a constant or the contents of another variable to it, etc.

Example 3.8 (Computation in context)
Related to computation with a mutable store is computation with an immutable one; that is, computation within a larger context that remains invariant across execution. In an irreversible setting, this job is typically handled by the reader monad (with context of type C), defined as Reader C X = C ⇒ X . This approach is fundamentally irreversible, however, as the context is "forgotten" whenever a value is computed by supplying it with a context. Even further, it relies on the reversibly problematic notion of monoidal closure.
A reversible version of this idea is one that remembers the context, giving us the reversible Reader arrow: This is precisely the same as the state arrow -indeed, the instance declarations for arr , (> > >), first, and inv are the same -save for the fact that we additionally require all Reader arrows r to satisfy c = c whenever r (x , c) = (y, c ). We notice that arr f satisfies this property for all f , whereas (> > >), first, and inv all preserve it. This resembles the "slice" construction on inverse categories with inverse products; see [11,Sec. 4.4].
As such, while we can provide access to the context via a function defined exactly as get for the reversible state arrow, we cannot provide an update function without (potentially) breaking this property -as intended. In practice, the property that the context is invariant across execution can be aided by appropriate interface hiding, i.e. exposing the Reader type and appropriate instance declarations and helpers (such as get and assert) but leaving the constructor for Reader arrows hidden.

Example 3.9 (Rewriter)
A particularly useful special case of the reversible state arrow is when the store S forms a group. While group multiplication if seen as a function G ⊗ G ↔ G is invertible only in degenerate cases, we can use parametrization to fix the first argument of the multiplication, giving it a much more reasonable signature of G → (G ↔ G). In this way, groups can be expressed as instances of the type class subject to the usual group axioms. This gives us an arrow of the form with instance declarations identical to that of RState G, save that we require G to be an instance of the Group type class. With this, adding or removing elements from state of type G can then be performed by which "rewrites" the state by the value a of type G. Note that while the name of this arrow was chosen to be evocative of the Writer monad known from irreversible functional programming, as it may be used for similar practical purposes, its construction is substantially different (i.e., irreversible Writer arrows are maps of the form X → Y × M where M is a monoid). Notice that preservation of length is required for first to work: if the arrow a does not preserve the length of xs, then zip (a xs, zs) is undefined. However, since arr lifts a pure function f to a map (which preserves length), and (> > >) and inv are given by the usual composition and inversion, the interface maintains this property. Example 3.11 (Reversible error handling) An inverse weak arrow comes from reversible computation with a possibility for failure. The weak Error arrow is defined using disjointness tensors as follows: In this definition, we think of the type E as the type of errors that could occur during computation. As such, a pure function f lifts to a weak arrow which always succeeds with value f (x) when given a nonerroneous input of x, and always propagates errors that may have occured previously. Raising an error reversibly requires more work than in the irreversible case, as the effectful program that produces an error must be able to recover from it in the converse direction. In this way, a reversible raise requires two pieces of data: a function of type X ↔ E that transforms problematic inputs into appropriate errors; and a choice function of type E ↔ E ⊕ E that decides if the error came from this site, injecting it to the left if it did, and to the right if it did not. The latter choice function is critical, as in the converse direction it decides whether the error should be handled immediately or later. Thus we define raise as follows: The converse of raise is handle, an (unconditional) error handler that maps matching errors back to succesful output values. Since unconditional error handling is seldom required, this can be combined with control flow (see Example 3.15) to perform conditional error handling, i.e. to only handle errors if they occur. Example 3.12 (Serialization) When restricting our attention, as we do here, to only first-order reversible functional programming languages, another example of inverse arrows arises in the form of serializers. A serializer is a function that transforms an internal data representation into one more suitable for storage, or for transmission to other running processes. To transform serialized data back into an internal representation, a suitable deserializer is used.
When restricting ourselves to the first-order case, it seems reasonable to assume that all types are serializable, as we thus avoid the problematic case of how to serialize data of function type. As such, assuming that all types X admit a function serialize : X ↔ Serialized X (where Serialized X is the type of serializations of data of type X), we define the Serializer arrow as follows: Notice how serialize † : Serialized X ↔ X takes the role of a (partial) deserializer, able to recover the internal representation from serialized data as produced by the serializer. A deserializer of the form serialize † will often only be partially defined, since many serialization methods allow many different serialized representations of the same data (for example, many textual serialization formats are whitespace insensitive). In spite of this shortcoming, partial deserializers produced by inverting serializers are sufficient for the above definition to satisfy the inverse arrow laws.

Example 3.13 (Dagger Frobenius monads)
Monads are also often used to capture computational side-effects. Arrows are more general. If T is a strong monad, then A = hom(−, T (+)) is an arrow: arr is given by the unit, > > > is given by Kleisli composition, and first is given by the strength maps. What happens when the base category is a dagger or inverse category modelling reversible pure functions? A monad T on a dagger category is a dagger Frobenius monad when it satisfies . The Kleisli category of such a monad is again a dagger category [15, Lemma 6.1], giving rise to an operation inv satisfying (9)-(10). A dagger Frobenius monad is strong when the strength maps are unitary. In this case (11)-(12) also follow. If the underlying category is an inverse category, then μ • μ † • μ = μ, whence μ • μ † = id, and (13)- (14) follow. Thus, if T is a strong dagger Frobenius monad on a dagger/inverse category, then A is a dagger/inverse arrow. The Frobenius monad T (X) = X ⊗ C 2 on the category of Hilbert spaces captures measurement in quantum computation [14], giving a good example of capturing an irreversible effect in a reversible setting. For more examples see [15]. Example 3.14 (Restriction monads) There is a notion in between the dagger and inverse arrows of the previous example. A (strong) restriction monad is a (strong) monad on a (monoidal) restriction category whose underlying endofunctor is a restriction functor. The Kleisli-category of a restriction monad T has a natural restriction structure: just define the restriction of f : X → T (Y ) to be η X •f . The functors between the base category and the Kleisli category then become restriction functors. If T is a strong restriction monad on a monoidal restriction category C, then Inv(C) has an inverse arrow (X, Y ) → (Inv(K (T )))(X, Y ).

Example 3.15 (Control flow)
While only trivial inverse categories have coproducts [11], less structure suffices for reversible control structures. When the domain and codomain of an inverse arrow both have disjointness tensors (see Definition 2.4), it can often be used to implement ArrowChoice. For a simple example, the pure arrow on an inverse category with disjointness tensors implements The laws of ArrowChoice [16] simply reduce to −⊕− being a bifunctor with natural quasi-injections. More generally, the laws amount to preservation of the disjointness tensor. For the reversible state arrow (Example 3.7), this hinges on ⊗ distributing over ⊕.
The splitting combinator (+ + +) is unproblematic for reversiblity, but the fanin combinator (|||) cannot be defined reversibly, as it explicitly deletes information about which branch was chosen. Reversible conditionals thus require two predicates: one determining the branch to take, and one asserted to join the branches after execution. The branch-joining predicate must be chosen carefully to ensure that it is always true after the then-branch, and false after the else-branch. This is a standard way of handling branch joining reversibly [35,34,12].

Example 3.16 (Superoperators)
Quantum information theory has to deal with environments. The basic category FHilb is that of finite-dimensional Hilbert spaces and linear maps. But because a system may be entangled with its environment, the only morphisms that preserve states are the so-called superoperators, or completely positive maps [31,7]: they are not just positive, but stay positive when tensored with an arbitrary ancillary object. In a sense, information about the system may be stored in the environment without breaking the (reversible) laws of nature. This leads to the so-called CPM construction. It is infamously known not to be a monad. But it is a dagger arrow on FHilb, where A X Y is the set of completely positive maps Aside from these, other examples do fit the interface of inverse arrows, though they are less syntactically interesting as they must essentially be "built in" to a particular programming language. These include reversible IO, which functions very similarly to irreversible IO, and reversible recursion, which could be used to give a type-level separation between terminating and potentially non-terminating functions, by only allowing fixed points of parametrized functions between arrows rather than between (pure) functions.

Inverse arrows, categorically
This section explicates the categorical structure of inverse arrows. Arrows on C can be modelled categorically as monoids in the functor category [C op × C, Set] [18]. They also correspond to certain identity-on-objects functors J : C → D. The category D for an arrow A is built by D(X, Y ) = A X Y , and arr provides the functor J. We will only consider the multiplicative fragment. The operation first can be incorporated in a standard way using strength [18,3], and poses no added difficulty in the reversible setting.
Clearly, dagger arrows correspond to D being a dagger category and J a dagger functor, whereas inverse arrows correspond to both C and D being inverse categories and J a (dagger) functor. This section takes the first point of view: which monoids correspond to dagger arrows and inverse arrows? In the dagger case, the answer is quite simple: the dagger makes [C op × C, Set] into an involutive monoidal category, and then dagger arrows correspond to involutive monoids. Inverse arrows furthermore require certain diagrams to commute.

Definition 4.1
An involutive monoidal category is a monoidal category C equipped with an involution: a functor ( ): C → C satisfying f = f for all morphisms f , together with a natural isomorphism χ X,Y : X ⊗ Y → Y ⊗ X that makes the following diagrams commute 4 :

x), and the action on morphisms is given by
. The unit of the tensor product is hom C .

Proposition 4.4 If C is a dagger category, then [C op × C, Set] is an involutive monoidal category when one defines the involution on objects
Proof. First observe that ( ) is well-defined: For any natural transformation of profunctors τ , τ is natural, and τ → τ is functorial. Define χ F,G by the following composite of natural isomorphisms: Checking that χ make the relevant diagrams commute is routine. Proof. It suffices to show that the dagger on an arrow corresponds to an involution on the corresponding monoid F . But this is easy: an involution on F corresponds to giving, for each X, Y a map F (X, Y ) → F (Y, X) subject to some axioms. That this involution is a monoid homomorphism amounts to it being a contravariant identity-on-objects-functor, and the other axiom amounts to it being involutive. 2

Remark 4.6
If the operation first is modeled categorically as (internal) strength, axiom (12) for dagger arrows can be phrased in [C op × C, Set] as follows: for each object Z of C, and each dagger arrow M , the profunctor M Z = M ((−)⊗Z, (+)⊗Z) is also a dagger arrow, and first −,+,Z is a natural transformation M ⇒ M Z . The arrow laws (7) and (8) imply that it is a monoid homomorphism, and the new axiom just states that it is in fact a homomorphism of involutive monoids. For inverse arrows this law is not needed, as any functor between inverse categories is automatically a dagger functor and thus every monoid homomorphism between monoids corresponding to inverse arrows preserves the involution.
Next we set out to characterize which involutive monoids correspond to inverse arrows. Given an involutive monoid M , the obvious approach would be to just state that the map M → M defined by a → a • a † • a is the identity. However, there is a catch: for an arbitrary involutive monoid, the map a → a • a † • a is not natural transformation and therefore not a morphism in [C op × C, Set]. To circumvent this, we first require some conditions guaranteeing naturality. These conditions concern endomorphisms, and to discuss them we introduce an auxiliary operation on [C op × C, Set].  For the rest of this section, assume the base category C to be an inverse category. This lets us multiply positive arrows by positive pure morphisms. If M is an involutive monoid in [C op × C, Set], then the map LM × L + (hom C ) → LM defined by (a, g † • g) → a • g † • g is natural: Similarly there is a map L + (hom) × LM → LM defined by (g † • g, a) → g † • g • a. Now the category corresponding to M satisfies a † • a • g † • g = g † • g • a † • a for all a and pure g if and only if the following diagram commutes: If this is satisfied for an involutive monoid M in [C op ×C, Set], then positive arrows multiply. In other words, the map This multiplication is commutative iff the following diagram commutes:  defined by (a, a  † , a) → a • a † • a is natural: Thus M satisfies a • a † • a = a if and only if the following diagram commutes: Hence we have established the following theorem.

Applications and related work
As we have seen, inverse arrows capture a variety of fundamental reversible effects. An immediate application of our results would be to retrofit existing typed reversible functional programming languages (e.g., Theseus [20]) with inverse arrows to accommodate reversible effects while maintaining a type-level separation between pure and effectful programs. Another approach could be to design entirely new such programming languages, taking inverse arrows as the fundamental representation of reversible effects. While the Haskell approach to arrows uses typeclasses [16], these are not a priori necessary to reap the benefits of inverse arrows. For example, special syntax for defining inverse arrows could also be used, either explicitly, or implicitly by means of an effect system that uses inverse arrows "under the hood".
To aid programming with ordinary arrows, a handy notation due to Paterson [27,28] may be used. The simplest form of this notation is based on process combinators, the central one being p → e 1 ≺ e 2 = ⎧ ⎨ ⎩ arr (λp.e 2 ) > > > e 1 if p is fresh for e 1 , arr (λp.(e 1 , e 2 )) > > > app otherwise.
Note that if the second branch is used, the arrow must additionally be an instance of ArrowApply (so that it is, in fact, a monad). Though we only know of degenerate examples where inverse arrows are instances of ArrowApply, this definition is conceptually unproblematic (from the point of view of guaranteeing reversibility) so long as the pure function λp.e 2 is first-order and reversible. A more advanced style of this notation is the do-notation for arrows, which additionally relies on the arrow combinator If the underlying monoidal dagger category has natural coassociative diagonals, for example when it has inverse products, this combinator does exist: the arrow combinator (& & &) can be defined as where copy : X → X ⊗ X is the natural diagonal (given in pseudocode by copy x = (x , x )), and the combinator second is derived from first in the usual way, i.e., as Inverse arrow law (13) then guarantees that doing, then undoing, and then doing the same operation is the same as doing it once.
A pleasant consequence of the semantics of inverse arrows is that inverse arrows are safe: as long as the inverse arrow laws are satisfied, fundamental properties guaranteed by reversible functional programming languages (such as invertibility and closure under program inversion) are preserved. In this way, inverse arrows provide reversible effects as a conservative extension to pure reversible functional programming.
A similar approach to invertibility using arrows is given by bidirectional arrows [2]. However, while the goal of inverse arrows is to add effects to already invertible languages, bidirectional arrows arise as a means to add invertibility to an otherwise uninvertible language. As such, bidirectional arrows have different concerns than inverse arrows, and notably do not guarantee invertibility in the general case.