Algebraic Effects Meet Hoare Logic in Cubical Agda

This paper presents a novel formalisation of algebraic effects with equations in Cubical Agda. Unlike previous work in the literature that employed setoids to deal with equations, the library presented here uses quotient types to faithfully encode the type of terms quotiented by laws. Apart from tools for equational reasoning, the library also provides an effect-generic Hoare logic for algebraic effects, which enables reasoning about effectful programs in terms of their pre- and post- conditions. A particularly novel aspect is that equational reasoning and Hoare-style reasoning are related by an elimination principle of Hoare logic.


INTRODUCTION
Programs in practice usually produce computational effects, such as I/O, mutable memory, nondeterminism, and probabilistic choice.Pioneered by Moggi [1989Moggi [ , 1991]], such computational effects can be categorically represented as monads.This view is further refined by Plotkin and Power [2002] who pointed out that almost all the monads modelling computational effects can be presented as algebraic theories specifying the primitive operations of an effect and its equational laws.
Based on the algebraic view, Plotkin andPretnar [2009, 2013] proposed a programming language feature, called an effect handler, which allows the programmer to introduce and eliminate effects conveniently in a program.Due to their flexibility, effect handlers have been quickly adopted by the programming language community, and implemented in many programming languages, e.g.Brachthäuser et al. [2018]; Ghica et al. [2022]; Leijen [2014Leijen [ , 2017]]; Sivaramakrishnan et al. [2021].
Besides these implementations, there are formalisations of algebraic theories [Abel 2021;Gunther et al. 2018] and computational effects [Li and Weirich 2022;Xia et al. 2020;Yoon et al. 2022], which facilitate verification of effectful programs.Notwithstanding, the existing formalisations in the literature fail to provide complete satisfaction for program verification for two reasons: Setoids.In these formalisations, programs modulo equational laws do not form types but rather setoids, which are commonly deemed tedious to work with.Hoare Logic.In these formalisations, reasoning about effectful programs in the equational style [Gibbons and Hinze 2011;Plotkin and Pretnar 2008] is well supported, but reasoning in the style of Hoare logic [Hoare 1969] does not have good support.
data Op where The main contribution of this paper is a new formalisation of algebraic effects which addresses these two shortcomings as follows: Quotients.Our formalisation of algebraic effects and theories includes equations, but avoids setoids and instead uses quotients.This is facilitated by the recent development of Homotopy Type Theory [Univalent Foundations Program 2013] and its computational realisation-Cubical Type Theory [Angiuli et al. 2021;Cohen et al. 2018], which supports quotient types natively.The particular implementation we use is Cubical Agda [Vezzosi et al. 2019].
Hoare Logic.The library contains an effect-generic Hoare logic, that is inspired by Schröder and Mossakowski [2003] and Goncharov and Schröder [2013].The idea is that pre-and post-conditions  and  of a Hoare triple {  } p { } are encoded as effectful programs that return a (not necessarily decidable) proposition, and the validity of a Hoare triple is encoded as an equality of programs: (do a ← ; x ← p; b ←  x; return (x , a |→| b)) ≡ (do a ← ; x ← p; b ←  x; return (x , True)) This encoding is explained in full in Section 6.The usual inference rules of Hoare logic are derivable based on this encoding.Moreover, we prove a novel elimination principle for Hoare logic that connects equational reasoning and Hoare-style reasoning pleasantly: whenever the return value of a program  is shown to satisfy a predicate  using Hoare logic, and  x implies that f x ≡ g x, then (do x ← p; f x) ≡ (do x ← p; g x).This principle is simple to state, but surprisingly difficult to prove constructively.Its proof is a significant novel contribution of this paper, presented in Section 7.
A Quick Taste.To be concrete, we sketch here an example of formalising and verifying a stateful and nondeterministic parser in our framework, which will also be our running example in later sections.The parser will be built in the style of monadic parser combinators [Hutton and Meijer 1998]: the stream of tokens is kept in a mutable state that the parser can inspect and consume, and the parser can make nondeterministic choices to parse the expression in possibly different ways.
To formalise the parser, the first step is to define the algebraic theory of its computational effects.Instead of defining a monolithic theory of all the effectful operations that the parser needs, an appealing feature of algebraic effects is that algebraic theories can be defined modularly and then combined into bigger ones.Wu et al. [2014] showed that parsers can be described by the combination of two separate algebraic theories: mutable state and nondeterminism.
Each algebraic theory consists of (i) a signature specifying the name of the operations and their arities and (ii) a collection of equational laws.To formalise a signature in our framework, we need to define a type, typically called Op, whose elements correspond to the operations, as well as a function, typically called Arity, which maps elements of Op to a type.The signatures of nondeterminism and state are shown in Figure 1.Nondeterminism, ND, has two operations: ‹|› for choices and fail for failures.State, S  , is parameterised by the type of the mutable state , and it has operations get and put for reading and writing that state respectively.
To complete the definition of the theory of nondeterminism and state, we also need to specify their equational laws.Our formalisation provides combinators for specifying equations conveniently.For example, associativity of nondeterministic choice is programmed as assoc = ∀ n  x y z → (x ‹|› y) ‹|› z x ‹|› (y ‹|› z) Additionally, the theory of nondeterministic choice has equations stipulating that ‹|› is idempotent and commutative, and that fail is an identity element of ‹|›.The theory of state has equations ( 12) stipulating the interaction of reading and writing the state, for example: put-put  = (do put s; get) (do put s; return s) With theories S and ND in place, we can combine them into the algebraic theory P for parsing.The combination that is desirable for parsing is tensoring [Hyland et al. 2006], P = ND ⊗ S String , which takes the disjoint union of the operations and equations of S and ND and adds new equations saying that every operation in S commutes with every operation in ND.
The algebraic theory P then automatically defines a monad Term P of P-computations.The monad is equipped with the operations from P and satisfies all the equational laws.With this monad, we can write programs with nondeterminism and state.
Now suppose that we are interested in parsing expressions built from a binary operator * and two constants ♦ and ♠.A parser is shown in Figure 2, in which the program any-char consumes an arbitrary character from the state, and the program char c consumes a character c from the state, and if the next character is not c, the operation fail is called.The result of parse-tree n is to return a binary tree in the monad Term; it simply uses nondeterministic choices to try different ways of parsing the input, and the argument  is fuel which limits the depth of recursion.
To demonstrate the correctness of the parser, we would like to show that for any t : Tree, if t is pushed to the input stream, the parser always parses the tree t back: (do push (print t); parse-tree n) ≡ return t (1) Here, push prepends a string to the input stream (push s = do r ← get; put (s + + r)).Note that this equality is stronger than merely proving the input-output behaviour of the parser using e.g.Hoare logic, since this equality ensures that each side of the equation can be substituted by the other side in any context without changing the semantics, which can be used in program transformation.
Although it is possible to prove this solely using the equational laws of P, such a proof is miserably tedious because of the many bureaucratic equational rewriting steps needed in the proof.For example, when showing that the first character c printed to the input is consumed by a corresponding any-char c by the parser, we will have to invoke the commutativity laws of P to swap any-char c with all operations in between: (do push-char c; . ..; any-char c) = • • • = (do push-char c; any-char c; . ..) so that push-char and any-char can cancel out using equations of P. In this example, the operations getting in the way between push-char and any-char are for printing the rest of the tree t, so we further need an induction on the tree t to do all the swapping.Through such attempts, it does not take too long to realise purely equational reasoning is too low-level for verifying programs with algebraic effects, and higher-level reasoning techniques are needed.
The solution offered by our framework is an effect-generic Hoare-logic: for every algebraic theory, there is automatically a notion of Hoare triples satisfying the usual inference rules of Hoare logic.Fig. 2. A simple parser of expressions using nondeterminism and state.
In the parser example, we can prove the following Hoare triple (explained in full later ( 27)): where remaining s is a predicate saying that the remaining input stream is equal to s.
Hoare triples and program equivalences are related by an "elimination principle" (Section 7) for Hoare triples: let  : A → Ω be a predicate on a type , and let f and g be two functions A → Term B such that for all x : A,  x implies f x ≡ g x, then the following holds: Using H -elim one can derive the desired equality (1) from the Hoare triple (2) in a few easy steps.The constructive proof of this elimination principle, one of the major technical contributions of this paper, is highly non-trivial and requires that the arities of the operations of the algebraic theory be finite, so the strings in the parser example in fact have to be bounded by a finite length.However, there is also a non-constructive proof of H -elim without this restriction.
We would like to emphasise that the role of Hoare logic here is not the verification goal per se, but a high-level proof technique for showing the desired equivalence (1).Such way of using Hoare-style logic has been proposed for specific effects and Hoare-style logics before [Song et al. 2023;Turon et al. 2013], but to our knowledge our framework is the first one to implement this technique for generic algebraic effects in a constructive setting.
Paper Organisation.The contribution of this paper is a library for formalising and verifying effectful programs based on algebraic effects.The library consists of the following components: • algebraic theories and their free algebras, • common computational effects and their combinations, • an equational logic and Hoare logic for reasoning about effectful programs, which amount to about 9000 lines of code.It is implemented in Cubical Agda 2.6.2.The rest of this paper presents the main constructions of our formalisation.We will assume basic familiarity with Agda.No prior knowledge about algebraic effects is assumed, so this paper may be read as a tutorial for algebraic effects.The structure of our exposition on algebraic effects in Sections 2-5 is influenced by Bauer [2019].The material is organised as follows: • Section 2 defines signatures, algebras, and free algebras.
• Section 3 defines equations over algebraic signatures and algebraic theories.
• Section 4 defines an equational logic for reasoning about terms of an algebraic theory, and uses quotient types to construct term algebras of algebraic theories.• Section 5 goes into some detail of the parser example that we have seen above to demonstrate how to program with algebraic effects in our formalisation.• Section 6 shows how Hoare logic can be defined for algebraic effects in a generic way.
• Section 7 shows an elimination principle of Hoare logic which relates equational reasoning and Hoare-style reasoning.• Section 8 discusses related work and we conclude in Section 9.

ALGEBRAIC SIGNATURES AND ALGEBRAS
To warm up, we begin with formalising algebraic theories without any equational laws, including their signatures, which specify the arities of operations of an algebraic theory; algebras of a signature, which are types equipped with operations specified by the signature; and free algebras, which are syntactic terms built from variables and operations.These concepts are standard ones from universal algebra [Birkhoff 1935;Cohn 1981], except that our formalisation is type-theoretic rather than set-theoretic, and that we will mainly use examples from computational effects.
Foundation.Our formalisation is based on intensional Martin-Löf type theory with universes and inductive types [Martin-Löf 1982], supplemented with function extensionality and effective set quotients.We use Cubical Agda [Vezzosi et al. 2019] to carry out our formalisation, but we will not rely on features of Cubical Type Theory apart from those mentioned above.
Using Cubical Agda gives our formalisation computational meaning-the formalised proofs are runnable programs, but in principle our formalisation also has semantics in any category that have the aforementioned constructs, in particular, all topoi.For example, by interpreting our formalisation in the presheaf topos over a cartesian closed category C, the formalisation can be read as a formalisation of C-enriched algebraic theories [Kelly and Power 1993], but we will not make use of this additional semantic generality in this paper.

Algebraic Signatures
An algebraic signature, or simply a signature, is a type Op of operation symbols paired with a type family Arity giving the arity of each operation: record Signature : Type 1 where constructor _◁_ field Op : Type Arity : Op → Type (3) In the literature, signatures are sometimes also called containers [Abbott et al. 2005].
Examples.In our running example of parsers, the nondeterminism effect facilitates the expression of computations that can return multiple results.We have seen its signature ND in Figure 1.The `fail operator represents a program with no results, and `‹|› is a binary operator combining the results from two sub-computations.Instead of a natural number, here the Arity function returns a type, where the cardinality of Arity  corresponds to the arity of the operation .The arity of a nullary operator like `fail is ⊥, the empty type, and a binary operator like `‹|› has arity Bool.In general, a natural-number arity n is represented by Fin n: where ⊤ is the singleton type, and ⊎ is disjoint union.As a result Fin  has exactly  closed elements.
The generalisation of arity from numbers to types allows for infinitary arities.For instance, given any type , the theory of mutable -state has two operations: `get for reading the state and `put for writing the state.Its signature S  is in Figure 1.The arity of `get is type , which may have infinitely many elements.Conceptually, the operation `get takes -many computations as arguments, and results in a computation that reads the state and then continues as the argument corresponding to the current state.In contrast, `put  for each  :  is a unary operation, whose only argument stands for the way to continue after writing  into the state.However, while it is possible to express infinite arities syntactically, to define the free model over a theory these arities need to preserve quotients (Definition 4.2), a condition we will explain in more detail in Section 4.4.All finite types preserve quotients, but there is strong evidence [Coquand et al. 2017] that infinitary types do not, at least in Cubical Agda.

Algebras of Signatures
Where a signature F describes an abstract collection of operations, an F-algebra captures the notion of a type supporting or "implementing" those operations.F-algebras are defined in terms of the functor F : Type → Type, which corresponds to one level of application of an operation in F: An F-algebra on a type C, referred to as the carrier of the algebra, is a function Here _ and _-Alg[_] are syntactically mixfix operators, where the underscores mark the places for arguments.We will use mixfix operators frequently in this paper to aid readability.Agda uses hierarchical universes to handle large sets, so Type : Type 1 , Type 1 : Type 2 , and so on.Agda also supports universe polymorphism, so functions can be generic over what universe level they work with.The technical intricacies of this system are not relevant to understanding our contributions, so we have simplified the presentation of some types, like F , by giving a universe monomorphic version in the text, where there is a polymorphic version in the formalisation.
Examples.Lists of -elements for an arbitrary type  (in this paper, variables , , , always mean arbitrary types) implement nondeterminism by the following algebra: The algebra maps each operation in nondeterminism to an operation on lists: the ‹|› operation goes to concatenation of lists, and fail constructs an empty list.A signature can have multiple algebras, even with the same carrier.The following are two ways that booleans implement nondeterminism: As another example, the signature S has an algebra over functions S → A × S: This interprets mutable state into the state monad [Moggi 1991], a function  →  ×  taking an initial state as input and produces a value of type A along with the final state.In this algebra, the argument k :  → ( →  × ) to the operation get is thought of as the continuation after reading the state.Therefore the result of get continues with s 1 and keeps the state unchanged, where s 1 is the initial state.Similarly, the result of put s 2 on k : ⊤ → (S → A × S) is the computation that ignores its initial state and continues as k ⟨⟩ with the new state s 2 .

Syntax Trees
Let F be a signature and  be any type.Syntax trees of terms built out of variables from  and operations from F are represented by the following Syntax type: data Syntax (F : Signature) ( : Type) : Type where var : The constructor var introduces a variable, and op builds a term by applying an operation from F to some existing terms.The parameter F will often be omitted: when F is clear from the context, we will write Syntax A rather than Syntax F A.
Examples.Consider the following pair of programs: These programs use the nondeterminism effect.up-to n computes all the natural numbers smaller than n, and odds n computes the odd numbers smaller than n.The guard function takes a condition and causes the computation to fail when the condition is false.
Let us build the value of type Syntax ND N that represents the syntax tree for the odds program.First, the do-notation in (8) can be desugared in the usual way: The do-bindings have been replaced by the monadic bind, which is defined as follows on Syntax: This performs variable substitution.The term xs > > =  takes a syntax tree xs and returns the tree with all variables replaced according to the continuation .
Next, the operators from the signature must be defined as functions on Syntax._‹|›_ : The definition for ‹|› here enshrines false as corresponding to the left branch of ‹|›.
Finally, the remaining symbols in the program must be defined in terms of Syntax.return : A → Syntax ND A return = var guard : Bool → Syntax ND ⊤ guard c = if c then return ⟨⟩ else fail

Interpreting the Syntax Tree
Syntax trees of a signature F can be interpreted into an F-algebra: This function takes an algebra  : F -Alg and a mapping  from the variables  to the algebra C, and interprets syntactic programs with operations of the algebra accordingly.
A more computational reading of this function is that the argument of type Syntax  is a program invoking operations from F and returning a value of type .The algebra  handles the operation calls in the program.The function  turns the -value returned by the program into the type  operates on.Therefore the function interp is called a (deep) effect handler [Plotkin and Pretnar 2013;Pretnar and Bauer 2014] when it is implemented as a programming language construct.
For an example of interpretation, the syntactic program odds of signature ND can be interpreted using the algebra over lists: interp list-int ( v → [ v ]) (odds 7) computes to [1,3,5].What makes effect handlers a powerful language feature is that different interpretations can be given to the same syntactic program: interp all-int is-odd (odds 7) computes to true.
Another example of an interpretation is the monadic bind on Syntax : This follows from the fact that the Syntax type is itself an algebra of F, via op: syntax-alg : There are some useful properties we can prove about interpretation.First, if the algebra supplied is op, and the variable assignment is var, the interpretation is an identity: (xs : Syntax ) → interp op var xs ≡ xs Moreover, the interpretation supports the fusion laws, a powerful tool in reasoning about combinations of interpretations.One such law states that for an F-algebra , a variable assignment , a continuation k, and a syntax tree xs, the following holds: This says that an interp after a bind can be fused with the continuation supplied to the bind.

Freeness of Syntax
We saw above that the Syntax type is itself an algebra of the signature F (syntax-alg); as it happens it is the free algebra.To define freeness formally first we need to define F-homomorphisms.
Homomorphisms.An F-homomorphism between two F-algebras is a function h between the two carriers C and D such that the function commutes with the operations of the signature: This definition of a homomorphism is the same as the usual definition of, for instance, a monoid homomorphism, stated in slightly more general language.On the signature for monoids, the above code for a homomorphism translates to a function ℎ between two carrier sets for two monoids, along with a pair of proofs that ℎ  • ℎ  = ℎ ( • ) and ℎ  = .
Freeness.Let  be any type.A (homotopic-) free F-algebra over  is an algebra  : F -Alg[ A ] over some type  with a function  :  → A such that for any algebra  : F -Alg[  ] and function This definition is a little dense: in simpler terms, it amounts to saying that if we inject some variable into the free algebra, and then use the operators of the signature on the free algebra to build a syntax tree, and then interpret that syntax tree into another algebra, it's the same as directly using the same operations in the latter algebra.
Theorem 2.1.Syntax F  with var is a free F-algebra over .
Proof Sketch.We have already seen that Syntax F  is an algebra of F, via op.Given another algebra  : F → , and a function  :  → , the homomorphism is given by ℎ = interp   .This satisfies the cancellative property by definition: ℎ •  = interp   • var =  .Finally, given any other homomorphism  : Syntax F →  with this cancellative property, it can be shown to be equal to ℎ as functions Syntax F →  by induction on Syntax F. To show that they are equal as homomorphisms (Syntax F, op) → ℎ (, ), we also need to show that the equalities witnessing their commutation with operations are equal.We have formalised this proof of homotopic freeness using a similar proof idea to Awodey et al. [2017] (which proved homotopic initiality).□

ALGEBRAIC THEORIES AND MODELS
Algebraic theories are a combination of operations (the signature) and equations (the laws).The previous section dealt only with the signature; this section will introduce laws to the discussion.Mirroring the development in the previous section, we will first define equations and algebraic theories, and then we will define models: algebras of the underlying signature of a theory that satisfy all the equations.The free models of algebraic theories, which mirror free algebras of signatures in Section 2, are slightly more involved, and will be treated in the next section.
Equations.The laws for an algebraic theory are a collection of formal equations between syntactic terms.Thus the type Equation F  is a pair of syntax trees, one for each side of the equation, in the signature F with free variables : record Equation F  where constructor _ _ field lhs rhs : Syntax F  Here, for instance, is the equation for associativity of ‹|›: assoc : Equation ND (Fin 3) assoc = (var 0 ‹|› var 1) ‹|› var 2 var 0 ‹|› (var 1 ‹|› var 2) This equation has three variables, hence  = Fin 3. The helper function ∀ n makes the statement of the equation a little more readable: Contexts.To state some equations succinctly it is useful to use variables drawn from a context: the following statement of the "put-put" law, for instance, uses the variables  1 and  2 .
This law says that a put immediately following another put overwrites the first one.It would be possible to encode this law as  ×  individual laws-one for each choice of  1 and  2 -but it is more reasonable to define a law as having a context Γ, which can contain parameters like  1 and  2 .
record Law F where field  : Type A Law F is a law for signature F. The law is under context Γ and free variables .The free variables can depend on the context: this is necessary, for example, to construct the tensoring law (21).
Theories.Finally, an algebraic theory over a signature F is a collection of laws over a signature F: record Theory F where field Laws : Type Eqns : Laws → Law F The theory of mutable state, for instance, has three laws: data Laws where put-put put-get get-put : Laws The Eqns field then returns the corresponding law for each index: The equation (do s 1 ← get ; s 2 ← get ; return (s 1 , s 2 )) = (do s ← get; return (s , s)) is sometimes also required as the "get-get" law of mutable state, but in fact follows from the equations above.
Models.Given an equation lhs rhs over a signature F, an F-algebra  : F C → C over some type C is said to respect this equation if the following proposition holds: which says that interpreting both sides of the equation using the algebra  under any variable assignment  results in the same value.Note that _Respects_ is syntactically an infix operator, so it is in the sans serif font to distinguish from an ordinary function.Moreover, if an algebra respects all the equations in a theory, we say that it models the theory: , where T is a theory with signature F, is an F-algebra over some carrier type C that respects the laws of T : For example, the algebra state-alg (6) can be shown to be a model for the theory of mutable state, since state-alg respects the state laws (12).

FREE MODELS OF ALGEBRAIC THEORIES
The core construction of this paper is the free model of an algebraic theory, which is similar to the free algebras of signatures in Section 2.5 but with equations taken into consideration.Free models are important as they give us a formal construction which represents the structure present in all models of a theory.Thus they may be seen as encoding the structure in the theory itself.In particular, if an algebraic theory describes a computational effect adequately, the free models of the theory may be seen as the computations with the effect.Some algebraic theories have direct descriptions of their free models; for instance, free monoids are precisely lists, and free commutative monoids are N-valued functions with finitely many non-zero values.However, such concrete descriptions do not always exist for algebraic theories.In general, free models are constructed by quotienting syntax trees Syntax F (7) with a suitable equivalence relation defined by the equational laws of T .
Section 4.1 will first quickly recap quotient types in Cubical Agda, and Section 4.2 will define the equivalence relation.Finally, the type for the free model itself, Term, will be presented in Section 4.3 and its properties expanded upon.Section 4.4 will show that Term is the free model for T .

Quotient Types
Given a type A and a type family _ _ : A → A → Type, their (set) quotient A / _ _ can be expressed as a higher inductive type [Univalent Foundations Program 2013] in Cubical Agda: The first constructor injects every element x of type A into the quotient; the second constructor identifies [ x ] and [ y ] whenever   is inhabited; the final constructor makes the type A / _ _ always a set in HoTT (also known as an h-set): isSet B = (x y : B) → (p q : x ≡ y) → p ≡ q (14) An alternative way to phrase ( 14) is that a type B is a set if for all pairs of elements x, y : B, the identity types  =  are propositions, which are types whose elements are all identified: isProp P = (x y : P) → x ≡ y Quotient types can be consumed by pattern-matching as usual, but in practice it tends to be easier to use the recursion principle below, which extends a function f : A → B to the quotient type provided that B is a set and f respects the relation: The size of bags can be computed using the recursion principle: The function  witnesses that the length function on lists respects element swaps xs ⟲ ys, so length : List A → N can be extended to bags.
Remark.In Cubical Agda, not all types are sets, i.e. satisfying the proposition ( 14).In particular, if we leave out squash/ in the definition of A / _ _, the reflexive equality refl : [a] ≡ [a] for some element a : A and the equality constructed by eq/    for any r : a a will be different elements of the equality type [a] ≡ [a], even if A is a set and _ _ is a proposition valued type family.This is certainly very different from quotient sets in the usual set-theoretic maths, and thus we need the additional constructor squash/ to "force" the quotient type to be a set.

Program Equivalence
Given an algebraic theory T over a signature F, the free models are obtained by quotienting syntax trees of F with what we call the program equivalence, which is the congruence relation generated by the equational axioms of T .It can be given as an inductive datatype in Cubical Agda: (1) The first constructor eq t says if two syntax trees can be obtained by instantiating an equation of T , they are equivalent.Its declaration has three parameters: i, the index of the particular law; , the context for that law; and , the variable assignment to the variables in the law.
(2) The next three constructors refl t , sym t and trans t extend the relation to an equivalence.
(3) The cong t constructor makes the relation a congruence relation on op: an operation acting on equivalent arguments   and k r leads to equivalent result.(4) The final constructor trunc t is a higher inductive constructor.It identifies any two elements of the type x t y, thus squashing x t y from the type of derivation trees of equivalence of x and y to a proof-irrelevant proposition of x being equivalent to y.
This datatype encodes precisely Birkhoff [1935]'s inference rules for the equational logic of universal algebra, deducing which syntactic terms are regarded as equivalent in an algebraic theory.

Terms up to Program Equivalence
With the program equivalence x t y for an algebraic theory T , we can define the type of T -terms up to program equivalence as the quotient type: where the argument  is the type of variables in terms.For clarity, we also define a specialised constructor . The rest of this section will be dedicated to proving properties about this type, culminating in a proof that it is indeed the free model and a program equivalence x t y holds iff the interpretations of x and y are equal in all models.
Interpretation.Like syntax trees, terms can be interpreted into models of T : Given an  : F -Alg that models T , a generator , and a proof that the carrier is a set, this function interprets into the carrier set from Term.
The definition of interp t is as follows: The definition works by extending interp : Syntax A → C to Term A using rec/.Thus we need the following lemma that interp respects the program equivalence  .Proof.This proof goes by induction on x  y.The cases for the four constructors for congruence follow from the fact that the propositional equality is also a congruence relation: A more interesting case is the constructor eq t i  k, which instantiates the -th law with a variable substitution k :   → Syntax A. We need to show To show this we apply the fusion law interp-comp (10), converting both sides of the goal to the shape interp  (interp   • ).Then by the assumption m :  Models T , both sides are equal.Using some equational reasoning combinators, the proof goes by The final case is trunc t .We need to show that the equality being produced is a proposition.This follows from the assumption of C being a set, since equalities on sets are propositions.□

Term Models
The first step in showing that Term is the free model of a theory T is showing that it is an algebra of the underlying signature F of T , which amounts to constructing an element Perhaps surprisingly, defining this is not entirely straightforward in a constructive setting.The problem becomes clear when the type F -Alg[ Term  ] is expanded: The only relevant functions available are the following two: If we were in a classical foundation, we could define op t by choosing a family of representative elements Arity  → Syntax  from the input family of quotients Arity  → Syntax  / _ _, and then applying op and [_].But in a constructive setting, we cannot make such a choice for all Arity  in general.Therefore we need to make an additional assumption on the arities' types.In particular, we need arities to support a choice principle: we will call this principle quotient preserving.Definition 4.2 (Quotient Preserving).From a high level, the missing piece in implementing op t is a way to commute a function arrow around a quotient, turning a function into a quotient into a quotiented function.(( → ( / )) → ( → ) /  ′ ).First, notice that the inverse always exists: for all A, B : Type and R : B → B → Type, where Pointwise lifts a type family on B to one on A → B: The function needed to implement op t is actually the inverse of dist, where  is the arity of the operator in question.Therefore, we say a type A is quotient preserving if for all types B and type families R : B → B → Type, the function dist is an isomorphism: All simple finite types are quotient-preserving: it is not difficult to construct the trav function when  = Bool, for example.Infinitary types, however, might not preserve quotients without additional axioms.Admitting the axiom of choice allows all types to preserve quotients; the axiom of countable choice (AC  , a weakened form of the axiom of choice that holds in many constructive systems), on the other hand, is precisely equivalent to the condition of N preserving quotients.The choice of foundation, in other words, influences which arities are permitted in term models.In the most general setting this means arities must be finite, so the parser example Figure 2 can only be applied to strings bounded by some arbitrary length.Now we assume a theory T whose arity types Arity o all preserve quotients and return to defining the algebra op t (16).The implementation of op t uses the recursion principle (15): This function takes k : Arity  → Syntax  / _  _, and applies trav to it, pulling the quotient outside the function arrow.Then, rec/ is used to apply a lambda function which re-wraps the subtree  in op, and cong-point proves that this re-wrapping respects the quotient.
Finally, for Term to be a model of T , op t must respect the laws of T , but this is straightforward since the eq t case of t precisely says that any two programs that are instances of equations of T are related, and Term is a quotient of t .
Since op t respects the laws of T , Term can now implement the monadic bind, via interp t : This, along with proofs of the monad laws, shows that Term is in fact a monad.
Analogously to the freeness of Syntax (Theorem 2.1), the Term type is the free model for T .
Theorem 4.3.Let A be a set.The algebra (Term A, op t ) is the free T -model over .
The proof of this theorem follows the structure of Theorem 2.1 closely.The theorem immediately implies the following corollary known as Birkhoff's completeness theorem [Birkhoff 1935].
Corollary 4.4.Let x and y : Syntax A be two syntactic trees.The proposition x t y holds if and only if interp t   x ≡ interp t   y for all T -models  : F C → C and functions  : A → C.

PROGRAMMING WITH EFFECTS
In this section we revisit the parsing example in Figure 2 and flesh out some details that we omitted in the introduction.Recall that we are interested in parsing expressions with a binary operation * and constants ♦ and ♠, and for simplicity, we only consider strings generated by the following function from some tree t : Tree, where Tree is the AST for expressions defined in Figure 2: print : Tree → String print ♦ = "♦" print ♠ = "♠" print (l ⊛ r) = "(" + + print l + + "*" + + print r + + ")" Combining Signatures.Three operations are used in parse-tree in Figure 2: ‹|›, fail, and char.These operations come from a combination, namely the tensor, of nondeterminism and mutable state.The signature of a tensor F ⊗ G is the coproduct ⊞ of the signatures of F and G: Syntax trees generated from the signature P = ND ⊞ S have operations from both nondeterminism and state.Here, for instance, are the syntactic representations of fail, put, and get: fail The remaining operation to define is char.This is a composite operation, defined using operations from both ND and S. Conceptually, the mutable state effect is being used here to refer to the string being parsed: the get operation returns the "rest of the string, yet to be parsed".The parser eof , for instance, parses the empty string: where null tests if a string is empty, meaning that this parser will fail if the remaining string is anything other than the empty string.The parsers any-char and char from Figure 2 are defined in a similar way.As a reminder, here are their signatures: any-char : Syntax Char char : Char → Syntax ⊤ any-char parses and consumes any single character: it first inspects the remaining string with get, failing if it is empty, then it sets the remaining string to the tail of what it was, finally returning the consumed character.This parser is used by char to parse a specific character using an equality test.
To convert a Syntax value to a Term, it is usually sufficient to wrap it in the set-quotient constructor (13).Sometimes slightly more complex functions, like op t , are needed.
put : Combining Laws.Just like the signatures, the laws of nondeterminism and state are combined to yield the laws of parsing.Any law that holds in either constituent theory holds in the theory of parsing.We have already seen the state laws ( 12), the laws of nondeterminism are as follows: These are the laws of a semilattice: this means the parser is also a semilattice, under ‹|› and fail.
On the Term type, these laws are upgraded to a full equalities.
‹|›-assoc : (x y z : Term A) → (x ‹|› y) ‹|› z ≡ x ‹|› (y ‹|› z) However, the simple sum of the laws yields an unsatisfactory semantics for the parser.Identities which involve the interaction of the two theories can run into trouble, for instance: ∀ (c : Char) (p q : Term A) → (do char c; p) ‹|› (do char c; q) ≡ (do char c; p ‹|› q) This simple equation says that the nondeterministic choice of parsers that start with the same character is equal to a parser for that character followed by the choice of the rest of the parsers.Unfortunately, though, this equation does not hold by default: the equation reorders state operations and nondeterminism operations, and it is not correct to assume that this yields an equivalent result.The solution is to have an additional law which insists this is the case.For a pair of signatures F and G, the commutativity law is as follows, for any operators fs ∈ F and gs ∈ G: This law states that operations from the constituent theories commute around each other.The tensor of theories T 1 and T 2 , denoted T 1 ⊗ T 2 , takes the sum of their signatures and laws with additionally laws of commutativity.The parsing effect is the tensor of nondeterminism and mutable state.Interpreting into Models.The Term monad for the algebraic theory P is only syntactic terms modulo equations, so programs like parse-tree written with Term cannot run yet.To actually run them, we need to find more concrete models for the theory P.
We have already seen a model for one of the constituent theories of the parsing effect: statepassing functions (6).In fact, this model is isomorphic to the free model of state.The two sides of this isomorphism are given by the following: toState This isomorphism actually goes further: the state monad transformer is in fact isomorphic to tensoring with theory of state [Hyland et al. 2006].To be precise, for the theory of mutable state S (over some state type ) and some other theory T , the term monad of their tensor is isomorphic to the state monad transformer: The other effect of parsing is nondeterminism.A suitable model for nondeterminism-considering the laws in (20)-is the finite set K, where K  is a finite, enumerable subsets of .The implementation of this type is not relevant here: see [Frumin et al. 2018] for an explanation of the type, or [Kidney 2020] for an implementation in Cubical Agda.It turns out that this model is also isomorphic to the free model of nondeterminism.Thus by ( 22), the term monad for parsing is isomorphic to String → K (A × String), with which we can actually run the parsers.
Recursion.Since elements of the term monad are inductively generated trees modulo the program equivalence, programs defined with general recursion cannot always be defined as elements of the term monad, since they may be infinite trees.One way to work around this problem is to use explicit step indexing as we did in the parser example (Figure 2): a recursive effectful program is represented as a sequence of finite approximations  : N → Term, where p 0 always triggers a failure and p (suc n) expands the recursion suc n times.Consequently, when stating a property about a recursive program, the step indices must be dealt with appropriately.
An alternative approach is to use the coinductive-version of Syntax to represent programs, which is the approach taken by Xia et al. [2020] in their framework of interaction trees; then infinite programs can be directly defined.However, it is not clear in this setting what the appropriate notion of models of an equational theory should be, let alone constructing free models.
Therefore the equational approach taken in this paper trades off direct support of recursion for the ability to define program equivalence by a set of equations directly.Consequently, program equalities proved on free models automatically hold in any other semantic models (Corollary 4.4).In contrast, the program equivalence of interaction trees is a bisimilarity induced by a specific operational semantics [Xia et al. 2020].Thus program equalities proven for the bisimilarity do not automatically hold for other models.It is worthwhile to reconcile these two approaches in the future, possibly by using guarded dependent type theory [Baunsgaard Kristensen et al. 2022;Bizjak et al. 2016;Sterling and Harper 2018] or constructive domain theory [de Jong and Escardó 2021].

AN EFFECT-GENERIC HOARE LOGIC
We have presented a full formalisation of algebraic effects which accounts for their equations.We will now demonstrate the power of this formalisation, in allowing for sophisticated reasoning about programs, culminating in a generic Hoare logic for algebraic effects.This logic is a constructive version of Schröder and Mossakowski [2003]'s monad-independent Hoare logic; what is novel is an elimination principle that connects Hoare-style reasoning and equational reasoning.
The Hoare logic of Schröder and Mossakowski [2003] is flexible: usually, Hoare logic is specific to stateful assertions; the logic we will develop here allows for assertions over any effect that can be encoded in our framework.As long as the assertion can be encoded as a term of type Term  (23) that is SEF and DET (25) it can be manipulated and expressed using Hoare logic.
Truth Values.In Homotopy Type Theory, truth values are represented by the following type: This is the type of propositions: true and false statements like "7 is a prime number" or "3 is even".A value of type  is a pair of a type ProofOf , representing the type of proofs of the proposition, and a constraint IsProp that ProofOf is a proposition in the HoTT sense.For example, the  value corresponding to "three is even" is as follows: The type of proofs of the (false) statement "3 is even" is an equality type 3 mod 2 ≡ 0, and it is a proposition since N is a set-equalities between any two natural numbers are propositions.In a classical setting, the booleans are a suitable type for truth values.Constructively, however, Bool represents only the decidable propositions.The  type above represents both true and false propositions, even if we do not necessarily know which a particular proposition belongs to.
The presence of the IsProp field means that the ProofOf type can have at most one inhabitant.This has the corollary that any biconditional propositions are equal: _iff_ This means that all true propositions are equal, as are all false propositions.In fact, there is no proposition that is not equal to one of the following values (∄. True ×  False): False :  ProofOf False = ⊥ IsProp False () The usual logical connectives can be defined on : The odd one out here is |∨|: its definition uses propositional truncation, ∥_∥, which makes the ProofOf type a proposition.Without it, ProofOf ( |∨|  ) could have multiple inhabitants (consider, for instance, ⊤ ⊎ ⊤).Propositional truncation is a special case of a quotient type: Propositional truncation is also useful for defining the truth value corresponding to the equality of two arbitrary values of some type , which is not necessarily a set in the HoTT sense: This allows us to talk about equality of values without always having to prove they are a set.
Assertions.It is often convenient to be able to state properties about an effectful program from within the program, with an assertion.For instance, with the parser effect, we might want to construct a predicate for parsers that do not consume any input: This program returns true when the state is the same before and after executing p.However, it constructs a computation full of truth values, rather than a truth value itself.
To convert from Term  to a proposition we will use G from Schröder and Mossakowski [2003].
The proposition G  corresponds to the statement that every truth value in  is True. here is allowed to have a return value, as well: this can be useful for chaining together predicates.The assertion no-input, then, can be encoded as: no-input p = G do s 1 ← get; x ← p; s 2 ← get; return (x , s 1 |≡| s 2 ) Pure Assertions.Usually, it is sensible to insist that assertions themselves do not produce effects.
They can of course depend on effects: for instance, an assertion in the parsing effect probably shouldn't modify the string being parsed, but it should be able to observe the string.The following pair of conditions capture this notion of semi-purity.SEF p = (do p; return ⟨⟩) ≡ return ⟨⟩ DET p = (do x ← p; y ← p; return (x , y)) ≡ (do x ← p; p; return (x , x)) (25) SEF (side-effect free) encodes that a program doesn't modify the ambient environment with some effect.This outlaws, for instance, programs like put x. DET (deterministic) encodes that the assertion itself can't be nondeterministic; this outlaws things like True ‹|› False.A "pure" assertion is an assertion that is both SEF and DET .Note that these notions of semi-purity instantiate to different notions of semi-purity depending on the underlying monad.In the case of state, SEF encodes a term that does not modify the state; on a monad for exceptions, SEF  is a statement that  does not throw an exception.
Pure assertions are still quite expressive because they can return any semantic propositions.For example, for the effect of state with the state type Loc → Maybe (Loc ⊎ Val) modelling a finite heap of some location type Loc and each allocated location stores either a pointer to another location or a value of type Val, separating conjunction in separation logic [Reynolds 2002a] can be expressed as do mem ← get; return (∃s 1 .∃s 2 .(is-disj-union mem s 1 s 2 ) |∧| p s 1 |∧| q s 2 ) which returns the proposition that the current allocated memory cells are the disjoint union of two heaps satisfying predicates  and  respectively.
Hoare Triples.We now have all of the components necessary to define Hoare triples.
Definition 6.1 (Hoare Triple).A Hoare triple { }  ←  {  } is a predicate over a program  : Term , and a pair of pure assertions  : Term  and  :  → Term .The predicate holds when  "implies"  , when , , and  are executed in sequence: We do not require the pre-and post-conditions be SEF or DET in the definition of Hoare triples, but instead take them as arguments to the inference rules or connectives where needed.For instance, the conjunction rule (26) requires SEF , DET , and SEF  .
As a small example, the no-input property can be encoded as a Hoare triple as follows: remaining : The combinator remaining asserts that the remaining string is equal to a supplied string.no-input p states that, for all strings s, if s remains before t's execution, then it will remain after t's execution.
Connectives on Assertions.Assertions can be combined using the standard logical connectives, giving a calculus for Hoare logic.We have, for instance, conjunction between two Hoare clauses: This says that if two assertions  and  hold after the precondition  and the program , then their conjunction also holds.Inference rules for other logical connectives exist as well.
Using Hoare Logic.Now that the fundamental components of Hoare logic have been defined, let's use it to verify the (partial) correctness of a simple program: the tree parser in Figure 2. We will not present the full proof here, just some highlights to demonstrate the power of the framework.First, let's construct a predicate for a parser returning a particular value.
The predicate  ∈  ↦ → v states that the parser  parses the string  and returns the value v.It does so by asserting that the string  is the prefix of the input (remaining( + +  )), running the parser p, and asserting that the value returned is equal to v and the rest of the string has not been altered.The parser we are interested in verifying is the tree parser Figure 2: This simple property asserts that, for any tree t, parsing the printed representation of t returns something equal to t.Note that this is a specification of partial correctness: as is the norm for Hoare logic, this specification only applies if the underlying program terminates successfully.Put another way, when the parser fails, any property holds.
( : This proof is built from small components, by pattern-matching on n and t.For instance, in the first case, when n = 0, the proof goal is: Since parse-tree zero = fail, this goal follows from (29).
We will look at one other case.When t = ♠ and n > 0, the parser should re-parse the result of printing the tree ♠.In Figure 2, notice that when n > 0, the parse-tree function produces three alternative parsers, combined using ‹|›.Parsers that use ‹|› can be verified with the following lemma: If a Hoare triple holds on p and q, then it holds on p ‹|› q.So, to verify this case of the proof we need to verify each of the alternative parsers.
Let's look at the two alternatives which parse ♠ and ♦.The proof obligations are: Both of these alternatives can be verified using the following lemma: This states that, if the remaining string starts with a character  1 , and char  2 succeeds, then  1 ≡  2 and the tail of the string remains.Notice that the postcondition here can be false:  1 need not equal  2 .The lemma is still valid, however, because if  1  2 then char  2 fails, so (29) applies.
Returning to the two alternatives, in both branches '♠' prefixes the remaining string.This means the first alternative succeeds, since it parses the character '♠', and returns the leaf ♠.
The second alternative, however, returns ♦, which makes the postcondition of the obligation return (♠ |≡| ♦) ⟨∧⟩ remaining s, which is clearly false.Luckily, applying (30) gives the following: The postcondition here contains '♠' |≡| '♦', itself a false assertion, from which we can derive any other assertion, including the obligation for this case.

RELATING HOARE-STYLE AND EQUATIONAL REASONING
Apart from being generic for the underlying computational effect, another appealing feature of the Hoare logic in Section 6 is that it is defined solely in terms of equations of programs.Therefore the two common ways for reasoning about programs-equational reasoning, which is traditionally more popular in functional programming, and Hoare-style reasoning, which is traditionally more popular in imperative programming-pleasantly meld together: a Hoare triple {  } x ←p {  x } is valid if and only if the following program equivalence holds: However, one might wonder if equation ( 31) is really "useful" for program reasoning, or is it just an arbitrary equality that encodes Hoare logic.For a minimal example, if we can use Hoare logic to show {} x ← p { return (x ≡ 0) }, does (31) imply that (do x ← p ; put x) ≡ (do p ; put 0)?To address this question, in this section we show the following "elimination principle" for Hoare logic that allows useful program equivalences to be extracted from valid Hoare triples.
Theorem 7.1 (H -elim).Given a theory with finite (Definition 7.2) variables and arities, let  : A → Ω be a predicate on some type , and let f and g be two functions A → Term B such that for all x : A, ProofOf ( x) implies f x ≡ g x.Then the following implication holds: At first glance, it might not be obvious why this theorem is difficult to prove, or what its significance is.It seems related to a simple congruence law, like: Indeed, this law holds straightforwardly: by function extensionality, since  and  are equal at every input (∀ .  ≡  ) then they are equal as functions ( ≡ ), and by congruence  ≡  implies  > > =  ≡  > > = .
In the case of H -elim, however, the continuations  and  are not equal everywhere: they are only equal where the proposition  holds on their input.This makes the job of H -elim more difficult: it must take a proof {}  ←  {return ( )} that  holds on the result of some term , and use that to prove that  and  are equal when executed after .
In the following, we first show that when the law of excluded middle (LEM) is assumed, H -elim can be proved fairly easily by a case split of whether  holds for each return value of .
Proof (with LEM).By definition the Hoare triple {}  ←  {return ( )} is the equality One formulation of LEM is  ≡ Bool, allowing us to change the type of the predicate to  :  → Bool.
We then construct a continuation Applying > > =  to each side of ( 33) yields Recall that the goal is  > > =  ≡  > > = .The remaining obligation, therefore, is: By cases on  , when   = False,   is returned and   ≡  .When   = True, then   is returned, but recall that ProofOf ( ) implies   ≡  , so the obligation is discharged.□ Of course relying on LEM means that constructivity is sacrificed-there may be no way to demonstrate a concrete derivation of a program equality produced by H -elim.If a fully constructive formalisation is desired, we have to take another route.Rather than inspecting the return value of  we will inspect the program equivalence (31) underlying the given Hoare triple derived using the rules of t (Section 4.2); this will give us the concrete structure allowing us to map a value  :  back to its position in its term, and from this derive the proof of  .Finally, this derivation will be modified to be the required program equivalence p > > = f ≡ p > > = g.
In order to map a value back to its position we will need to be able to search terms exhaustively for particular variables.This is only possible when the arity types of the algebraic theory are finite.The formal definition of finiteness we use here is Bishop finiteness [Bishop and Bridges 1985;Frumin et al. 2018;Kidney 2020;Yorgey 2014].
Definition 7.2 (Bishop Finiteness).A type  is bishop finite, E , if it is merely isomorphic to some finite prefix of the natural numbers.
Bishop finite types are searchable, meaning that for any decidable predicate  on a Bishop-finite type  we can decide if the predicate holds on any value of the type.This last predicate can be lifted and applied to syntax trees, meaning that any syntax tree with bishop-finite arities can itself be searched with a decidable predicate.
To modify the program equality encoding the Hoare triple to the goal p > > = f ≡ p > > = g, we will crucially rely on the fact that we can track the provenance in a derivation d : s t t.By provenance, what we mean is the correspondence between the leaf nodes of s and t if we view d : s t t as a process rewriting s to t.For example, suppose d : t 1 t t 2 uses an equation  =  ⊗  to rewrite a term  1 to another term  2 in one step, then the left subtree of  2 is necessarily  1 .Moreover, no matter how we modify the right subtree of  2 , the equivalence t 1 t t 2 still holds, since  can be taken to be anything when rewriting with the equation  =  ⊗ .
More precisely, the provenance of a derivation d : s   for ,  : Syntax A is a pair of terms s p , t p : Syntax I p for a discrete type I p , i.e. having decidable equality (while the type  may not have decidable equality) such that s p and t p are equivalent under  , and that there exists a function k p : I p → A which mapped to s p and t p : Syntax I p resulting in exactly  and  : Syntax A (that is to say,  and  have the same shape as s p and t p respectively).Provenance information is encoded in these data because the decidable equality of I p allows us to test if a leaf node of s p and a leaf node of t p is the same; if they are different, then we know that the corresponding leaf nodes at the same position of  and  can be different for the derivation s t t to go through.
Formally, the data for provenance are collected as the following record: record Provenance (s t : Syntax A) : Type where field I p : Type a I ?= : Discrete I p s p : Index s → I p t p : Index t → I p k p : In this record, the two terms s p and t p are represented as two functions s p : Index s → I p and t p : Index t → I p from indices into each respective tree, since they necessarily have the same shape as  and , differing only in leaf nodes.Terms can be reconstructed from such functions using fill.
At first, it may seem that there is an alternative design for the Provenance structure that is simpler and more intuitive.Instead of tracking every variable in the tree, we could instead track subtrees.In concrete terms, this would change the type of the k p field from I p → A to I p → Syntax , where k p returns the subtree at a particular index.This approach seems more natural in the context of equations like x ⊗ y = x, where clearly the succinct way to represent this rewriting step is as the removal of the whole subtree y.Our approach would instead represent this step as the field s p having a larger image than t p , which is much more indirect.
As is often the case with mechanised proofs, what is simple for humans is complex for computers.In this case, using indices that point to every value in the tree allows for a much more uniform representation that would not be possible with a subtree-based representation.In particular, this representation lets us construct an actual signature that corresponds to syntax trees, where the interpretation of that signature is isomorphic to Syntax.
Position : This representation means that the fields s p and t p can be easily converted to and from Syntax types, via the Indexed isomorphism.This isomorphism is witnessed by the lookup and fill functions.
The key lemma for showing Theorem 7.1 is the following.
Lemma 7.3.Given a pair of syntax trees  and  with   , in a theory with finite arities and variables, the type ∥Provenance   ∥ is inhabited.
Proof.The proof proceeds via induction on the relation   .refl t Given  : Syntax , we want to construct an element of Provenance  .The index for this case is I p = Index s.In other words an index into the original tree.And we let k p = lookup s.The other fields follow trivially since the copies of the trees are exact copies of .
sym t The case for symmetry is even simpler than refl t : given a Provenance  , we need to produce a Provenance  .Since the datatype is itself symmetric, we can simply switch around all the chiral fields, i.e. s p (sym t ) = t p ; t p (sym t ) = s p .
cong t Given an operator   , a pair of continuations   ,   : Arity   → Syntax , and provenance D at every point of Arity   , the task for the case of congruence is to construct an element of Provenance (op (  ,   )) (op (  ,   )).The index that we will construct for this case is: Again, the rest of the coherence proofs follow in a relatively trivial way.
eq t The eq t case is the most important of the proof: in the relation, this case is what can allow the shape of the equivalent trees to change, and it is where variables can be introduced or removed.Given an equation with left-hand-side lhs, right-hand-side rhs, and continuation , this case constructs Provenance (lhs > > = ) (rhs > > = ).The index here is a variable in the equations paired with an index into the continuation applied at that variable: The rest of the fields follow by applying the continuation and looking up in the result.trans t Perhaps surprisingly, this is the most difficult case of the proof.Given , ,  : Syntax , ℎ : Provenance  , and ℎ : Provenance  , the task is to construct an element of Provenance  .We need to pick an index here that can be remapped to every variable in either of the trees.We will use a (set) pushout: a pushout of two functions  :  →  and  :  →  is a disjoint union of  and , with the added quotient that alternate injections inl  and inr  are equal if there is some  that points to both (  ≡  ∧   ≡ ): data Pushout (f : Note that the trunc constructor makes this a set pushout, rather than a fully-general pushout type.The new index is the following pushout, i.e. a sum of the indices from both sides, with the quotient that if they point to the same variable in the middle () they are equal: I p = Pushout {A = Index u} {B = lhs.I p } {C = rhs.I p } lhs.t p rhs. s p (34) To reconstruct the original variable we will pattern match on the pushout, and apply k p from the left-hand-side or right-hand-side respectively: k p : I p → A k p (inl x) = lhs.k p x k p (inr x) = rhs.k p x k p (push i j) = (sym (lhs.t-k i) rhs.s-k i) j k p (trunc x y p q i j) = setA (k p x) (k p y) (cong k p p) (cong k p q) i j This function is then proved coherent by applying the combination of t-k and s-k from the left- Proof.Given two values ,  : Pushout  , we need to decide if they are equal.This is complicated by the presence of the pushout paths available: inl  ≡ inl  obviously holds, as does inr  ≡ inr , but pushout values can be equal in other ways.For instance, inl ( ) ≡ inr ( ) and, by extension, inl  ≡ inl  ′ if there is an  and  such that  ≡   ∧   ≡   ∧   ≡  ′ .In fact, there can be a chain of s representing a path between two pushout variables.Deciding if two pushout values are equal amounts to determining if such a chain exists.Since  is finite, we can enumerate all possible chains, and check each for coherence, yielding a terminating algorithm.
First, we characterise the type of paths in a pushout: A path is represented by a (possibly empty) list of s; the Push predicate returns if a list forms a coherent path between two sums.
Every path between two sums is represented by at least one such chain: we can prove this by showing the relation ≈ * is reflexive, transitive, and symmetric, and supports the push relation.
Notice, however, that a chain can have loops, where the same  is passed through multiple times.A loopless path is one with no duplicate s: Any such chain can be shortened yielding an equivalent path simply by cutting out the loop: As a result, any path between two sums must be represented by at least one loopless chain.
Finally, we know that there are only finitely many lists of s without duplicates (if  is finite).As a result, there are only finitely many "loopless" paths between two sums; since  and  are discrete we can test all of these for coherence, meaning we can exhaustively search for all such paths, yielding an algorithm for decidable equality. □ Finally, we are ready to prove H -elim (Theorem 7.1) assuming that all arity types of the algebraic theory are finite (Definition 7.2) but not LEM.
Proof Sketch.The first step of this proof is to dispense with the Term and equality, and replace them with Syntax trees and  .The two are equivalent by the effectiveness of quotients, although we had to be careful to preserve coherence when translating between the two representations.Concretely, this means that our goal has changed to the following for some [] = : where f ‹$› s = do x ← s ; return (f s), and ? = x →(x ,  x), and T = x →(x ,True).
First, we will apply Lemma 7.3 to build Provenance (? ‹$› ) (T ‹$› ), from which we will extract eqv p .We will then apply a continuation  ′ to each side of eqv p , and finally we will show that the result is equal to This takes an index  : I p and finds the variable it points to with k p , and then if the index is in the left-hand-side tree it applies  , otherwise it applies .
The expression does (∈s?i) returns a Boolean indicating whether or not the index i is present in the tree s.To search a tree for a particular value the tree must be finite; this is the case since all trees are well founded and we have assumed that all the arities are finite.
Next we will prove this continuation is coherent.For the left-hand-side we have: This says that, for a position in the left-hand-side tree, applying  ′ to the index at that position in the copy is the same as applying  to the variable at that position.Similarly for the right-hand-side, ∈t p ⇒g : (i : We will not go into the details of these respective proofs (they involve  and the fields s-k and t-k).
However, they can be used to construct the following proofs: These combined with eqv p give the final goal  > > =    > > = .□ Remark.The constructive proof of H -elim above requires that all arities be finite.This means that, for instance, the constructive proof of H -elim applies to the parser example of this paper only when strings stored in the mutable state are finitely bounded.This is a reasonable constraint: after all, computers in the real world have finite memory.However, note that the nonconstructive proof of H -elim does not need finiteness, so constructivity can be traded off for infinite arities.
Applying H -elim to the Parser.Finally we demonstrate how to apply Theorem 7.1 to the parser example (Figure 2), to derive the program equality (1) from the round-trip lemma (28).First, recall the definition of s ∈ p ↦ → v, given in ( 27).Expanding some definitions yields the following: Using some of the standard Hoare combinators we convert the above to: This is essentially a rephrasing of ( 27): "when s ∈ p ↦ → v holds, prepending s to the input stream and then running the parser p returns the value v and leaves the input stream in its original state".
From here, we can apply H -elim to turn the equalities in the postcondition to program equalities: (do s ← get; put (print t + + s); t This equality states that the value returned by parse-tree will indeed be the correct tree.By the state and monad laws, the equation above simplifies to (do push (print t); parse-tree n) ≡ (do push (print t); parse-tree n; put ; return ) This equation and the goal (1) already have the same left-hand side.To simplify the right-hand side, we use the following lemma.
Lemma 7.5.For the effect of parsing, if a term  : Term  is total, in the sense that there exists some syntax tree  : Syntax  such that [  ] =  and  does not contain the operation fail in its all subtrees, (do ; put ) = put  Using this lemma, if we assume that the program do {push (print t); parse-tree n} is total, which is in fact true for sufficiently large step-index , then (35) immediately implies our goal: (do push (print t); parse-tree n) ≡ return t

RELATED WORK
Algebraic Effects.This paper is basically a formalisation of universal algebra [Birkhoff 1935;Cohn 1981], whose connection to computational effects was first developed by Plotkin andPower [2002, 2004] and Hyland et al. [2007Hyland et al. [ , 2006]].Plotkin and Pretnar [2008] also introduced an equational first-order logic tailored for reasoning about programs using algebraic effects.In contrast, our formalisation is embedded in Cubical Agda.The expressiveness of Cubical Agda makes it possible to implement all the logic connectives and inference rules in [Plotkin and Pretnar 2008] as just functions over the datatype Term and the universe Ω of propositions.For example, their principle of induction over computations is subsumed by the usual induction principles of Term and Syntax.Plotkin andPretnar [2009, 2013] internalised homomorphisms out of the free model of an algebraic theory as a programming language feature-effect handlers.Since then, many researchers have developed the theory and implementation of effect handlers, to name a few, CPS translations [Schuster et al. 2022[Schuster et al. , 2020]], effect systems [Bauer and Pretnar 2013;Leijen 2014], parametricity [Biernacki et al. 2017;Zhang and Myers 2019], type dependency [Ahman 2017].Our paper is not about effect handlers qua language construct, but future work may use our library to build upon.
Formalisations.Our paper is certainly not the first formalisation of universal algebras in type theory.Previous formalisations include the work by Gunther et al. [2018], Abel [2021], and DeMeo and Carette [2022] in Agda and Capretta [1999]'s formalisation in Coq.These formalisations use setoids to deal with quotients, while our formalisation uses higher inductive types, which is typically regarded as easier to work with.A more important difference of our formalisation from these works is that we focus on the applications of universal algebras in computational effects.
There is a very rich literature on frameworks of formalising effects in proof assistants, especially Coq, that we cannot provide a full survey here.Xia et al. [2020] used interaction trees, also known as free completely iterative algebras [Aczel et al. 2003] in category theory, which is a coinductive version of the Syntax monad to formalise effectful programs with recursion in Coq.In comparison, in our library recursive programs have to be formalised with a fuel argument.However, the strength of our library is that it avoids setoids, and it provides a generic Hoare logic for reasoning about effects more intuitively.Li and Weirich [2022] introduced and formalised Tlön embeddings and program adverbs which are a generalisation of free monads that encompass non-monadic notions of computational effects such as applicative functors.Saito and Affeldt [2022] formalised in Coq a hierarchy of monad classes and many applications of monadic equational reasoning.Fiore et al. [2022] show that quotient-inductive types can be constructed in the type theory of toposes with natural numbers and universes satisfying the "Weakly Initial Set of Covers" axiom.In this setting, it would be unnecessary to require the arities type in Term to be quotient preserving.However, sets in HoTT do not form a topos but only a Π -pretopos so their construction does not apply.It is worth investigating whether their construction can be adapted to HoTT.
The agda-unimath library [Rijke et al. 2021] recently added a formalisation of universal algebra in HoTT independently of ours (and we would like to thank the reviewers for bringing it to our attention).Presently it is a relatively small formalisation and features a proof that the quotient of a model by a congruence relation is again a model, whereas our formalisation aims to be a full-fledged framework for verifying programs with algebraic effects.Generic Hoare Logics.The Hoare logic in our library is based on Schröder and Mossakowski [2003]'s monad-independent Hoare logic formalised in the HasCasl specification language [Schröder and Mossakowski 2002].Our new development is the fully constructive proof of the elimination principle of Hoare logic that we saw in Section 7.
A variation of the idea of encoding Hoare logic as program equations is proposed by Goncharov and Schröder [2013].Under the assumption that there is a certain kind of CPO structure on the Kleisli arrows of a monad  , they devise a relatively complete Hoare logic for  -computations.In this Hoare logic, assertions are encoded as programs returning a unit value instead of a proposition; if the assertion is not met, the corresponding program should fail.The advantage of this framework is that this construction can be carried out on categories that do not have a universe of propositions.Hasuo [2015] generalised Goncharov and Schröder [2013]'s construction by allowing weakest precondition transformation to be specified by any Eilenberg-Moore algebras satisfying certain conditions.This flexibility makes this framework expressive enough to cover both total correctness and partial correctness for various kinds of effects.
Aguirre and Katsumata [2020]'s fibrational framework seems to be the most general form of generic Hoare logic so far.In this framework, assertions no longer need to be programs, as long as there is a way to do weakest precondition transformation along Kleisli arrows-captured as the categorical concept Grothendieck fibrations.For example, "assertions" of the effect of probabilistic choice can be the remaining running time of a program, which is just a number, and the weakest precondition transformation computes the expectation of the running time.
Logic and Refinement.The idea of unifying Hoare-style reasoning and (in)equational reasoning has been proposed before, especially for separation logic [Frumin et al. 2021;Gäher et al. 2022;Liang and Feng 2016;Song et al. 2023;Turon et al. 2013].Turon et al. [2013] introduce an expressive concurrent separation logic, CaReSL, with which program refinement can be encoded as certain Hoare triples.In contrast, our framework works with program equivalence rather than program refinement, and more importantly we take program equivalence as the primitive concept and Hoare logic as the derived concept.In this aspect, our framework is closer to Song et al. [2023]'s conditional contextual refinement, which takes program refinement as the basic concept and on which a notion  ⊢  1 ⊑  2 of a program  1 refining  2 under the pre-condition  is defined, where  is a separation logic formula.Such conditional refinement can be used to define Hoare triples.The main difference between Song et al. [2023]'s framework and ours is that Song et al. [2023] have fixed the effects (IO, mutable state, nondeterminism, function calls), and use a specific trace semantics of these effects to define the basic notion of program refinements, whereas our framework is completely generic to the effects, whose semantics is specified by an algebraic theory.
Dijkstra monads [Maillard et al. 2019;Silver and Zdancewic 2021;Swamy et al. 2016Swamy et al. , 2013] ] are another approach to specifying and verifying monadic computations, using a separate monadic construction-the Dijkstra monad itself-to build and compose specifications over some base monad.Specification using Dijkstra monads allows the specification of arbitrary computations of type  , but expects the specifier to define a bespoke "specification monad" for the particular effect in question.Our work, on the other hand, requires computations to be defined as algebraic effects, but provides a generic specification framework automatically.That said, it is possible to define a Dijkstra monad over an arbitrary Term monad, which would be worth exploring in future work.

CONCLUSION
This paper presents a formalisation of algebraic effects in Cubical Agda that is then extended with a Hoare logic.We hope that this library can be a useful building block for future programming language designers seeking to formalise the semantics of their languages with effects in Agda, as well as programmers who would like to verify their effectful programs.
Future work abounds.One direction is to improve the support for recursive programs.A promising approach is to adopt Clocked Cubical Type Theory [Baunsgaard Kristensen et al. 2022], which provides guarded recursion and coinductive counterparts of higher-inductive types.
Another direction is to support more general forms of algebraic effects such as parameterised algebraic effects [Staton 2013[Staton , 2015] ] for dynamically created instances of effects, scoped effects for operations that delimit scopes [Piróg et al. 2018;Wu et al. 2014;Yang et al. 2022], latent effects for effects which incorporate advanced control flow mechanisms, [van den Berg et al. 2021], and their generalisations into monoids with operations [Yang and Wu 2023].
Also, it is worthwhile to extend the effect-generic Hoare logic.Currently the logic is only for partial correctness, but it should be relatively easy to adapt the Hoare logic to account for total correctness.Also, we would like to investigate separation logic [Reynolds 2002b] in the effect-generic setting and look for an axiomatisation of algebraic effects that admit reasoning by separation logic.
Finally, there is still a lot of room for improvement regarding the user experience of program verification with our library.The speed of type checking in Agda 2.6.2 can sometimes be quite slow when the definitions get complicated, which can probably be mitigated using controlled unfolding [Gratzer et al. 2022] that is already available in Agda 2.6.4.Moreover, many simple equational rewriting steps are still needed when verifying a program in our framework.In the future we plan to resolve this by incorporating solvers for decidable algebraic theories into our framework.
With these future prospects in mind, we anticipate that our work can be the starting point of a powerful and easy to use program verification framework that reaps the rewards of the advancements in modern type theory and denotational semantics.
-side and right-hand-side respectively; these proofs show that an index pointing into the same place in the middle tree  is equal.The trees s p and t p are given by:s p : Index s → I p s p = inl •lhs.s p t p : Index t → I p t p = inr • rhs.t p By the definition of the quotient, we have fill (shape ) s p ≡ fill (shape ) t p , which satisfies the eqv p coherence property.What remains is to show the index type (34) has decidable equality, i.e. being discrete, and it is a consequence of the following lemma.□ Lemma 7.4.A Pushout ( :  → ) ( :  → ) is discrete if  and  are discrete and  is finite.