Contract lenses: Reasoning about bidirectional programs via calculation

Abstract Bidirectional transformations (BXs) are a mechanism for maintaining consistency between multiple representations of related data. The lens framework, which usually constructs BXs from lens combinators, has become the mainstream approach to BX programming because of its modularity and correctness by construction. However, the involved bidirectional behaviors of lenses make the equational reasoning and optimization of them much harder than unidirectional programs. We propose a novel approach to deriving efficient lenses from clear specifications via program calculation, a correct-by-construction approach to reasoning about functional programs by algebraic laws. To support bidirectional program calculation, we propose contract lenses, which extend conventional lenses with a pair of predicates to enable safe and modular composition of partial lenses. We define several contract-lens combinators capturing common computation patterns including 
$\textit{fold}, \textit{filter},\textit{map}$
 , and 
$\textit{scan}$
 , and develop several bidirectional calculation laws to reason about and optimize contract lenses. We demonstrate the effectiveness of our new calculation framework based on contract lenses with nontrivial examples.


Introduction
A bidirectional transformation (BX) is a pair of mappings between source and view data objects, one in each direction.When the source is updated, a (forward) transformation executes to obtain an updated view.For a variety of reasons, the view may also be subjected to direct manipulation, requiring a corresponding (backward) transformation to keep the source consistent.Much work has gone into this area with applications in databases (Bancilhon & Spyratos, 1981;Bohannon et al., 2006;Tran et al., 2020), software model transformation (Stevens, 2008;He & Hu, 2018;Tsigkanos et al., 2020;Stevens, 2020), graph transformation (Hidaka et al., 2010), etc; in particular, there has been several language-based approaches that allow transformations in both directions to be programmed together (e.g., Foster et al., 2007;Matsuda et al., 2007;Voigtländer, 2009;Ko et al., 2016).
The lens framework (Foster et al., 2007) is the leading approach to BX programming.A lens consists of a pair of transformations: a forward transformation get producing a view from a source, and a backward transformation put which takes a source and a possibly modified view, and reflects the modifications on the view to the source, producing an updated source.It can be represented as a record using Haskell-like notations as: The additional argument S in put ensures that a view does not have to contain all the information of the source for backward transformation to be viable.
These two transformations should be well behaved in the sense that they satisfy the following round-tripping properties: put s (get s) = s GETPUT get (put s v) = v PUTGET The GETPUT property requires that no-change to the view should be reflected as no-change to the source, while the PUTGET property requires that all changes in the view should be completely reflected to the source so that the changed view can be successfully recovered by applying the forward transformation to the updated source.
One main advantage of lenses is their modularity.The lens composition 1 ; 2 : S ↔ T of lenses 1 : S ↔ V and 2 : V ↔ T is defined as:1 1 ; 2 = Lens g p where g = get 2 • get 1 p s t = put 1 s (put 2 (get 1 s) t ) In the forward direction, lens composition is simply a function composition of the two get functions.In the backward direction, it will first put the updated t back to the intermediate v produced by get 1 s using 2 , and then put the updated v back to s.
Lenses are programmed in special languages that preserve round-tripping properties by construction.One popular type of such languages are lens combinators, that is, higherorder functions that construct complex lenses by composing simpler ones.Designing lens languages that are expressive and easy-to-use has been a popular research topic (Bohannon et al., 2008;Foster et al., 2008;Barbosa et al., 2010;Hofmann et al., 2011;Matsuda & Wang, 2015a, 2018;Ko et al., 2016), effectively creating the paradigm of bidirectional programming.
This flourishing scene of languages invites the next question of software development: what are the suitable methods of BX program construction?
Is there an algebraic theory of lens combinators that would underpin optimization of lens expressions in the same way that the relational algebra and its algebraic theory are used to optimize relational database queries?... This algebraic theory will play a crucial role in a more serious implementation effort.(Foster et al., 2007) Motivated by this question, we propose a calculation framework which optimizes lenses over lists from clear specifications using the algebraic structures of lens combinators.

Program calculation and the challenge of partiality
Program calculation (Bird, 1989b) is an established technique for reasoning about and optimizing functional programs.The idea is that program developments may benefit from simple properties and laws: equivalences between programming constructs.And consequently, one may calculate with programs -in the same way that one calculates with numeric quantities in algebra -to transform simple specifications into sophisticated and efficient implementations.Each step of a calculation is a step of equational reasoning, where properties of a fragment of the program, such as relations between data structures and algebraic identities, are applied to transform the program structure.A great advantage of this method is that the resulting implementation is guaranteed to be semantically equivalent to the original specification, removing the onerous task of verifying the correctness of the resulting implementation.
Our observation is that program calculation is a good fit to BX programming in a number of different ways.In terms of philosophy, both advocate correctness by construction aiming at significantly reducing the verification and maintenance effort.In terms of representation, both rely heavily on forming programs using composition and computation patterns: in BX languages, the computation patterns are typically captured as lens combinators which are designed to preserve well-behavedness, and in program calculation, the use of computation patterns allows general algebraic laws such as fusion laws and Horner's rule (Gibbons, 2002(Gibbons, , 2011) ) to be applied to specific instances without the need of special analysis.
However, the more complex setting of BX as compared to unidirectional programs posts unique challenges to program calculation.First of all, calculating BX cannot be superficially treated as calculating twice, once in each direction, as the round-tripping properties bind get and put closely together, demanding simultaneous reasoning with both.Moreover, lenses are often partially defined, making it hard to reason about the construction and compositions of combinators like map, fold, and scan.Semantic preservation amid calculation is difficult in this case as well (note that a change in the definedness of a function changes its semantics).
In this context, the term partiality links to round-tripping properties.A lens is partial when its put component cannot successfully restore consistency for certain inputs, even if this function is total (Stevens, 2014). 2 This partiality can be inherent, where the get component is non-surjective; there is no meaningful put semantics for values outside the codomain of get.This partiality can also be of design choices, as forcing a lens to be total may introduce unwanted complexity.As an example, consider following definition of list mapping as a (high-order) lens which takes a lens of type A ↔ B and return a lens of type This lens is partial: when the view list is updated to be longer, the put component cannot deal with the inconsistency of the structure (length) between the original source list and the updated view list correctly; it only returns a new source list of the same length as the original one.As a result, the PUTGET property is broken, as shown by , 3] where bid is the trivial identity lens.It is common in practice to assume that only certain view updates are permitted, for example, the length of the view list is preserved.With such an assumption, bmap serves as a correct lens.
As a remark, for some lenses such as bmap, it is possible to make their definitions total without contracts and any other constraints on sources and views by using more complicated machinery such as default values (Foster et al., 2007;Pacheco & Cunha, 2011).However, giving total definitions to lenses (especially their put components) requires more involved types and semantics and leads to extra programming work for designing lenses.It is totally not necessary to endure this extra complication when we can guarantee that the changes on views always satisfy certain constraints, such as preserving the structures (lengths) of views.Moreover, forcing total definitions also results in challenges to the development of calculation laws, again due to the additional complications of types and semantics.For example, the calculation law of bmap with default values will require additional semantic conditions on them as shown in Appendix A.
In this work, instead of insisting on giving total definitions to all lenses, we use a pair of predicates to constrain the changes on the source and view so that partial lenses can be constructed correctly and composed well.It also facilitates the development of simple but powerful calculation laws.

Contributions
In this paper, we develop a calculation framework to reason about and optimize bidirectional programs over lists.Our goal is to transform lenses with clear specifications to efficient ones by applying calculation laws.Specifically, we propose an extension to traditional lenses, which we call contract lenses, to enable the construction and composition of possibly partial lenses.We develop several contract-lens combinators, which are highorder functions that characterize key bidirectional computation patterns on lists.And we establish related calculation laws that lay the foundation of a general algebraic theory for BX calculation.
Contract Lenses.The main idea of contract lenses is to utilize a pair of fine-grained predicates, one on source and one on view, to characterize the bidirectional behavior on propagating changes in a compositional way.Composition of contract lenses is justified by the implication relation between the view predicate of the former lens and the source predicate of the latter lens.We also provide an equivalence relation between contract lenses for calculation.(Section 4) Contract-Lens Combinators.We develop bidirectional computation patterns on the list data structure using contract lenses, including bidirectional fold, map, and scan.An interesting finding is that some bidirectional versions of map and scan cannot be expressed as instances of bidirectional fold due to the requirement of maintaining the consistency of inner dependencies of data structures (Section 5).

Contract-Lens Calculation
Laws.We establish calculation laws that transform compositions of such combinators into equivalent but efficient forms.We provide bidirectional versions of many algebraic laws, including fold fusion, map fusion, fold-map fusion, and the scan lemma.These laws comprise a bidirectional algebraic theory that manipulates lenses directly, which underpins the optimization of bidirectional programs (Section 6).
Mechanized Proofs in Agda.We prove the technical details of our calculation framework in Agda, including the correctness of all contract-lens combinators and calculation laws, as well as most of the examples.The proof consists of 4k lines of Agda code (Section 9 and Supplementary Files).
Moreover, we showcase the ability of our framework to construct and calculate lenses by advanced examples that either have intricate partial bidirectional behaviors or are well studied in both BXs and program calculation literature (Section 7).Section 8 discusses related works, and Section 10 concludes.
One thing worth noting is that our primary goal is to propose a calculation framework without restricting to any specific reasoning method.Users are free to calculate contract lenses with pencil/paper proofs following the tradition of program calculation (Bird, 1989b) or formalize the calculation via theorem provers like our mechanized proofs in Agda.It is even possible to develop automatic reasoning tools based on our framework.

Background: Program calculation
Program calculation (Bird, 1989b;Gibbons, 2002) is a technique for constructing efficient programs that are correct by construction.It is suitable for humans to derive efficient programs by hand (Bird, 1989b), as well as for compilers to optimize programs automatically (Gill et al., 1993;Hu et al., 1996).The principle of program calculation is to express the initial specification of the programming problem in terms of a set of higher-order functions, which support generic algebraic laws, so that an efficient implementation can be calculated through a process of equational reasoning based on the algebraic laws.

Specification with folds
Fold is a computation pattern that captures structural recursion.In Haskell, there are two versions of fold on list: foldl which can be used to define a range of functions.We give some examples as follows, which are also used in the remainder of the paper: Here, maximum computes the maximum of a list, sum sums up all the elements in a list, map f applies function f to each element of a list, filter p accepts a list and keeps those elements that satisfy p, scanr keeps the intermediate results of foldr in a list (similarly we have a scanl), inits returns all initial segments (prefix lists) of a list, and tails returns all tail segments (postfix lists) of a list.Note that foldr f e has two arguments, which can be combined into one foldr alg where alg is a function of type Either One advantage of writing foldr this way is that it can be generalized to arbitrary algebraic data types such as trees (Gibbons, 2002), and its dual unfoldr can be easily defined: There are some variants of the above functions that will be used later: The main difference is that they remove the empty list from the result.For example, Note that the functions defined with fold are all executable programs.But we call them specifications in the context of program calculation because such definitions (despite being clear and concise) are not necessarily efficient (especially when multiple folds are composed together).Program calculation is about turning such specifications into more efficient (though likely less clear) implementations.

Algebraic laws
The foundation of program calculation is the algebraic laws, which can be applied step by step to derive efficient implementations.The most important algebraic law for fold is the foldr fusion law: It states that a function h composed with a foldr can be fused into a single foldr if the fusible condition h • f = g • F L h is satisfied.Note that F L is the so-called list functor, which is defined by: where + and × on functions are defined by (f The function const and id are defined by const x = x and id x = x.
There is a corresponding fusion law for foldl too.And for some special cases of fold, the fusible conditions are always satisfied and therefore omitted from the laws: Note that F m is the so-called map functor, which is defined by: It is worth noting that it is possible for an algebraic law to abstract a complex derivation step.For instance, the following Horner's lemma shows a big step to fuse a complex composition into a single foldl.
Lemma 1 (Horner's Rule).Let ⊕ and ⊗ are associative operators.Suppose ⊗ distributes through ⊕ and b is a left-identity of ⊕, then: where x y = (x ⊗ y) ⊕ a, and a is the value passed to foldl (⊗).

A calculational example
The maximum segment sum problem (mss for short) is to compute the maximum of the sums of the segments in a list.Developing an efficient implementation of it is challenging, and it has become a classic example to show off the power of program calculation.
The idea is to start with a straightforward specification as follows: Given a list, we first enumerate all the segments by map tails • inits.Then we calculate the sum of all segments by map (map sum) and get the maximum of these results of sum by maximum • map maximum.This implementation is easy to understand but very inefficient (O(n3 ) where n is the length of the list).Through program calculation, one can step-by-step rewrite the program through applying a sequence of algebraic laws to reach a version that has time complexity O(n).The details of the calculation can be found in Bird (1989a).The challenge that this paper aims to address is: Can the same be done for bidirectional programs -deriving efficient lenses from clear specifications?

Overview
In this section, we informally introduce contract lenses and demonstrate how they facilitate the construction of a bidirectional program calculation framework.

Taming partiality with contract lenses
The core idea of contract lenses is to enrich traditional lenses with source and view conditions (also called contracts) restricting the changes on source and view, as below: where is a lens with only get and put.The contracts are highlighted through the paper.Though we write cs and cv around the lens for readability in this section, a contract lens is formally defined as a four-tuple consisting of get, put, cs, and cv. 3  This is a BX setting, so we assume that it is the views that are actively updated and the sources are passively changed accordingly.Given a source s and an updated view v, the view condition cv is a predicate that takes two arguments: the original view get s and updated view v, restricting the permitted values of the updated view in relation to the original view.The source condition cs has a similar structure.It takes two arguments: the original source s and the updated source put s v, specifying an invariant that must hold for source changes as a result of valid view changes.
For the list mapping lens bmap we have seen in the introduction, we are interested in a condition that rules out any changes to the structure (length) of the view, which we specify as the following predicate: This condition is enough to ensure that the put component of bmap can always restore consistency between the updated view and source without breaking the round-tripping properties.In addition, we can conclude for bmap that if the view length does not change, the source length does not change either.This gives rise to the following contract lens where {eqlength} serves as both the view and source conditions:4 {eqlength} bmap {eqlength} Two lenses can be composed if the view condition of the former matches the source condition of the latter.For example, we can compose two bmaps: With contract lenses, the partiality issues of lens composition is reduced to local reasoning of adjacent conditions.Moreover, since we always want the modification on view (and source) to satisfy the contracts, the round-tripping properties also only need to hold when the contracts are satisfied, which significantly simplify the design of lenses.For instance, when designing {eqlength} bmap {eqlength}, we do not need to consider how to put back the changes to source when the length of view is changed any more.
The idea of introducing contracts is natural because when updating a view of type V in a BX setting, we usually want the updated view to satisfy certain constraints (like being of the same length as the original view), instead of allowing it to be any value of type V .Another option of solving the partiality problem is to give total definitions to all lenses.However, as we have discussed in Section 1.1, it leads to several obstacles to designing lenses and developing a calculation framework, which we avoid by using contracts lenses.

Calculation with contract lenses
Once we have established the composition of contract lenses, we can start to design a calculation framework for lenses.
For the sake of demonstration, we start with a contrived example: given a list of nonempty lists, we extract all head elements of the lists and then filter out the even elements.(More realistic examples will be given in Section 7.) In the unidirectional setting, one can apply the FOLD-MAP FUSION law to fuse the two passes of the list as follows:5 filter even • map head = { expressing filter as foldr } foldr (λa r → if even a then a : With contract-lens combinators, we can give a bidirectional version of the specification: {eqlength} bmap bhead {eqlength}; {eqlength} bfilter even {ceven} where bhead = CLens head (λxs x → x : tail xs) The view condition of bfilter even is defined as: which depends on the predicate even. 6The combinator bfilter is a bidirectional version of filter implemented by bfoldr , which is a bidirectional version of foldr with contracts (Section 5).We have already seen bmap in Section 1.1.In this example, bfilter even is also given source and view conditions including eqlength, which is needed to be composed with {eqlength} bmap bhead {eqlength}.The contracts of {eqlength} bfilter even {ceven} make sense: if the number of even elements is not changed, the total number of elements will neither be changed because the odd elements, which do not appear in the view, remain invariant.
The advantage of calculating with contract lenses is that we only need to care about the round-tripping properties under the source and view conditions, which simplifies the design of lenses, and as a result simplifies the calculation laws.For bfoldr , we have a bidirectional version of FOLD-MAP FUSION law called BFOLDR'-BMAP FUSION, with which we can bidirectionalize the calculation process of filter even • map head we have seen before:7 {eqlength} bmap bhead {eqlength}; {eqlength} bfilter even {ceven} = { expressing bfilter as bfoldr } {eqlength} bmap bhead {eqlength}; {eqlength} bfoldr (bfilterAlg even The bmapF is a bidirectional version of F m used in the FOLD-MAP FUSION law, and the bfilterAlg even is a bidirectional version of λa r → if even a then a : r else r defined in Section 5.1.2.
This "banality" of the calculation is the strength of our framework, as we have successfully set up a system that allows programmers to reason about lenses in almost exactly the same way as they have done for unidirectional programs for decades.In the rest of the paper, we will formally develop the contract lens framework and continue to demonstrate the kind of reasoning that it enables through examples far more advanced than the ones we have seen in this section.

Contract lenses
In this section, we formally define contract lenses, a natural extension of the traditional lenses with contracts.This novel construction enables us to express a wide class of partial BXs while ensuring safe and modular composition.

Contract lenses
Lenses essentially manipulate changes.A put propagates a change in view back to a change in source with respect to a get function.As we have already seen in Section 3, to guarantee correct change propagation, we extend lenses with a pair of constraints, cs and cv, describing the conditions of changes in the source and the view, respectively.

Definition 1 (Contract Lenses). A contract lens 8 between source of type S and view of type V consists of a pair of transformations get and put together with a pair of relations:
a source condition cs : S → S → Set and a view condition cv : where the following round-tripping properties are satisfied for every s : S and v : V: For backward transformations, the BACKWARDVALIDITY law and the CONDITIONEDPUTGET say that if the change in the view satisfies cv, then the change in the source should satisfy cs, and the put-get law holds.For forward transformations, the FORWARDVALIDITY law and the CONDITIONEDGETPUT say that if the source s satisfies cs s s, then the view get s should satisfy cv, and the get-put law holds.The condition cs s s in the CONDITIONEDGETPUT law is necessary to keep the system consistent: if the get-put law put s (get s) = s holds, replacing v with get s in the BACKWARDVALIDITY law, we have cs s (put s (get s)) = cs s s.The BACKWARDVALIDITY law and FORWARDVALIDITY law are important for the proof of the Theorem 1, which states that the composition of contract lenses preserves round-tripping properties.Essentially, they guarantee that the contracts are propagated by get and put.
We have a few remarks to make here.
First, as we have discussed in Section 1, all functions including get and put components of lenses are total in this paper.For simplicity, some function definitions are abridged and lack some catch-all patterns.Complete definitions of these functions can be found in the Agda formalization.
Second, to be more consistent with our Agda formalisation, we use the Set type in Agda to represent the type for predicates.Note that any value b of type Bool can be transformed into Set by using the expression b = True.For readability, we allow this transformation to be implicit in the rest of the paper.That is to say, anywhere a value of type Set is needed, we can fill in a value of type Bool.
Third, the role of source conditions in contract lenses are primarily for describing the "effect" on source updates after ruling out those view updates, which can be seen in the rule BACKWARDVALIDITY: when inputs are restricted to satisfy the view condition, the corresponding outputs are guaranteed to satisfy the source condition.This guarantee is necessary for contract-lens composition.The rule FORWARDVALIDITY and CONDITIONEDGETPUT are conditioned on cs s s, a predicate on the identity source update, which should hold in most of the cases.The requirement here is necessary for proving the correctness of contract-lens composition.Also note that even though we add conditions to the traditional GETPUT and PUTGET laws, we do not weaken the properties of lenses.Since we always want them to hold, the condition cv (get s) v should always be satisfied when we compute put s v, and the condition cs s s should always be satisfied when we compute get s. 9  We use the following notational conventions: • We use cs , cv , get , put to refer to the source condition, view condition, forward transformation, and backward transformation of a contract lens , respectively.
• Lists start from index 1 and the notation x i refers to the i-th element of a list x.Now we give some simple examples of contract lenses.We leave more interesting examples in Section 5.

Example 1 (Embedding Traditional Lenses into Contract Lenses). As contract lenses are extensions of traditional lenses, traditional lenses can be embedded into contract lenses by adding dummy conditions ctrue, where ctrue
= .
Example 2 (Bidirectional Inits).An interesting example is a bidirectional version of inits defined in Section 2.1.The view condition essentially describes the range of the inits .It is a little complicated, but this kind of detailed specification is needed for calculation: binits With the help of the condition on the view change (which keeps the "inits" structure), our putback function becomes very simple, just returning the last element if it is not empty.

Composition of contract lenses
Contract lenses are compositional, which is similar to that of traditional lenses, except that we need to be sure that the change conditions match well.
Definition 2 (Composition of Contract Lenses).For two contract lenses 1 : S ↔ V and Theorem 1 (Well-behaved Composition).For any two contract lenses 1 : S ↔ V and 2 : V ↔ T, their composition 1 ; 2 : S ↔ T satisfies the round-tripping properties.
Notice that we not only require the backward implication cs 2 v v ⇒ cv 1 v v , but also the forward one cv 1 v v ⇒ cs 2 v v. Intuitively, the latter is used to establish a connection between the FORWARDVALIDITY law of 1 and 2 .Moreover, we can strengthen the condition of composition to make it easier to use.We say that two predicates The condition of composition can be strengthened to cs 2 ⇔ cv 1 , which is sufficient in most cases.

Equivalence of contract lenses
Now we define an equivalence relation over contract lenses.

Theorem 2 (Lens Equivalence is an Equivalence Relation). The equivalence relation between contract lenses is reflexive, symmetric, and transitive.
There is nothing special about this definition of the equivalence relation.The equivalence relation for contract lenses is the base for our equational program reasoning and plays an important role in developing our program calculation theory of contract lenses.

Contract-lens combinators
Lens combinators have become a popular approach to programming BXs because of their modularity and correctness-by-construction.In this section, we define several lens combinators to capture fundamental patterns (higher-order functions) for easy construction of complex contract lenses in a compositional manner as well as to demonstrate the expressiveness and flexibility of our new contract lens framework.
Since BXs can be considered as unidirectional forward programs with additional put semantics, our idea is to bidirectionalize widely used recursion schemes in (forward) functional programming including fold, map, filter, and scan.The main challenge is that these functions are usually not bijective, which requires contracts to make them total and suitable for calculation.Different contracts will lead to different bidirectional version of the same high-order functions and are useful for different situations.We will give both total bidirectional versions of these functions, and their variants which have some additional conditions on the source and the view to make them flexible for composing with each other.It will be interesting to see later that although map and scan can be implemented by fold, it turns out to be more useful to implement bidirectional versions of map and scan individually to attain better control over their contracts and behaviors.

Bidirectional fold
As we have seen in Section 2.1, folds are of vital importance in program calculation.We start with bfoldr, a bidirectional version of foldr , with trivial source and view conditions: One challenge for designing higher-order contract lenses is that they usually impose certain constraints to the contracts of their lens parameters.For instance, a trivial bidirectional version of foldr requires the parameter lens to have the trivial contract ctrue.To specify such requirements, we use similar syntax to refinement types, which is easily readable and understandable by humans and is also suitable for pencil/paper proofs.In theorem provers, one could use existential types to express the requirements of contracts like our Agda formalization.
We introduce the following syntactic sugar to specify the source and view conditions of parameters for higher-order contract lenses: The type of bfoldr can be simplified to bfoldr Given a simple contract lens : Either () (S, V ) ↔ V with trivial contracts, bfoldr returns a contract lens of type [S ] ↔ V also with trivial contracts, synchronizing a list of type [S ] with a value of type V .For the get direction, we simply use the unidirectional foldr .For the put direction, we recursively construct an updated source list (using unfoldr ) from the original source and an updated view step by step through put , the backward transformation of .Formally, we define bfoldr as follows: Note that the put direction of the above definition is inefficient since it computes "g as" every time coalg (a : as, v ) is called.A more efficient implementation is to calculate all g as in advance using a scanr as shown in Appendix B.1.We will use the efficient definition of bfoldr in the following sections.Similarly, we can define bfoldl, which is omitted here.The following example shows how the bidirectional fold works.
Example 3 (Bidirectional Maximum).Considering that we want to synchronize a list with its maximum, we can define it in terms of bfoldr by bmaximum : where bmax is a bidirectional version of max whose backward transformation uses the modified value to replace the maximum value of the parameter pair: To see a computation instance of bmaximum, we refer to Appendix C.1.

Bidirectional fold: Preserving length and transmitting constraints
While bfoldr is useful when it is total in both get and put directions, we may wish to keep the length of the source unchanged after put.For example, considering the bmaximum in Example 3, we may wish to keep the length of the source list after put bmaximum , and furthermore, we hope that the source and view conditions of bfoldr be able to express some extra constraints on the elements.All these can be concisely expressed as the following higher-order contract lens: The lift and licond, two high-order predicates, require their arguments to be of the same shape and structurally lift predicates over sum types (Either) and list types, respectively: The lens combinator bfoldr takes two predicates cs and cv and have the same definition of get and put components as bfoldr.The cs represents the constraints on the elements of the source list, and the cv represents the view condition.Notice that the predicate parameters cs and cv are kind of administrative; their main role is to guarantee that the source condition of the parameter lens is of shape lift cs cv for some cs and cv .Idealy, we can make them existentially bound.We opt to have explicit predicate parameters to make the presentation clear and more consistent with our Agda formalization.
Example 4 (Bidirectional Maximum Preserving Length).A direct use of bfoldr is to define a bidirectional version of maximum that preserves the length of the source list. bmaximum One may doubt that the put (Left ()) v = Right (v , −∞) in bmax might break the equal length condition.In fact, it will never be executed because the view condition requires the maximum value to be unchanged when it is −∞.

Bidirectional filter
As an application of bidirectional folds, we construct the bidirectional filter, which appears frequently in application scenarios of BXs, often in the forms of explicit combinators (Foster et al., 2007) or SQL selection commands (Abou-Saleh et al., 2018).
The unidirectional version of filter can be implemented by foldr as filter pr = foldr (λx xs → if pr x then x : xs else xs) [ ], which returns a list of elements satisfying the predicate pr.With the bfoldr introduced above, we are able to define a bidirectional version of filter which preserves the lengths of the source and view lists: The function fcond is defined as fcond pr = licond (λ x → pr x ).The bfilterAlg pr is essentially a bidirectional version of the function λx xs → if pr x then x : xs else xs.One example of bfilter is the bfilter even defined in Section 3.2.

Bidirectional map
Map is another important high-order function in functional programming and program calculation, which applies a function to each element of a list.In this section, we will give three different definitions of bidirectional map with different source and view conditions.The first one is bmap, which is just a bidirectional map that preserves the length of the source and view list.It has no other constraints on the source and view.The second one is bmap , which takes the constraint on individual elements of the list into consideration.The third one is bmapl (and bmapr), which goes a step further and takes into account the constraints on adjacent elements of the list as well.These three bidirectional versions of map cover a large range of applications.In particular, the most powerful bmapl is helpful in our later calculation of bidirectional maximum segment sum.

Bidirectional map: Preserving length
First, we give bmap which preserves the lengths of both source and view lists.It simply requires the parameter to have trivial contracts like bfoldr: where p as bs = map (λ(x, y) → put x y) (zip as bs ) It is clear to see that if the change on the view does not change its length, after backward propagation through put bmap , the length of the source will not be changed.
As shown in Section 2.1, map is just a special version of fold.Similarly, we can also implement bmap using bfoldr as shown in Appendix B.2.One example of bmap is the bmap bhead defined in Section 3.2.

Bidirectional map: Preserving inner constraints
The above bmap assumes that the lens argument it takes never introduces any constraint.But this is not always the case.When the parameter lens has nontrivial contracts, the bidirectional map combinator should reflect these contracts in its result lens.Thus, we define another version of bidirectional map which takes the inner constraints on elements of lists into consideration: The bmap simply lifts the contracts of its parameter to all elements in the source and view lists.As seen above, bmap is a generalized version of bmap; they are equivalent when the parameter lens has trivial contracts.Also, we can implement bmap using bfoldr in the same way as shown in Appendix B.2.One example of bmap is shown in Appendix C.2 In the above definition of bmap , we directly use cs and cv in the contracts of the result lens.The bmap has no requirement on the contracts of .Another alternative definition of bmap more similar to the definition of bfoldr which takes predicate parameters is as follows: We use the first definition in the paper as it takes fewer arguments.

Bidirectional map: Preserving constraints on adjacent elements
In practice, it is very common that map f is composed with a function that produces a list with some constraints on adjacent elements.For instance, map f may be composed with inits , where the result of [as 1 , as 2 , . . ., as n ] produced by inits [a 1 , a 2 , . . ., a n ] has the constraint (init as i = as i−1 ) ∧ (init In bidirectional programming, we need to carefully specify this kind of constraints.Recall the binits in Section 4.1 with the following view condition: The composition binits; bmap inviolates the condition in Definition 2. This motivated us to introduce bmapl, another bidirectional version of map which is able to express constraints on adjacent elements. The core idea is that for bmap , we augment the parameter lens of type S ↔ V with an extra argument of type S representing the adjacent element, which leads to a parameterized lens : S → (S ↔ V ).Notice that is still a bidirectional version of a function of type S → V , so we need to restrict the get components of all s to be the same function for any s : S. We again use similar syntax to refinement types to express the requirement on the parameters and define the following syntactic sugar: Our two syntactic sugars can be used nestedly: The bidirectional map preserving constraints on adjacent elements is defined as follows: bmapl : ( cs : where bs 0 = get ( as 0 ) as 0 g as = map (λ(a , a) → get a a) (zip (as 0 : init as) as) p as bs = scanl (λ(a, b ) a → put a a b ) as 0 (zip as bs ) cs t as = (∀ 1 ≤ i ≤ |as|.cs as i−1 as i ) ∧ eqlength t as cv t bs = (∀ 1 ≤ i ≤ |as|.cv bs i−1 bs i ) ∧ eqlength t bs The constraints on adjacent elements of lists are specified by cs and cv.For example, if we take cs to be λx y → (init y = x) and as 0 to be [ ], then the source condition of the bmapl as 0 is equivalent to cv binits , and thus, the composition binits; bmapl [ ] is valid.The implementation of bmapl a 0 is visualized in Figure 1.The parameterized lens : (a : S) ⇒ ({λ a → cs a a } S ↔ V {λ b → cv (get ( a) a) b }) takes the adjacent element of source as the argument.As we have mentioned in Section 4.1, get a means using the get component of a simply as a total function.For the get direction, when computing b i from a i , we pass the adjacent element a i−1 to and make sure that we have cv b i−1 b i , which ensures the view list satisfies the constraints on adjacent elements.For the put direction, when computing a i from b i and a i , we pass a i−1 to and make sure that we have cs a i−1 a i , which ensures the updated source list satisfies the constraints on adjacent elements.
Note that we use the name bmapl because the constraints are leftward on every pair of a i−1 and a i .Similarly, we have a bmapr which are used to deal with constraints rightward on every pair of a i and a i+1 , usually generated by some scanr (⊕) a 0 .The implementation is almost the same except for replacing scanl in the code with scanr .One example of bmapl is shown in Appendix C.3.

Bidirectional map using inner bidirectional fold
As we have seen so far, bmapl is useful to give a bidirectional version for map f with expressive contraints.What if f is a fold?Since bmapl takes a parameterized lens of type S ⇒ (S ↔ V ), we cannot directly pass either bfoldr or bfoldr to bmapl.Moreover, since the bidirectional fold we needed depends on the cs in the source condition of the result of bmapl, it is actually difficult to give a general bidirectional fold.Fortunately, we can define some special bidirectional versions of fold to cope with some frequently used constraints, such as λa i−1 a i → init a i = a i−1 .The bfoldl init shown below is such a special bfold that can be used inside bmapl: The bfoldl init takes a parameterized contract lens and returns another parameterized contract lens which is suitable to be passed to bmapl.Notice that the result parameterized lens bfoldl init cv b 0 of type [S ] ⇒ ([S ] ↔ V ) indeed has the same get component for any argument as : [S ], because the get component does not use as at all.The get direction is a standard foldl, and the put direction only computes the last element of the new source list, since other elements are given as the argument indicated by the source condition λ as → (init as = as).
For an example usage of bfoldl init , we refer to Appendix C.4.

Bidirectional scan
After discussing bidirectional fold and map, we turn to bidirectional scan, which is an efficient computation pattern using an accumulation parameter and is useful for optimization (as will be seen later).The main challenge to bidirectionalize scan is that the result of scan may have constraints between adjacent elements similar to bmapl.In this section, we give a powerful bidirectional version of scan with the help of contract lenses: The implementation of bscanl cv b 0 is visualized in Figure 2. The get direction is a standard scanl .For the put direction, when computing a i from a i and b i , we pass b i−1 to the lens to restrict the result of put is of form Right ( , b i−1 ).
For an example usage of bscanl, we refer to Appendix C.5.

Bidirectional calculation laws
So far, we have seen that fundamental high-order functions such as fold, filter, map, and scan can be extended naturally from unidirectional to bidirectional, and that these bidirectional versions can be used to describe various bidirectional behaviors through suitable definitions of get, put, and the source/view conditions.In this section, we shall develop several important bidirectional calculation laws for manipulating them, including bidirectional versions of FOLD FUSION, MAP FUSION, and SCAN LEMMA.These bidirectional calculation laws are useful to reason about and optimize bidirectional programs.

Bidirectional fold fusion
We start with a bidirectional version of the FOLD FUSION law for bfoldr.To characterize bidirectional fold fusion law, we first bidirectionalize the list functor F L in Section 2.2: The tricky part lies in the last line above when there is a mismatch in the constructors of source and view.The implementation chooses a default value b 0 of type V to help with this process.With this bidirectional list functor, we can have the following bidirectional fold fusion law, which is similar to the unidirectional fold fusion law but with this explicit default value: 1 ; = blistF (get 1 (Left ())) ; 2 bfoldr 1 ; = bfoldr 2 BFOLDR FUSION It reads that the lens composition bfoldr 1 ; can be fused into a single lens bfoldr 2 if there exists 2 such that the equation 1 ; = blistF (get 1 (Left ())) ; 2 holds.Similarly, we have another fusion law for bfoldr , for which we need a slightly different bidirectional version of the list functor F L .The good thing is that we do not need the default value anymore because the contracts of bfoldr guarantee that there will not be any mismatch: Then, the fusion law is stated as: 1 ; = blistF cs ; 2 bfoldr cs cv 1 ; = bfoldr cs ct 2 BFOLDR' FUSION

Bidirectional map fusion
The bidirectional map fusion laws for bmap and bmap are quite easy since they just map to each element of the list in both forward and backward transformations.Since bmap is a special case of bmap , we only give the bidirectional map fusion law for bmap : Similarly, we can give the bidirectional map fusion law for bmapl: bmapl cs cv a 0 1 ; bmapl cv ct b 0 2 = bmapl cs ct a 0 ( 1 ; ; 2 ) BMAPL FUSION where (; ; ) is the composition of parameterized lenses whose types are of form S ⇒ (S ↔ V ).It is defined as follows: (; ; ) : The definition of 1 ; ; 2 is quite intuitive.We just pass the parameter a to 1 , and the result of a after the forward transformation of 1 to 2 .Notice that we still use the syntactic sugar S ⇒ (S ↔ T) for the type of the result parameterized lenses, which means the get component is the same for any parameter.This makes natural sense because both 1 and 2 have fixed get components.It is also easy to check that the composition 1 a; 2 (get 1 a a) is well defined (i.e., satisfies the condition in Definition 2).

Bidirectional fold-map fusion
We give a bidirectional fold-map fusion law for bfoldr and bmap , both of which preserve the length of the source list.
First, we bidirectionalize F m defined in Section 2.2 with conditions required by bfoldr : bmapF The result of bmapF has the same source condition as the lens bfoldr takes.Now we can give the bidirectional fold-map fusion law for bfoldr : bmap 1 ; bfoldr cv ct 2 = bfoldr cs ct (bmapF ct 1 ; 2 ) BFOLDR'-BMAP FUSION

Bidirectional scan lemma
In the unidirectional world, the SCAN LEMMA is a special version of the FOLD FUSION law.Note that replacing inits with inits and scanl with scanl , the scan lemma still holds.The major challenge for developing a similar bidirectional calculation law on contract lenses is that the inits introduces a constraint on adjacent elements of the view list.Fortunately, the contract-lens combinator bmapl can handle constraints on adjacent elements.With bmapl, bfoldl init , and bscanl, we can successfully obtain a bidirectional version of scan lemma: binits; bmapl (λa a → init a = a) cv [ ] (bfoldl init cv b 0 ) = bscanl cv b 0 BIDIRECTIONAL SCAN LEMMA The form of the bidirectional scan lemma is quite similar to its unidirectional version modulo some administrative parameters for contracts.We give an example of BIDIRECTIONAL SCAN LEMMA in Appendix C.6.

Examples
In this section, we will demonstrate further through three examples that with contract lenses, combinators, and associated calculation laws, we are able to flexibly construct and optimize bidirectional programs.The first example is a projection problem from geometry, where the conditions afforded by contract lenses are essential for its construction.
The second example concerns bidirectional data conversion, specifically, string processing and formatting.It showcases that within our framework, such computation tasks can be constructed in a point-free style, of which efficiency is guaranteed by calculational laws.The third example stems from a classic scenario of program calculation, and it demonstrates the ability to reason about and optimize complicated bidirectional programs through semantics-preserving transformation based on calculational laws, in a way that one would have done for unidirectional programs.

Projection onto a hyperplane
Let us look at an example to see the expressive power of contract lenses, especially how we can use contracts to constrain the changes of source and view.One basic computation in the area of geometry is to calculate the projection of a point onto a hyperplane in a higher-dimensional Euclidean space.In this example, we want to synchronize a point xs = [x 1 , x 2 , . . ., x n ] 11 in a n-dimensional Euclidean space with the projection of it onto the hyperplane H : What's more, there is a unique hyperplane H parallel to H and through the point xs.We want an extra property that the new point obtained from backward transformation is on the hyperplane H .In other words, the task is to synchronize a list of numbers with the differences between each number and the mean of all numbers; meanwhile, the mean of the source list is unchanged after changes on the view list.
One way to implement this synchronization using lenses is to compose two lenses, where one lens synchronizes a list with a pair of the list itself and its mean, and the other lens synchronizes this pair with the list of differences.The constraints that the dimension n and the hyperplane H should not be changed can be easily expressed with contracts.The full implementation is as follows: The specifications of synchronization behavior on each lenses are clearly expressed by contracts, which enables the compositions as we see in the definition of bproj.

String formatting and processing
Specifying programs that manipulate texts/strings bidirectionally is not new and has been extensively studied in Bohannon et al. (2008) and Matsuda & Wang (2015b).The novelty of our framework is that it supports a point-free style of specifications and calculational reasonings for such computational tasks.

String formatting
Let us look at the following string formatting task: given an input string, we want to filter out all digits and convert all remaining characters to upper case.With contract-lens combinators, we readily specify it in point-free style (for simplicity, we assume that characters in strings are either numbers or letters): The composition is valid, since one can check that fcond (not • isDigit) and licond (λ c → not (isDigit c)) are by definition equivalent.
In this naive specification, intermediate structures are created after one lens and are immediately consumed by another, in both directions.Recall that bfilter is an instance of bfoldr , using BFOLDR' FUSION, we reason as follows: The definition of balg is not as complicated as it seems: it is essentially the combination of bfilterAlg (not • isDigit) and btoUpper.
It is easy to verify the condition of the BFOLDR' FUSION law, which is the lens equivalence relation: The calculated version creates no intermediate structure and hence is more efficient in practice.

String encoding and decoding
Another useful string processing algorithm is the encoding and decoding, which is usually used in compressing a string.It is very appropriate to write them as a single bidirectional program in order to make it easier to maintain and optimize the encoding and decoding algorithms at the same time (Matsuda & Wang, 2020).Let us consider the following simple string encoding algorithm which illustrates the idea of Run Length Encoding: where encode = (head ws, length ws) ascii (x, y) For simplicity, the input string has already been splitted into a list of strings, where each string consists of consecutive identical characters.The compression compresses consecutive identical characters into its ASCII value and number of consecutive occurrences.The map encode maps the consecutive identical characters to the pair of the character and the length.Then the map ascii transforms the characters to their ASCII values.Finally, the foldr cat concatenates the pairs to a single list.For example,12 compression ["aaaaa", "bbbb", "ccccccccc"] = [97,5,98,4,99,9] Using the contract-lens combinators we defined in Section 5, it is easy to derive a bidirectional version of the function compression.The length of the results should not be changed; meanwhile, the ASCII values in the results should all be greater than or equal to 0 and less than 128.Thus, the view condition is defined as where the following contract lenses are used: It is easy to check the contract lens bcompression is well defined.However, this version of bcompress is not so efficient because it traverses the string three times.We can use the bidirectional calculation laws in Section 6 to reduce both the compression and the decompression algorithms to only one traversal simultaneously: bfoldr (λ as → allsame as) cv comp (bmapF (bencode; bascii); bcat)

Bidirectional maximum segment sum
Now let us turn to another example involving more advanced program calculation.The maximum segment sum is a classic problem in the area of program calculation.To demonstrate the ability of our calculation framework, we change the specification of mss in Section 2.3 into a bidirectional version directly using contract-lens combinators and optimize it to a more efficient version which has time complexity O(n) in both get and put directions; meanwhile, the semantics is preserved.
To see this concretely, let us first get a bidirectional version of mss without considering efficiency.To achieve this, we introduce a refinement type TailsList a = {as : where each element of the list is the tail of the previous element, and the tail of the last element is the empty list.The specification of the bidirectional version of mss is bmss : where the definitions of the contracts and contract lenses appeared are The binits and bmaximum have been already defined in the previous sections.It is easy to check that bmss is well defined, that is, it satisfies round-tripping properties and the condition of lens composition.
Next, we make use of the bidirectional calculation rules we developed in Section 6 to optimize the bmss.
The get direction of (btails init ; ; bmapSum; ; bmaximum2) a for any a : [Int ] is similar to the original Horner's rule with ⊗ = + and ⊕ = max.It would take space to develop a general bidirectional Horner's rule for any ⊕ and ⊗, because we require that ⊕ and ⊗ form a ring structure and keep it in the bidirectional setting.However, it is useful to define and prove some specific bidirectional versions of the Horner's rule like this.
By now, we have successfully derived a correct and linear-time efficient bidirectional program that can synchronize a list with its maximum segment sum.

Related work
In this section, we discuss related work on partiality in the lens framework, Hoare-style reasoning of BX, automatic bidirectionalization, and some attempts on calculating with lenses.

Lens family and partiality of put
The most prominent approach to BX is the lens framework formally introduced by Foster et al. (2007).It is highly influential and directly inspired a number of follow-on works including Boomerang (Bohannon et al., 2008), quotient lenses (Foster et al., 2008), matching lenses (Barbosa et al., 2010), symmetric lenses (Hofmann et al., 2011), edit lenses (Hofmann et al., 2012), BiGUL (Ko et al., 2016), applicative lenses (Matsuda & Wang, 2015a), HOBiT (Matsuda & Wang, 2018), and so on.The present paper on contract lenses is no exception.On the issue of partiality, different approaches were taken by the various works, which can be broadly categorized into the following.

Formulation of contracts and relation to type systems
As argued in Section 1.1, giving total definitions to get and put components is not always desirable, as the effort in achieving it necessarily complicates program design and reasoning.Some previous work on lenses ensures the totality of them by advanced type systems, with enriched type constraints over the type variables S, V in the lens type S ↔ V .For example, in Foster et al. (2007), partial lenses are ruled out by set-based type constraints that precisely characterize the domain/range of get and put, and in Boomerang (Bohannon et al., 2008), the underlying String type is enriched with regular languages to serve as types for dictionary lenses.
As far as we know, lens formulations with enriched type systems like the above are not readily used to flexibly express the bidirectional behaviors we see in this paper.Take bmap : (S ↔ V ) → [S ] ↔ [V ] as an example.With contracts, we can easily ensure that the changes on view do not modify the length of lists by setting the view condition to eqlength.However, it is nontrivial to express the "equal length" view condition by only constraining the types S and V themselves, instead of specifying constraints on the changes of values of types S and V .By adding an additional parameter to bmap specifying the length of the source and view list, one could encode bmap indirectly with a notion of dependent/refinement types into something like the following: This version of bmap fixes the length of lists, which is obviously less general than the versions using eqlength like the bmap in Section 5.2.1 and bmap in Section 5.2.2.
The "equal length" view condition is essentially a constraint on the dynamic changes of inputs to a lens, which can be nicely handled by our view contract.In our framework, contracts specifies the ranges that lens components behave well, the dynamic changes that a lens can reasonably accept, and the conditions that different components can compose together.
It is worth noting that different from the previous work on constraining the source and view types (Foster et al., 2007;Bohannon et al., 2008), contracts are not part of types, but rather additional specifications that parallel get and put.Moreover, users have full control of these specifications, just as how they specify the component get and put in the first place.In this sense, user have the flexibility to choose different contracts based on the same underlying get and put.For instance, the "equal length" condition for bmap may be strengthened so that additionally the first element of the list is preserved.These choices are completely up to the users.
An alternative design choice of contract lenses is to encode the BACKWARDVALIDITY and FORWARDVALIDITY laws as well as the extra conditions of the CONDITIONEDPUTGET and CONDITIONEDGETPUT laws directly into the types of get and put with refinement types: With the above refinement type signatures, we can use the original PUTGET and GETPUT laws of lenses.Note that the definition of contract lenses is still a four-tuple of get, put, cs, and cv in this case.There is no clear advantage or disadvantage between these two approaches.We choose to characterize the properties of contracts with explicit laws like BACKWARDVALIDITY and FORWARDVALIDITY to avoid the complication of type signatures and emphasize the differences between traditional lenses and contract lenses.
In this work, we do not impose any restriction on the constraints used in contracts.It is the users' work to prove the round-tripping properties of contract lenses and the welldefinedness of lens composition by either handwritten proofs or formalization in theorem provers like Agda.As a result, the designer of a practical system that implements contract lenses has to strike a balance between expressiveness of contracts and checkability of contracts implications.Nonetheless, we believe such systems are implementable, by restricting the set of contracts available to users to a small set of efficiently solvable constraints.As shown in our examples, simple predicates like eqlength can already help with constructing powerful combinators like generic mapping over lists.

Edit lenses
Edit lenses (Hofmann et al., 2012) model changes to view/source as operations (edits) in contrast to states in the traditional lenses.The edits are represented as monoids, and monoid actions on set become the actions of applying an edit to a state.As a result, only the edits in the monoid are allowed to be applied to the states, which in a way restricts changes to the source and view.But unlike contract lenses, these restrictions are not used to address partiality; in fact, edit lenses have the same problem of partiality as state-based ones because the monoid actions are allowed to be partial.For example, the edit del which deletes the last element of a list is partial as we cannot apply it to an empty list.Extra dynamic checks are needed to ensure that the computation of edit lenses will not fail.For contract lenses, the get and put will not fail as long as the source conditions and view conditions are satisfied.

Totality with Maybe monad
Another approach is to wrap the return type of get and put in the Maybe monad to remove partiality (Matsuda & Wang, 2015a;Ko et al., 2016;Xia et al., 2019).The put direction is a total function of type s → v → Maybe s, and it returns Nothing at run-time when an invalid input is passed to it.This approach is unsuitable for program calculation as it lacks the ability to reason about partiality statically.We want to know the static specification of a program and get meaning results instead of just getting a Nothing when the program fails.Moreover, the specification can guide the design of program calculation laws.

Other discussions
The properties of partial BX and the relations between them are discussed extensively in Stevens (2014).Different from our goal, the discussion there does not concern practical program construction nor mentions composition of transformations.In contrast, we focus on lenses that satisfy the round-tripping property on possibly partial domains.We make partiality explicit as a component of lenses and use it to explore composition behavior of partial lenses.

Hoare-style reasoning of bidirectional transformation
In Ko & Hu (2018), a reasoning framework for BiGUL programs based on Hoare logic is proposed, which is able to precisely characterize the bidirectional behaviors by reasoning in the put direction.The main concept is the put triplet in the form of {R}b{R }, which includes a set of pre-and post-conditions that are used to reason about the behavior of put in a way similar to the Hoare logic: if the original source s and the updated view v satisfy the precondition R, then put b s v will produce an updated source satisfying the post-condition R .
To some extent, their pre-and post-conditions serve a similar purpose to our BACKWARDVALIDITY law: if the original source s and the updated view v satisfy the view condition cv (get s) v, then put s v will successfully produce an updated source satisfying the source condition cs s (put s v).However, the novelty of contract lenses does not solely rely on the BACKWARDVALIDITY law but also the combination with other three laws of the round-tripping properties which give a clear specification of lenses to resolve the partiality problem and make the composition of contract lenses easy and well-behaved.It is worth mentioning that in their framework, reasoning about lens composition is difficult and involves several complicated proof rules.In contrast, contract lenses make such reasoning easy: two lenses 1 : {cs 1 } S ↔ V {cv 1 } and 2 : {cs 2 } V ↔ T {cv 2 } can be composed into a lens 1 ; 2 : {cs 1 } S ↔ T {cv 2 } given the condition proposed in Definition 2.
Furthermore, the purpose of pre-and post-conditions differs from that of source and view conditions.While pre-and post-conditions mainly focus on specifying the behaviors of the put components, our primary objective is to address the partiality problem of lenses, which allows for straightforward design of lenses and calculation laws.

Bidirectionalization
Bidirectionalization is an approach to bidirectional programming that is different from the lens framework.Instead of writing bidirectional programs directly in a specialized language, it aims to mechanically convert existing unidirectional programs into bidirectional ones.Voigtländer (2009) gives a high-order function bff that receives a polymorphic get function and returns its put counterpart.The technique is extended (Voigtländer et al., 2010) by combining it with syntactic bidirectionalization (Matsuda et al., 2007), which separates view changes in shape and in content.However, bidirectionalization is done for whole programs which lacks modular reasoning of compositions and therefore is not suitable for program calculation.

Calculating with lenses
The goal of generic point-free lenses (Pacheco & Cunha, 2010) is the most similar to ours.In that work, lens combinators are designed for many traditional high-order functions including fold and map.Subsequently, the point-free lenses are used for a limited form of calculation where the universal property (uniqueness) of the lens version of fold was proved and used to establish some program calculation laws for lenses such as the fold-map fusion (Pacheco & Cunha, 2011).
But very different from ours, their work is based on the traditional lenses without contracts, which means that the problem of partiality seriously limits the composition of lenses.As a result, many crucial calculation laws such as the SCAN LEMMA are not expressible in their framework.

Formalization with Agda
In this section, we briefly discuss one possible formalization of contract lenses in Agda.We use this formalisation to prove the correctness of lens composition, all lens combinators, all calculation laws, and most of the examples (except the string processing example in Section 7.2) in this paper.As mentioned in Section 1.2, our intention is not to restrict potential users of contract lenses within this formalization but rather to provide a calculation framework which allows any method of reasoning.This Agda formalization shows one potential way to mechanize our framework.
The formalization of the whole contract lens calculation framework is rather straightforward.A contract lens is a (possibly mutually defined) four-tuple get, put, cs, and cv, with a set of laws on them.This construction is formalized faithfully in the Agda code, where we define the lens type as a record type: One difference between our Agda formalization and what we have in the paper is that the Agda formalization does not use the syntactic sugar : S ⇒ (S ↔ V ) defined in Section 5.2.3 to restrict the parameterized lens to have a fixed get component.Instead, it defines as a lens of type (S, S) ↔ (V , V ), where the parameter is embedded into the first component of the source pair.The former form is more clear and suitable for human reading, while the latter form is easier to formalize.We provided a translation between these two kinds of lenses and proved its correctness in the Agda formalization.
For the calculation part of this framework, we defined an equivalence relation between lenses of the above type as described in Definition 3. We also prove the congruence theorem for high-order lenses.Take bmap, for example, we prove that if 1 ∼ 2 , then bmap 1 ∼ bmap 2 .Our calculation laws are defined as theorems stating equivalences of lenses.

Conclusion
In this work, we propose a framework based on program calculation to enable the development of complex but efficient BX programs that are correct by construction.As part of the framework, we design a novel extension to lenses, contract lenses, for handling partiality and use it to justify general composition of lenses.Based on this, we extend the theories for program calculation to BX programming by designing combinators to capture bidirectional recursive computation patterns and proving their properties.We look at the list datatype and give proofs for fundamental calculation laws including various fusion laws for bidirectional fold and map and the bidirectional scan lemma.We showcase the construction of a realistic projection program, the derivation of efficient bidirectional string processing programs, and the maximum segment sum program to demonstrate the effectiveness of our framework.
This work focuses on the calculation for BXs on lists, which mirrors the classic work on the theory of list (Bird, 1989a(Bird, , 1987) ) in the literature of program calculation.Generalizing this bidirectional program calculation framework to algebraic datatypes generated by polynomial functors is a natural next step.Another possible future work is to design practical systems based on contract lenses to reason about and optimize BXs, automating the verification of round-tripping properties and lens composition using Satisfiability Modulo Theories (SMT) solvers.

A Calculating with total lenses
It is possible to make bmap total: The additional parameter a 0 is used as a default source value.
One can develop an associated map fusion law for it: get 1 a 0 = b 0 bmap total a 0 1 ; bmap total b 0 2 = bmap total a 0 ( 1 ; 2 ) BMAPTOTAL FUSION However, this law requires get 1 a 0 = b 0 , a semantic condition on default values, which is an unwanted proof obligation to program calculators and optimizers.

B Equivalent implementation of combinators
This appendix shows the code for equivalent implementations of some contract-lens combinators in Section 5.

B.1 Efficient bfoldr
This section shows an efficient implementation of bfoldr:

C Examples for combinators
This appendix shows examples for some contract-lens combinators and calculation laws in Sections 5 and 6.

Fig. 1 .
Fig. 1.Implementation of bmapl.The left figure shows the computation of the get and the right figure shows the computation of the put.

Fig. 2 .
Fig. 2. Implementation of bscanl.The left figure shows the computation of the get and the right figure shows the computation of the put.