Skip to main content
Erschienen in:
Buchtitelbild

Open Access 2019 | OriginalPaper | Buchkapitel

Composing Bidirectional Programs Monadically

verfasst von : Li-yao Xia, Dominic Orchard, Meng Wang

Erschienen in: Programming Languages and Systems

Verlag: Springer International Publishing

Aktivieren Sie unsere intelligente Suche, um passende Fachinhalte oder Patente zu finden.

search-config
loading …

Abstract

Software frequently converts data from one representation to another and vice versa. Naïvely specifying both conversion directions separately is error prone and introduces conceptual duplication. Instead, bidirectional programming techniques allow programs to be written which can be interpreted in both directions. However, these techniques often employ unfamiliar programming idioms via restricted, specialised combinator libraries. Instead, we introduce a framework for composing bidirectional programs monadically, enabling bidirectional programming with familiar abstractions in functional languages such as Haskell. We demonstrate the generality of our approach applied to parsers/printers, lenses, and generators/predicates. We show how to leverage compositionality and equational reasoning for the verification of round-tripping properties for such monadic bidirectional programs.

1 Introduction

A bidirectional transformation (BX) is a pair of mutually related mappings between source and target data objects. A well-known example solves the view-update problem [2] from relational database design. A view is a derived database table, computed from concrete source tables by a query. The problem is to map an update of the view back to a corresponding update on the source tables. This is captured by a bidirectional transformation. The bidirectional pattern is found in a broad range of applications, including parsing [17, 30], refactoring [31], code generation [21, 27], and model transformation [32] and XML transformation [25].
When programming a bidirectional transformation, one can separately construct the forwards and backwards functions. However, this approach duplicates effort, is prone to error, and causes subsequent maintenance issues. These problems can be avoided by using specialised programming languages that generate both directions from a single definition [13, 16, 33], a discipline known as bidirectional programming.
The most well-known language family for BX programming is lenses [13]. A lens captures transformations between sources S and views V via a pair of functions https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figa_HTML.gif \(: S \rightarrow V\) and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figb_HTML.gif \(: V \rightarrow S \rightarrow S\). The https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figc_HTML.gif function extracts a view from a source and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figd_HTML.gif takes an updated view and a source as inputs to produce an updated source. The asymmetrical nature of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fige_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figf_HTML.gif makes it possible for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figg_HTML.gif to recover some of the source data that is not present in the view. In other words, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figh_HTML.gif does not have to be injective to have a corresponding  https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figi_HTML.gif .
Bidirectional transformations typically respect round-tripping laws, capturing the extent to which the transformations preserve information between the two data representations. For example, well-behaved lenses [5, 13] should satisfy:
$$\begin{aligned} \textsf {put} \; (\textsf {get} \; s)\; s = s \quad \quad \quad \quad \textsf {get} \; (\textsf {put} \; v \; s) = v \end{aligned}$$
Lens languages are typically designed to enforce these properties. This focus on unconditional correctness inevitably leads to reduced practicality in programming: lens combinators are often stylised and disconnected from established programming idioms. In this paper, we instead focus on expressing bidirectional programs directly, using monads as an interface for sequential composition.
Monads are a popular pattern [35] (especially in Haskell) which combinator libraries in other domains routinely exploit. Introducing monadic composition to BX programming significantly expands the expressiveness of BX languages and opens up a route for programmers to explore the connection between BX programming and mainstream uni-directional programming. Moreover, it appears that many applications of bidirectional transformations (e.g., parsers and printers [17]) do not share the lens get/put pattern, and as a result have not been sufficiently explored. However, monadic composition is known to be an effective way to construct at least one direction of such transformations (e.g., parsers).
Contributions. In this paper, we deliberately avoid the well-tried approach of specialised lens languages, instead exploring a novel point in the BX design space based on monadic programming, naturally reusing host language constructs. We revisit lenses, and two more bidirectional patterns, demonstrating how they can be subject to monadic programming. By being uncompromising about the monad interface, we expose the essential ideas behind our framework whilst maximising its utility. The trade off with our approach is that we can no longer enforce correctness in the same way as conventional lenses: our interface does not rule out all non-round-tripping BXs. We tackle this issue by proposing a new compositional reasoning framework that is flexible enough to characterise a variety of round-tripping properties, and simplifies the necessary reasoning.
Specifically, we make the following contributions:
  • We describe a method to enable monadic composition for bidirectional programs (Sect. 3). Our approach is based on a construction which generates a monadic profunctor, parameterised by two application-specific monads which are used to generate the forward and backward directions.
  • To demonstrate the flexibility of our approach, we apply the above method to three different problem domains: parsers/printers (Sects. 3 and 4), lenses (Sect. 5), and generators/predicates for structured data (Sect. 6). While the first two are well-explored areas in the bidirectional programming literature, the third one is a completely new application domain.
  • We present a scalable reasoning framework, capturing notions of compositionality for bidirectional properties (Sect. 4). We define classes of round-tripping properties inherent to bidirectionalism, which can be verified by following simple criteria. These principles are demonstrated with our three examples. We include some proofs for illustration in the paper. The supplementary material [12] contains machine-checked Coq proofs for the main theorems.
    An extended version of this manuscript [36] includes additional definitions, proofs, and comparisons in its appendices.
  • We have implemented these ideas as Haskell libraries [12], with two wrappers around attoparsec for parsers and printers, and QuickCheck for generators and predicates, showing the viability of our approach for real programs.
We use Haskell for concrete examples, but the programming patterns can be easily expressed in many functional languages. We use the Haskell notation of assigning type signatures to expressions via an infix double colon “ https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figj_HTML.gif ”.

1.1 Further Examples of BX

We introduced lenses briefly above. We now introduce the other two examples used in this paper: parsers/printers and generators/predicates.
Parsing and printing. Programming language tools (such as interpreters, compilers, and refactoring tools) typically require two intimately linked components: parsers and printers, respectively mapping from source code to ASTs and back. A simple implementation of these two functions can be given with types:
Parsers and printers are rarely actual inverses to each other, but instead typically exhibit a variant of round-tripping such as:
The left equation describes the common situation that parsing discards information about source code, such as whitespace, so that printing the resulting AST does not recover the original source. However, printing retains enough information such that parsing the printed output yields an AST which is equivalent to the AST from parsing the original source. The right equation describes the dual: printing may map different ASTs to the same string. For example, printed code \(1+2+3\) might be produced by left- and right-associated syntax trees.
For particular AST subsets, printing and parsing may actually be left- or right- inverses to each other. Other characterisations are also possible, e.g., with equivalence classes of ASTs (accounting for reassociations). Alternatively, parsers and printers may satisfy properties about the interaction of partially-parsed inputs with the printer and parser, e.g., if https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figm_HTML.gif :
Thus, parsing and printing follows a pattern of inverse-like functions which does not fit the lens paradigm. The pattern resembles lenses between a source (source code) and view (ASTs), but with a compositional notion for the source and partial “gets” which consume some of the source, leaving a remainder.
Writing parsers and printers by hand is often tedious due to the redundancy implied by their inverse-like relation. Thus, various approaches have been proposed for reducing the effort of writing parsers/printers by generating both from a common definition [17, 19, 30].
Generating and checking. Property-based testing (e.g., QuickCheck) [10] expresses program properties as executable predicates. For instance, the following property checks that an insertion function https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figo_HTML.gif , given a sorted list—as checked by the predicate https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figp_HTML.gif —produces another sorted list. The combinator https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figq_HTML.gif represents implication for properties.
To test this property, a testing framework generates random inputs for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figs_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figt_HTML.gif . The implementation of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figu_HTML.gif applied here first checks whether https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figv_HTML.gif is sorted, and if it is, checks that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figw_HTML.gif is sorted as well. This process is repeated with further random inputs until either a counterexample is found or a predetermined number of test cases pass.
However, this naïve method is inefficient: many properties such as https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figx_HTML.gif have preconditions which are satisfied by an extremely small fraction of inputs. In this case, the ratio of sorted lists among lists of length n is inversely proportional to n!, so most generated inputs will be discarded for not satisfying the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figy_HTML.gif precondition. Such tests give no information about the validity of the predicate being tested and thus are prohibitively inefficient.
When too many inputs are being discarded, the user must instead supply the framework with custom generators of values satisfying the precondition: https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figz_HTML.gif .
One can expect two complementary properties of such a generator. A generator is sound with respect to the predicate https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figaa_HTML.gif if it generates only values satisfying https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figab_HTML.gif ; soundness means that no tests are discarded, hence the tested property is better exercised. A generator is complete with respect to https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figac_HTML.gif if it can generate all satisfying values; completeness ensures the correctness of testing a property with https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figad_HTML.gif as a precondition, in the sense that if there is a counterexample, it will be eventually generated. In this setting of testing, completeness, which affects the potential adequacy of testing, is arguably more important than soundness, which affects only efficiency.
It is clear that generators and predicates are closely related, forming a pattern similar to that of bidirectional transformations. Given that good generators are usually difficult to construct, the ability to extract both from a common specification with bidirectional programming is a very attractive alternative.
Roadmap. We begin by outlining a concrete example of our monadic approach via parsers and printers (Sect. 2), before explaining the general approach of using monadic profunctors to structure bidirectional programs (Sect. 3). Section 4 then presents a compositional reasoning framework for monadic bidirectional programs, with varying degrees of strength adapted to different round-tripping properties. We then replay the developments of the earlier sections to define lenses as well as generators and predicates in Sects. 5 and 6.

2 Monadic Bidirectional Programming

A bidirectional parser, or biparser, combines both a parsing direction and printing direction. Our first novelty here is to express biparsers monadically.
In code samples, we use the Haskell pun of naming variables after their types, e.g., a variable of some abstract type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figae_HTML.gif will also be called https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figaf_HTML.gif . Similarly, for some type constructor https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figag_HTML.gif , a variable of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figah_HTML.gif will be called https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figai_HTML.gif . A function https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figaj_HTML.gif (a Kleisli arrow for a monad https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figak_HTML.gif ) will be called https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figal_HTML.gif .
Monadic parsers. The following data type provides the standard way to describe parsers of values of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figam_HTML.gif which may consume only part of the input string:
It is well-known that such parsers are monadic [35], i.e., they have a notion of monadic sequential composition embodied by the interface:
The sequential composition operator https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figap_HTML.gif , called bind, describes the scheme of constructing a parser by sequentially composing two sub-parsers where the second depends on the output of the first; a parser of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figaq_HTML.gif values is made up of a parser of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figar_HTML.gif and a parser of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figas_HTML.gif that depends on the previously parsed https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figat_HTML.gif . Indeed, this is the implementation given to the monadic interface:
Bind first runs the parser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figav_HTML.gif on an input string https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figaw_HTML.gif , resulting in a value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figax_HTML.gif which is used to create the parser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figay_HTML.gif , which is in turn run on the remaining input https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figaz_HTML.gif to produce parsed values of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figba_HTML.gif . The https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbb_HTML.gif operation creates a trivial parser for any value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbc_HTML.gif which does not consume any input but simply produces https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbd_HTML.gif .
In practice, parsers composed with https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbe_HTML.gif often have a relationship between the output types of the two operands: usually that the former “contains” the latter in some sense. For example, we might parse an expression and compose this with a parser for statements, where statements contain expressions. This relationship will be useful later when we consider printers.
As a shorthand, we can discard the remaining unparsed string of a parser using projection, giving a helper function https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbf_HTML.gif .
Monadic printers. Our goal is to augment parsers with their inverse printer, such that we have a monadic type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbg_HTML.gif which provides two complementary (bi-directional) transformations:
However, this type of printer https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbi_HTML.gif (shown also in Sect. 1.1) cannot form a monad because it is contravariant in its type parameter https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbj_HTML.gif . Concretely, we cannot implement the bind ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbk_HTML.gif ) operator for values with types of this form:
We are stuck trying to fill the hole ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbm_HTML.gif ) as there is no way to get a value of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbn_HTML.gif to pass as an argument to https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbo_HTML.gif (first printer) and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbp_HTML.gif (second printer which depends on a https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbq_HTML.gif ). Subsequently, we cannot construct a monadic biparser by simply taking a product of the parser monad and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbr_HTML.gif and leveraging the result that the product of two monads is a monad.
But what if the type variables of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbs_HTML.gif were related by containment, such that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbt_HTML.gif is contained within https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbu_HTML.gif and thus we have a projection https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbv_HTML.gif ? We could use this projection to fill the hole in the failed attempt above, defining a bind-like operator:
This is closer to the monadic form, where https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbx_HTML.gif resolves the difficulty of contravariance by “contextualizing” the printers. Thus, the first printer is no longer just “a printer of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figby_HTML.gif ”, but “a printer of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figbz_HTML.gif extracted https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figca_HTML.gif https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcb_HTML.gif ”. In the context of constructing a bidirectional parser, having such a function to hand is not an unrealistic expectation: recall that when we compose two parsers, typically the values of the first parser for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcc_HTML.gif are contained within the values returned by the second parser for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcd_HTML.gif , thus a notion of projection can be defined and used here to recover a https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figce_HTML.gif in order to build the corresponding printer compositionally.
Of course, this is still not a monad. However, it suggests a way to generate a monadic form by putting the printer and the contextualizing projection together, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcf_HTML.gif and fusing them into https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcg_HTML.gif . This has the advantage of removing the contravariant occurence of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figch_HTML.gif , yielding a data type:
If we fix the first parameter type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcj_HTML.gif , then the type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figck_HTML.gif of printers for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcl_HTML.gif values is indeed monadic, combining a reader monad (for some global read-only parameter of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcm_HTML.gif ) and a writer monad (for strings), with implementation:
The printer https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figco_HTML.gif ignores its input and prints nothing. For bind, an input https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcp_HTML.gif is shared by both printers and the resulting strings are concatenated.
We can adapt the contextualisation of a printer by the following operation which amounts to pre-composition, witnessing the fact that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcq_HTML.gif is a contravariant functor in its first parameter:

2.1 Monadic Biparsers

So far so good: we now have a monadic notion of printers. However, our goal is to combine parsers and printers in a single type. Since we have two monads, we use the standard result that a product of monads is a monad, defining biparsers:
By pairing parsers and printers we have to unify their covariant parameters. When both the type parameters of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figct_HTML.gif are the same it is easy to interpret this type: a biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcu_HTML.gif is a parser from strings to https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcv_HTML.gif values and printer from https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcw_HTML.gif values to strings. We refer to biparsers of this type as aligned biparsers. What about when the type parameters differ? A biparser of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcx_HTML.gif provides a parser from strings to https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcy_HTML.gif values and a printer from https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figcz_HTML.gif values to strings, but where the printers can compute https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figda_HTML.gif values from https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdb_HTML.gif values, i.e., https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdc_HTML.gif is some common broader representation which contains relevant https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdd_HTML.gif -typed subcomponents. A biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figde_HTML.gif can be thought of as printing a certain subtree https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdf_HTML.gif from the broader representation of a syntax tree https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdg_HTML.gif .
The corresponding monad for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdh_HTML.gif is the product of the previous two monad definitions for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdi_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdj_HTML.gif , allowing both to be composed sequentially at the same time. To avoid duplication we elide the definition here which is shown in full in Appendix A of the extended version [36]
We can also lift the previous notion of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdk_HTML.gif from printers to biparsers, which gives us a way to contextualize a printer:
In the rest of this section, we use the alias “ https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdm_HTML.gif ” for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdn_HTML.gif with flipped parameters where we read https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdo_HTML.gif as applying the printer of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdp_HTML.gif on a subpart of an input of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdq_HTML.gif calculated by https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdr_HTML.gif , thus yielding a biparser of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figds_HTML.gif .
An example biparser. Let us write a biparser, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdt_HTML.gif , for strings which are prefixed by their length and a space. For example, the following unit tests should be true:
We start by defining a primitive biparser of single characters as:
A character is parsed by deconstructing the source string into its head and tail. For brevity, we do not handle the failure associated with an empty string. A character https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdw_HTML.gif is printed as its single-letter string (a singleton list) paired with https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdx_HTML.gif .
Next, we define a biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdy_HTML.gif for an integer followed by a single space. An auxiliary biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figdz_HTML.gif (on the right) parses an integer one digit at a time into a string. Note that in Haskell, the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figea_HTML.gif -notation statement https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figeb_HTML.gif desugars to “ https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figec_HTML.gif ...” which uses https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figed_HTML.gif and a function binding https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figee_HTML.gif in the scope of the rest of the desugared block.
On the right, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figeg_HTML.gif extracts a https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figeh_HTML.gif consisting of digits followed by a single space. As a parser, it parses a character ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figei_HTML.gif ); if it is a digit then it continues parsing recursively ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figej_HTML.gif ) appending the first character to the result ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figek_HTML.gif ). Otherwise, if the parsed character is a space the parser returns https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figel_HTML.gif . As a printer, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figem_HTML.gif expects a non-empty string of the same format; https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figen_HTML.gif extracts the first character of the input, then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figeo_HTML.gif prints it and returns it back as https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figep_HTML.gif ; if it is a digit, then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figeq_HTML.gif extracts the rest of the input to print recursively. If the character is a space, the printer returns a space and terminates; otherwise (not digit or space) the printer throws an error.
On the left, the biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figer_HTML.gif uses https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figes_HTML.gif to convert an input string of digits (parsed by https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/480721_1_En_6_Figet_HTML.gif ) into an integer, and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figeu_HTML.gif to convert an integer to an output string printed by https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figev_HTML.gif . A safer implementation could return the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figew_HTML.gif type when parsing but we keep things simple here for now.
After parsing an integer https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figex_HTML.gif , we can parse the string following it by iterating https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figey_HTML.gif times the biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figez_HTML.gif . This is captured by the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfa_HTML.gif combinator below, defined recursively like https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfb_HTML.gif but with the termination condition given by an external parameter. To iterate https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfc_HTML.gif times a biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfd_HTML.gif : if https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfe_HTML.gif , there is nothing to do and we return the empty list; otherwise for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figff_HTML.gif , we run https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfg_HTML.gif once to get the head https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfh_HTML.gif , and recursively iterate https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfi_HTML.gif times to get the tail https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfj_HTML.gif .
Note that although not reflected in its type, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfk_HTML.gif expects, as a printer, a list https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfl_HTML.gif of length https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfm_HTML.gif : if https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfn_HTML.gif , there is nothing to print; if https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfo_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfp_HTML.gif extracts the head of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfq_HTML.gif to print it with https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfr_HTML.gif , and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfs_HTML.gif extracts its tail, of length https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figft_HTML.gif , to print it recursively.
(akin to https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfv_HTML.gif from Haskell’s standard library). We can now fulfil our task:
Interestingly, if we erase applications of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfx_HTML.gif , i.e., we substitute every expression of the form https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfy_HTML.gif with https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figfz_HTML.gif and ignore the second parameter of the types, we obtain what is essentially the definition of a parser in an idiomatic style for monadic parsing. This is because https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figga_HTML.gif is the identity on the parser component of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgb_HTML.gif . Thus the biparser code closely resembles standard, idiomatic monadic parser code but with “annotations” via https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgc_HTML.gif expressing how to apply the backwards direction of printing to subparts of the parsed string.
Despite its simplicity, the syntax of length-prefixed strings is notably context-sensitive. Thus the example makes crucial use of the monadic interface for bidirectional programming: a value (the length) must first be extracted to dynamically delimit the string that is parsed next. Context-sensitivity is standard for parser combinators in contrast with parser generators, e.g., Yacc, and applicative parsers, which are mostly restricted to context-free languages. By our monadic BX approach, we can now bring this power to bear on bidirectional parsing.

3 A Unifying Structure: Monadic Profunctors

The biparser examples of the previous section were enabled by both the monadic structure of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgd_HTML.gif and the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figge_HTML.gif operation (also called https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgf_HTML.gif , with flipped arguments). We describe a type as being a monadic profunctor when it has both a monadic structure and a https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgg_HTML.gif operation (subject to some equations). The notion of a monadic profunctor is general, but it characterises a key class of structures for bidirectional programs, which we explain here. Furthermore, we show a construction of monadic profunctors from pairs of monads which elicits the necessary structure for monadic bidirectional programming in the style of the previous section.
Profunctors. In Sect. 2.1, biparsers were defined by a data type with two type parameters ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgh_HTML.gif ) which is functorial and monadic in the second parameter and contravariantly functorial in the first parameter (provided by the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgi_HTML.gif operation). In standard terminology, a two-parameter type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgj_HTML.gif which is functorial in both its type parameters is called a bifunctor. In Haskell, the term profunctor has come to mean any bifunctor which is contravariant in the first type parameter and covariant in the second.1 This differs slightly from the standard category theory terminology where a profunctor is a bifunctor \(\mathsf {F} : \mathcal {D}^{\mathsf {op}} \times \mathcal {C} \rightarrow \) Set. This corresponds to the Haskell community’s use of the term “profunctor” if we treat Haskell in an idealised way as the category of sets.
We adopt this programming-oriented terminology, capturing the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgk_HTML.gif operation via a class https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgl_HTML.gif . In the preceding section, some uses of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgm_HTML.gif involved a partial function, e.g., https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgn_HTML.gif . We make the possibility of partiality explicit via the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgo_HTML.gif type, yielding the following definition.
Definition 1
A binary data type is a profunctor if it is a contravariant functor in its first parameter and covariant functor in its second, with the operation:
which should obey two laws:
where https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgr_HTML.gif composes partial functions (left-to-right), captured by Kleisli arrows of the Maybe monad.
The constraint https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgs_HTML.gif captures a universally quantified constraint [6]: for all types https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgt_HTML.gif then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgu_HTML.gif has an instance of the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgv_HTML.gif class.2
The requirement for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgy_HTML.gif to take partial functions is in response to the frequent need to restrict the domain of bidirectional transformations. In combinator-based approaches, combinators typically constrain bidirectional programs to be bijections, enforcing domain restrictions by construction. Our more flexible approach requires a way to include such restrictions explicitly, hence https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgz_HTML.gif .
Since the contravariant part of the bifunctor applies to functions of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figha_HTML.gif , the categorical analogy here is more precisely a profunctor \(\mathsf {F} : {\mathcal {C}_T}^{\mathsf {op}} \times \mathcal {C} \rightarrow \mathbf Set \) where \(\mathcal {C}_T\) is the Kleisli category of the partiality ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighb_HTML.gif ) monad.
Definition 2
A monadic profunctor is a profunctor https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighc_HTML.gif (in the sense of Definition 1) such that   https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighd_HTML.gif   is a monad for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighe_HTML.gif . In terms of type class constraints, this means there is an instance https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighf_HTML.gif and for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighg_HTML.gif there is a https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighh_HTML.gif instance. Thus, we represent monadic profunctors by the following empty class (which inherits all its methods from its superclasses):
Monadic profunctors must obey the following laws about the interaction between profunctor and monad operations:
(for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighk_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighl_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighm_HTML.gif ). These laws are equivalent to saying that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighn_HTML.gif lifts (partial) functions into monad morphisms. In Haskell, these laws are obtained for free by parametricity [34]. This means that every contravariant functor and monad is in fact a monadic profunctor, thus the following universal instance is lawful:
Corollary 1
Biparsers form a monadic profunctor as there is an instance of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighp_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighq_HTML.gif satisfying the requisite laws.
Lastly, we introduce a useful piece of terminology (mentioned in the previous section on biparsers) for describing values of a profunctor of a particular form:
Definition 3
A value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighr_HTML.gif of a profunctor https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighs_HTML.gif is called aligned if https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fight_HTML.gif = https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighu_HTML.gif .

3.1 Constructing Monadic Profunctors

Our examples (parsers/printers, lenses, and generators/predicates) share monadic profunctors as an abstraction, making it possible to write different kinds of bidirectional transformations monadically. Underlying these definitions of monadic profunctors is a common structure, which we explain here using biparsers, and which will be replayed in Sect. 5 for lenses and Sect. 6 for bigenerators.
There are two simple ways in which a covariant functor https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighv_HTML.gif (resp. a monad) gives rise to a profunctor (resp. a monadic profunctor). The first is by constructing a profunctor in which the contravariant parameter is discarded, i.e., https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighw_HTML.gif ; the second is as a function type from the contravariant parameter https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighx_HTML.gif to https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighy_HTML.gif , i.e., https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fighz_HTML.gif . These are standard mathematical constructions, and the latter appears in the Haskell profunctors package with the name https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figia_HTML.gif . Our core construction is based on these two ways of creating a profunctor, which we call https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figib_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figic_HTML.gif respectively:
The naming reflects the idea that these two constructions will together capture a bidirectional transformation and are related by domain-specific round-tripping properties in our framework. Both https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figie_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figif_HTML.gif map any functor into a profunctor by the following type class instances:
There is an additional constraint here for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figih_HTML.gif , enforcing that the monad https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figii_HTML.gif is a member of the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figij_HTML.gif class which we define as:
This provides an interface for monads which can internalise a notion of failure, as captured at the top-level by https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figil_HTML.gif in https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figim_HTML.gif .
Furthermore, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figin_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figio_HTML.gif both map any monad into a monadic profunctor:
The product of two monadic profunctors is also a monadic profunctor. This follows from the fact that the product of two monads is a monad and the product of two contravariant functors is a contravariant functor.

3.2 Deriving Biparsers as Monadic Profunctor Pairs

We can redefine biparsers in terms of the above data types, their instances, and two standard monads, the state and writer monads:
The backward direction composes the writer monad with the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figit_HTML.gif monad using https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figiu_HTML.gif (the writer monad transformer, equivalent to composing two monads with a distributive law). Thus the backwards component of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figiv_HTML.gif corresponds to printers (which may fail) and the forwards component to parsers:
For the above code to work in Haskell, the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figix_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figiy_HTML.gif types need to be defined via either a https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figiz_HTML.gif type or https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figja_HTML.gif in order to allow type class instances on partially applied type constructors. We abuse the notation here for simplicity but define smart constructors and deconstructors for the actual implementation:3
The monadic profunctor definition for biparsers now comes for free from the constructions in Sect. 3.1 along with the following instance of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjc_HTML.gif for the writer monad transformer with the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjd_HTML.gif monad:
In a similar manner, we will use this monadic profunctor construction to define monadic bidirectional transformations for lenses (Sect. 5) and bigenerators (Sect. 6).
The example biparsers from Sect. 2.1 can be easily redefined using the structure here. For example, the primitive biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjf_HTML.gif becomes:
Codec library. The codec library [8] provides a general type for bidirectional programming isomorphic to our composite type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjh_HTML.gif :
Though the original codec library was developed independently, its current form is a result of this work. In particular, we contributed to the package by generalising its original type ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjj_HTML.gif ) to the one above, and provided https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjk_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjl_HTML.gif instances to support monadic bidirectional programming with codecs.

4 Reasoning about Bidirectionality

So far we have seen how the monadic profunctor structure provides a way to define biparsers using familiar operations and syntax: monads and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjm_HTML.gif -notation. This structuring allows both the forwards and backwards components of a biparser to be defined simultaneously in a single compact definition.
This section studies the interaction of monadic profunctors with the round-tripping laws that relate the two components of a bidirectional program. For every bidirectional transformation we can define dual properties: backward round tripping (going backwards-then-forwards) and forward round tripping (going forwards-then-backwards). In each BX domain, such properties also capture additional domain-specific information flow inherent to the transformations. We use biparsers as the running example. We then apply the same principles to our other examples in Sects. 5 and 6. For brevity, we use https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjn_HTML.gif as an alias for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjo_HTML.gif .
Definition 4
A biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjp_HTML.gif is backward round tripping if for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjq_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjr_HTML.gif then (recalling that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjs_HTML.gif ):
That is, if a biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figju_HTML.gif when used as a printer (going backwards) on an input value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjv_HTML.gif produces a string https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjw_HTML.gif , then using https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjx_HTML.gif as a parser on a string with prefix https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjy_HTML.gif and suffix https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figjz_HTML.gif yields the original input value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figka_HTML.gif and the remaining input https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkb_HTML.gif .
Note that backward round tripping is defined for aligned biparsers (of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkc_HTML.gif ) since the same value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkd_HTML.gif is used as both the input of the printer (typed by the first type parameter of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figke_HTML.gif ) and as the expected output of the parser (typed by the second type parameter of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkf_HTML.gif ).
The dual property is forward round tripping: a source string https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkg_HTML.gif is parsed (going forwards) into some value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkh_HTML.gif which when printed produces the initial source https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figki_HTML.gif :
Definition 5
A biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkj_HTML.gif is forward round tripping if for every https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkk_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkl_HTML.gif we have that:
Proposition 1
The biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkn_HTML.gif (Sect. 3.2) is both backward and forward round tripping. Proof by expanding definitions and algebraic reasoning.
Note, in some applications, forward round tripping is too strong. Here it requires that every printed value corresponds to at most one source string. This is often not the case as ASTs typically discard formatting and comments so that pretty-printed code is lexically different to the original source. However, different notions of equality enable more reasonable forward round-tripping properties.
Although one can check round-tripping properties of biparsers by expanding their definitions and the underlying monadic profunctor operations, a more scalable approach is provided if a round-tripping property is compositional with respect to the monadic profunctor operations, i.e., if these operations preserve the property. Compositional properties are easier to enforce and check since only the individual atomic components need round-tripping proofs. Such properties are then guaranteed “by construction” for programs built from those components.

4.1 Compositional Properties of Monadic Bidirectional Programming

Let us first formalize compositionality as follows. A property \(\mathcal {R}\) over a monadic profunctor https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figko_HTML.gif is a family of subsets https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkp_HTML.gif of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkq_HTML.gif indexed by types https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkr_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figks_HTML.gif .
Definition 6
A property \(\mathcal {R}\) over a monadic profunctor https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkt_HTML.gif is compositional if the monadic profunctor operations are closed over \(\mathcal {R}\), i.e., the following conditions hold for all types https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figku_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkv_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkw_HTML.gif :
Unfortunately for biparsers, forward and backward round tripping as defined above are not compositional: https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figky_HTML.gif is not backward round tripping and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figkz_HTML.gif does not preserve forward round tripping. Furthermore, these two properties are restricted to biparsers of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figla_HTML.gif (i.e., aligned biparsers) but compositionality requires that the two type parameters of the monadic profunctor can differ in the case of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlb_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlc_HTML.gif . This suggests that we need to look for more general properties that capture the full gamut of possible biparsers.
We first focus on backward round tripping. Informally, backward round tripping states that if you print (going backwards) and parse the resulting output (going forwards) then you get back the initial value. However, in a general biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figld_HTML.gif , the input type of the printer https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figle_HTML.gif differs from the output type of the parser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlf_HTML.gif , so we cannot compare them. But our intent for printers is that what we actually print is a fragment of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlg_HTML.gif , a fragment which is given as the output of the printer. By thus comparing the outputs of both the parser and printer, we obtain the following variant of backward round tripping:
Definition 7
A biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlh_HTML.gif is weak backward round tripping if for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figli_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlj_HTML.gif , and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlk_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figll_HTML.gif then:
Removing backward round tripping’s restriction to aligned biparsers and using the result https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figln_HTML.gif of the printer gives us a property that is compositional:
Proposition 2
Weak backward round tripping of biparsers is compositional.
Proposition 3
The primitive biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlo_HTML.gif is weak backward round tripping.
Corollary 2
Propositions 2 & 3 imply https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlp_HTML.gif is weak backward round tripping.
This property is “weak” as it does not constrain the relationship between the input https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlq_HTML.gif of the printer and its output https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlr_HTML.gif . In fact, there is no hope for a compositional property to do so: the monadic profunctor combinators do not enforce a relationship between them. However, we can regain compositionality for the stronger backward round-tripping property by combining the weak compositional property with an additional non-compositional property on the relationship between the printer’s input and output. This relationship is represented by the function that results from ignoring the printed string, which amounts to removing the main effect of the printer. Thus we call this operation a purification:
Ultimately, when a biparser is aligned ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlt_HTML.gif ) we want an input to the printer to be returned in its output, i.e, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlu_HTML.gif should equal https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlv_HTML.gif . If this is the case, we recover the original backward round tripping property:
Theorem 1
If https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlw_HTML.gif is weak backward round tripping, and for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlx_HTML.gif . https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figly_HTML.gif , then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figlz_HTML.gif is backward round tripping.
Thus, for any biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figma_HTML.gif , we can get backward round tripping by proving that its atomic subcomponents are weak backward round tripping, and proving that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmb_HTML.gif . The interesting aspect of the purification condition here is that it renders irrelevant the domain-specific effects of the biparser, i.e., those related to manipulating source strings. This considerably simplifies any proof. Furthermore, the definition of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmc_HTML.gif is a monadic profunctor homomorphism which provides a set of equations that can be used to expedite the reasoning.
Definition 8
A monadic profunctor homomorphism between monadic profunctors https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmd_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figme_HTML.gif is a polymorphic function https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmf_HTML.gif such that:
Proposition 4
The https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmh_HTML.gif operation for biparsers (above) is a monadic profunctor homomorphism between https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmi_HTML.gif and the monadic profunctor https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmj_HTML.gif .
Corollary 3
(of Theorem 1 with Corollary 2 and Proposition 4) The biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmk_HTML.gif is backward round tripping.
Proof
First prove (in Appendix B [36]) the following properties of biparsers https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figml_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmm_HTML.gif , and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmn_HTML.gif (writing https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmo_HTML.gif for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmp_HTML.gif ):
https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Equ1_HTML.png
(4.1)
https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Equ2_HTML.png
(4.2)
https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Equ3_HTML.png
(4.3)
From these and the homomorphism properties we can prove https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmq_HTML.gif :
Combining https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figms_HTML.gif with Corollary 2 ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmt_HTML.gif is weak backward round tripping) enables Theorem 1, proving that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmu_HTML.gif is backward round tripping.
The other two core examples in this paper also permit a definition of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmv_HTML.gif . We capture the general pattern as follows:
Definition 9
A purifiable monadic profunctor is a monadic profunctor https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmw_HTML.gif with a homomorphism https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmx_HTML.gif from https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmy_HTML.gif to the monadic profunctor of partial functions https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figmz_HTML.gif . We say that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figna_HTML.gif is the pure projection of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignb_HTML.gif .
Definition 10
A pure projection https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignc_HTML.gif is called the identity projection when https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignd_HTML.gif for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figne_HTML.gif .
Here and in Sects. 5 and 6, identity projections enable compositional round-tripping properties to be derived from more general non-compositional properties, as seen above for backward round tripping of biparsers.
We have neglected forward round tripping, which is not compositional, not even in a weakened form. However, we can generalise compositionality with conditions related to injectivity, enabling a generalisation of forward round tripping. We call the generalised meta-property quasicompositionality.

4.2 Quasicompositionality for Monadic Profunctors

An injective function \(f : A \rightarrow B\) is a function for which there exists a left inverse \(f^{-1} : B \rightarrow A\), i.e., where \(f^{-1} \circ f = id\). We can see this pair of functions as a simple kind of bidirectional program, with a forward round-tripping property (assuming f is the forwards direction). We can lift the notion of injectivity to the monadic profunctor setting and capture forward round-tripping properties that are preserved by the monadic profunctor operations, given some additional injectivity-like restriction. We first formalise the notion of an injective arrow.
Informally, an injective arrow https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignf_HTML.gif produces an output from which the input can be recalculated:
Definition 11
Let https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figng_HTML.gif be a monad. A function https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignh_HTML.gif is an injective arrow if there exists https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figni_HTML.gif (the left arrow inverse of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignj_HTML.gif ) such that for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignk_HTML.gif :
Next, we define quasicompositionality which extends the compositionality meta-property with the requirement for https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignm_HTML.gif to be applied to injective arrows:
Definition 12
Let https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignn_HTML.gif be a monadic profunctor. A property https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figno_HTML.gif indexed by types https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignp_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignq_HTML.gif is quasicompositional if the following holds
We now formulate a weakening of forward round tripping. As with weak backward round tripping, we rely on the idea that the printer outputs both a string and the value that was printed, so that we need to compare the outputs of both the parser and the printer, as opposed to comparing the output of the parser with the input of the printer as in (strong) forward round tripping. If running the parser component of a biparser on a string https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figns_HTML.gif yields a value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignt_HTML.gif and a remaining string https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignu_HTML.gif , and the printer outputs that same value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignv_HTML.gif along with a string https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignw_HTML.gif , then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignx_HTML.gif is the prefix of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figny_HTML.gif that was consumed by the parser, i.e., https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Fignz_HTML.gif .
Definition 13
A biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figoa_HTML.gif is weak forward round tripping if for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figob_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figoc_HTML.gif , and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figod_HTML.gif then:
Proposition 5
Weak forward round tripping is quasicompositional.
Proof
We sketch the qcomp-bind case, where https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figof_HTML.gif for some https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figog_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figoh_HTML.gif that are weak forward roundtripping. From https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figoi_HTML.gif , it follows that there exists https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figoj_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figok_HTML.gif such that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figol_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figom_HTML.gif . Similarly https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figon_HTML.gif implies there exists https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figoo_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figop_HTML.gif such that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figoq_HTML.gif https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figor_HTML.gif https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figos_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figot_HTML.gif https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figou_HTML.gif https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figov_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figow_HTML.gif https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figox_HTML.gif https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figoy_HTML.gif . Because https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figoz_HTML.gif is an injective arrow, we have https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpa_HTML.gif (see appendix). We then use the assumption that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpb_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpc_HTML.gif are weak forward roundtripping on https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpd_HTML.gif and on https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpe_HTML.gif , and deduce that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpf_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpg_HTML.gif therefore https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figph_HTML.gif .
Proposition 6
The https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpi_HTML.gif biparser is weak forward round tripping.
Corollary 4
Propositions 5 and 6 imply that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpj_HTML.gif is weak forward round tripping if we restrict the parser to inputs whose digits do not contain redundant leading zeros.
Proof
All of the right operands of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpk_HTML.gif in the definition of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpl_HTML.gif are injective arrows, apart from https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpm_HTML.gif at the end of the auxiliary https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpn_HTML.gif biparser. Indeed, the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpo_HTML.gif function is not injective since multiple strings may parse to the same integer: https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpp_HTML.gif . But the pre-condition to the proposition (no redundant leading zero digits) restricts the input strings so that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpq_HTML.gif is injective. The rest of the proof is a corollary of Propositions 5 and 6.
Thus, quasicompositionality gives us scalable reasoning for weak forward round tripping, which is by construction for biparsers: we just need to prove this property for individual atomic biparsers. Similarly to backward round tripping, we can prove forward round tripping by combining weak forward round tripping with the identity projection property:
Theorem 2
If https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpr_HTML.gif is weak forward round-tripping, and for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figps_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpt_HTML.gif , then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpu_HTML.gif is forward round tripping.
Corollary 5
The biparser https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpv_HTML.gif is forward round tripping by the above theorem (with identity projection shown in the proof of Corollary 3) and Corollary 4.
In summary, for any BX we can consider two round-tripping properties: forwards-then-backwards and backwards-then-forwards, called just forward and backward here respectively. Whilst combinator-based approaches can guarantee round-tripping by construction, we have made a trade-off to get greater expressivity in the monadic approach. However, we regain the ability to reason about bidirectional transformations in a manageable, scalable way if round-tripping properties are compositional. Unfortunately, due to the monadic profunctor structuring, this tends not to be the case. Instead, weakened round-tripping properties can be compositional or quasicompositional (adding injectivity). In such cases, we recover the stronger property by proving a simple property on aligned transformations: that the backwards direction faithfully reproduces its input as its output (identity projection). Appendix C in our extended manuscript [36] compares this reasoning approach to a proof of backwards round tripping for separately implemented parsers and printers (not using our combined monadic approach).

5 Monadic Bidirectional Programming for Lenses

Lenses are a common object of study in bidirectional programming, comprising a pair of functions https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpw_HTML.gif satisfying well-behaved lens laws shown in Sect. 1. Previously, when considering the monadic structure of parsers and printers, the starting point was that parsers already have a well-known monadic structure. The challenge came in finding a reasonable monadic characterisation for printers that was compatible with the parser monad. In the end, this construction was expressed by a product of two monadic profunctors https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpx_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpy_HTML.gif for monads https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figpz_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqa_HTML.gif . For lenses we are in the same position: the forwards direction ( https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqb_HTML.gif ) is already a monad—the reader monad. The backwards direction https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqc_HTML.gif is not a monad since it is contravariant in its parameter; the same situation as printers. We can apply the same approach of “monadisation” used for parsers and printers, giving the following new data type for lenses:
The result of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqe_HTML.gif is paired with a covariant parameter https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqf_HTML.gif (the result type of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqg_HTML.gif ) in the same way as monadic printers. Instead of mapping a view and a source to a source, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqh_HTML.gif now maps values of a different type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqi_HTML.gif , which we call a pre-view, along with a source https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqj_HTML.gif into a pair of a view https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqk_HTML.gif and source https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figql_HTML.gif . This definition can be structured as a monadic profunctor via a pair of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqm_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqn_HTML.gif constructions:
Thus by the results of Sect. 3, we now have a monadic profunctor characterisation of lenses that allows us to compose lenses via the monadic interface.
Ideally, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqp_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqq_HTML.gif should be total, but this is impossible without a way to restrict the domains. In particular, there is the known problem of “duplication” [23], where source data may appear more than once in the view, and a necessary condition for put to be well-behaved is that the duplicates remain equal amid view updates. This problem is inherent to all bidirectional transformations, and bidirectional languages have to rule out inconsistent updates of duplicates either statically [13] or dynamically [23]. To remedy this, we capture both partiality of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqr_HTML.gif and a predicate on sources in https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqs_HTML.gif for additional dynamic checking. This is provided by the following https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqt_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqu_HTML.gif monadic profunctors:
Going forwards, getting a view https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqw_HTML.gif from a source https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqx_HTML.gif may fail if there is no view for the current source. Going backwards, putting a pre-view https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqy_HTML.gif updates some source https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figqz_HTML.gif (via the state transformer https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figra_HTML.gif ), but with some further structure returned, provided by https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrb_HTML.gif (similar to the writer transformer used for biparsers, Sect. 3.2). The https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrc_HTML.gif here captures the possibility that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrd_HTML.gif can fail. The https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figre_HTML.gif structure provides a predicate which detects the “duplication” issue mentioned earlier. Informally, the predicate can be used to check that previously modified locations in the source are not modified again. For example, if a lens has a source made up of a bit vector, and a https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrf_HTML.gif sets bit i to 1, then the returned predicate will return https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrg_HTML.gif for all bit vectors where bit i is 1, and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrh_HTML.gif otherwise. This predicate can then be used to test whether further https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figri_HTML.gif operations on the source have modified bit i.
Similarly to biparsers, a pre-view https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrj_HTML.gif can be understood as containing the view https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrk_HTML.gif that is to be merged with the source, and which is returned with the updated source. Ultimately, we wish to form lenses of matching input and output types (i.e. https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrl_HTML.gif ) satisfying the standard lens well-behavedness laws, modulo explicit management of partiality via https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrm_HTML.gif and testing for conflicts via the predicate:
L-PutGet and L-GetPut are backward and forward round tripping respectively. Some lenses, such as the later example, are not defined for all views. In that case we may say that the lens is backward/forward round tripping in some subset https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figro_HTML.gif when the above properties only hold when https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrp_HTML.gif is an element of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrq_HTML.gif .
For every source type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrr_HTML.gif , the lens type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrs_HTML.gif is automatically a monadic profunctor by its definition as the pairing of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrt_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figru_HTML.gif (Sect. 3.1), and the following instance of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrv_HTML.gif for handling failure and instance of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrw_HTML.gif to satisfy the requirements of the writer monad:
A simple lens example operates on key-value maps. For keys of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figry_HTML.gif and values of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figrz_HTML.gif , we have the following source type and a simple lens:
The https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsb_HTML.gif component of the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsc_HTML.gif lens does a lookup of the key https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsd_HTML.gif in a map, producing https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figse_HTML.gif of a https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsf_HTML.gif . The https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsg_HTML.gif component inserts a value for key https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsh_HTML.gif . When the key already exists, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsi_HTML.gif overwrites its associated value.
Due to our approach, multiple calls to https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsj_HTML.gif can be composed monadically, giving a lens that gets/sets multiple key-value pairs at once. The list of keys and the list of values are passed separately, and are expected to be the same length.
We refer interested readers to our implementation [12] for more examples, including further examples involving trees.
Round tripping. We apply the reasoning framework of Sect. 4, taking the standard lens laws as the starting point (neither of which are compositional).
We first weaken backward round tripping to be compositional. Informally, the property expresses the idea, that if we https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsl_HTML.gif some value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsm_HTML.gif in a source https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsn_HTML.gif , resulting in a source https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figso_HTML.gif , then what we https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsp_HTML.gif from https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsq_HTML.gif is https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsr_HTML.gif . However two important changes are needed to adapt to our generalised type of lenses and to ensure compositionality. First, the value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figss_HTML.gif that was put is now to be found in the output of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figst_HTML.gif , whereas there is no way to constrain the input of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsu_HTML.gif because its type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsv_HTML.gif is abstract. Second, by sequentially composing lenses such as in https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsw_HTML.gif , the output source https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsx_HTML.gif of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsy_HTML.gif will be further modified by https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figsz_HTML.gif , so this round-tripping property must constrain all potential modifications of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figta_HTML.gif . In fact, the predicate https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtb_HTML.gif ensures exactly that the view https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtc_HTML.gif has not changed and is still https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtd_HTML.gif . It is not even necessary to refer to https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figte_HTML.gif , which is just one source for which we expect https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtf_HTML.gif to be https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtg_HTML.gif .
Definition 14
A lens https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figth_HTML.gif is weak backward round tripping if for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figti_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtj_HTML.gif , for all sources https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtk_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtl_HTML.gif , and for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtm_HTML.gif , we have:
Theorem 3
Weak backward round tripping is a compositional property.
Again, we complement this weakened version of round tripping with the notion of purification.
Proposition 7
Our lens type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figto_HTML.gif is a purifiable monadic profunctor (Definition 9), with a family of pure projections https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtp_HTML.gif indexed by a source https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtq_HTML.gif , defined:
Theorem 4
If a lens https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figts_HTML.gif is weak backward round tripping and has identity projections on some subset https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtt_HTML.gif (i.e., for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtu_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtv_HTML.gif then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtw_HTML.gif ) then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtx_HTML.gif is also backward round tripping on all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figty_HTML.gif .
To demonstrate, we apply this result to https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figtz_HTML.gif .
Proposition 8
The lens https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figua_HTML.gif is weak backward round tripping.
Proposition 9
The lens https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figub_HTML.gif has identity projection: https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuc_HTML.gif https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figud_HTML.gif https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figue_HTML.gif https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuf_HTML.gif .
Our lens https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figug_HTML.gif is therefore weak backward round tripping by construction. We now interpret/purify https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuh_HTML.gif as a partial function, which is actually the identity function when restricted to lists of the same length as https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figui_HTML.gif .
Proposition 10
For all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuj_HTML.gif such that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuk_HTML.gif , and for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figul_HTML.gif then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figum_HTML.gif .
Corollary 6
By the above results, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figun_HTML.gif for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuo_HTML.gif is backward round tripping on lists of length https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figup_HTML.gif .
The other direction, forward round tripping, follows a similar story. We first restate it as a quasicompositional property.
Definition 15
A lens https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuq_HTML.gif is weak forward round tripping if for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figur_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figus_HTML.gif , for all sources https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figut_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuu_HTML.gif , and for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuv_HTML.gif , we have:
Theorem 5
Weak forward round tripping is a quasicompositional property.
Along with identity projection, this gives the original forward L-GetPut property.
Theorem 6
If a lens https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figux_HTML.gif is weak forward round tripping and has identity projections on some subset https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuy_HTML.gif (i.e., for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figuz_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figva_HTML.gif then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvb_HTML.gif ) then https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvc_HTML.gif is also forward round tripping on https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvd_HTML.gif .
We can thus apply this result to our example (details omitted).
Proposition 11
For all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figve_HTML.gif , the lens https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvf_HTML.gif is forward round tripping on lists of length https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvg_HTML.gif .

6 Monadic Bidirectional Programming for Generators

Lastly, we capture the novel notion of bidirectional generators (bigenerators) extending random generators in property-based testing frameworks like QuickCheck [10] to a bidirectional setting. The forwards direction generates values conforming to a specification; the backwards direction checks whether values conform to a predicate. We capture the two together via our monadic profunctor pair as:
The forwards direction of a bigenerator is a generator, while the backwards direction is a partial function https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvi_HTML.gif . A value https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvj_HTML.gif represents a subset of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvk_HTML.gif , where https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvl_HTML.gif is a generator of values in that subset and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvm_HTML.gif maps pre-views u to members of the generated subset. In the backwards direction, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvn_HTML.gif defines a predicate on https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvo_HTML.gif , which is true if and only if https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvp_HTML.gif is https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvq_HTML.gif of some value. The function https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvr_HTML.gif extracts this predicate from the backward direction:
The bigenerator type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvt_HTML.gif is automatically a monadic profunctor due to our construction (Sect. 3). Thus, monad and profunctor instances come for free, modulo (un)wrapping of constructors and given a trivial instance of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvu_HTML.gif :
Due to space limitations, we refer readers to Appendix E [36] for an example of a compositionally-defined bigenerator that produces binary search trees.
Round tripping. A random generator can be interpreted as the set of values it may generate, while a predicate represents the set of values satisfying it. For a bigenerator https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvw_HTML.gif , we write https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvx_HTML.gif when https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvy_HTML.gif is a possible output of the generator. The generator of a bigenerator https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figvz_HTML.gif should match its predicate https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwa_HTML.gif . This requirement equates to round-tripping properties: a bigenerator is sound if every value which it can generate satisfies the predicate (forward round tripping); a bigenerator is complete if every value which satisfies the predicate can be generated (backward round tripping). Completeness is often more important than soundness in testing because unsound tests can be filtered out by the predicate, but completeness determines the potential adequacy of testing.
Definition 16
A bigenerator https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwb_HTML.gif is complete (backward round tripping) when https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwc_HTML.gif implies https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwd_HTML.gif .
Definition 17
A bigenerator https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwe_HTML.gif is sound (forward round tripping) if for all https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwf_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwg_HTML.gif implies that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwh_HTML.gif .
Similarly to backward round tripping of biparsers and lenses, completeness can be split into a compositional weak completeness and a purifiable property.
As before, the compositional weakening of completeness relates the forward and backward components by their outputs, which have the same type.
Definition 18
A bigenerator https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwi_HTML.gif is weak-complete when
Theorem 7
Weak completeness is compositional.
In a separate step, we connect the input of the backward direction, i.e., the checker, by reasoning directly about its pure projection (via a more general form of identity projection) which is defined to be the checker itself:
Theorem 8
A bigenerator https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwk_HTML.gif is complete if it is weak-complete and its checker satisfies a pure projection property: https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwl_HTML.gif
Thus to prove completeness of a bigenerator https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwm_HTML.gif , we first have weak-completeness by construction, and we can then show that https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwn_HTML.gif is a restriction of the identity function, interpreting all bigenerators simply as partial functions.
Considering the other direction, soundness, there is unfortunately no decomposition into a quasicompositional property and a property on pure projections. To see why, let https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwo_HTML.gif be a random uniform bigenerator of booleans, then consider for example, https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwp_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwq_HTML.gif , where https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwr_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figws_HTML.gif . Both satisfy any quasicompositional property satisfied by https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwt_HTML.gif , and both have the same pure projection https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwu_HTML.gif , and yet the former is unsound—it can generate https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwv_HTML.gif , which is rejected by https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figww_HTML.gif —while the latter is sound. This is not a problem in practice, as unsoundness, especially in small scale, is inconsequential in testing. But it does raise an intellectual challenge and an interesting point in the design space, where ease of reasoning has been traded for greater expressivity in the monadic approach.
Bidirectional transformations are a widely applicable technique used in many domains [11]. Among language-based solutions, the lens framework is most influential [3, 4, 13, 14, 24, 29]. Broadly speaking, combinators are used as programming constructs with which complex lenses are created by combining simpler ones. The combinators preserve round tripping, and therefore the resulting programs are correct by construction. A problem with lens languages is that they tend to be disconnected from more general programming. Lenses can only be constructed by very specialised combinators and are not subject to existing abstraction mechanisms. Our approach allows bidirectional transformations to be built using standard components of functional programming, and gives a reasoning framework for studying compositionality of round-tripping properties.
The framework of applicative lenses [18] uses a function representation of lenses to lift the point-free restriction of the combinator-based languages, and enables bidirectional programming with explicit recursion and pattern matching. Note that the use of “applicative” in applicative lenses refers to the transitional sense of programming with \(\lambda \)-abstractions and functional applications, which is not directly related to applicative functors. In a subsequent work, the authors developed a language known as HOBiT [20], which went further in featuring proper binding of variables. Despite the success in supporting \(\lambda \)-abstractions and function applications in programming bidirectional transformations, none of the languages have explored advanced patterns such as monadic programming.
The work on monadic lenses [1] investigates lenses with effects. For instance, a “put” could require additional input to resolve conflicts. Representing effects with monads helps reformulate the laws of round-tripping. In contrast, we made the type of lenses itself a monad, and showed how they can be composed monadically. Our method is applicable to monadic lenses, yielding what one might call monadic monadic lenses: monadically composable lenses with monadic effects. We conjecture that laws for monadic lenses can be adapted to this setting with similar compositionality properties, reusing our reasoning framework.
Other work leverages profunctors for bidirectionality. Notably, a Profunctor optic [26] between a source type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwx_HTML.gif and a view type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwy_HTML.gif is a function of type https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figwz_HTML.gif , for an abstract profunctor https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figxa_HTML.gif . Profunctor optics and our monadic profunctors offer orthogonal composition patterns: profunctor optics can be composed “vertically” using function composition, whereas monadic profunctor composition is “horizontal” providing sequential composition. In both cases, composition in the other direction can only be obtained by breaking the abstraction.
It is folklore in the Haskell community that profunctors can be combined with applicative functors [22]. The pattern is sometimes called a monoidal profunctor. The codec library [8] mentioned in Sect. 3 prominently features two applications of this applicative programming style: binary serialisation (a form of parsing/printing) and conversion to and from JSON structures (analogous to lenses above). Opaleye [28], an EDSL of SQL queries for Postgres databases, uses an interface of monoidal profunctors to implement generic operations such as transformations between Haskell datatypes and database queries and responses.
Our framework adapts gracefully to applicative programming, a restricted form of monadic programming. By separating the input type from the output type, we can reuse the existing interface of applicative functors without modification. Besides our generalisation to monads, purification and verifying round-tripping properties via (quasi)compositionality are novel in our framework.
Rendel and Ostermann proposed an interface for programming parsers and printers together [30], but they were unable to reuse the existing structure of https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figxb_HTML.gif , https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figxc_HTML.gif and https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figxd_HTML.gif classes (because of the need to handle types that are both covariant and contravariant), and had to reproduce the entire hierarchy separately. In contrast, our approach reuses the standard type class hierarchy, further extending the expressive power of bidirectional programming in Haskell. FliPpr [17, 19] is an invertible language that generates a parser from a definition of a pretty printer. In this paper, our biparser definitions are more similar to those of parsers than printers. This makes sense as it has been established that many parsers are monadic. Similar to the case of HOBiT, there is no discussion of monadic programming in the FliPpr work.
Previous approaches to unifying random generators and predicates mostly focused on deriving generators from predicates. One general technique evaluates predicates lazily to drive generation (random or enumerative) [7, 9], but one loses control over the resulting distribution of generated values. Luck [15] is a domain-specific language blending narrowing and constraint solving to specify generators as predicates with user-provided annotations to control the probability distribution. In contrast, our programs can be viewed as generators annotated with left inverses with which to derive predicates. This reversed perspective comes with trade-offs: high-level properties would be more naturally expressed in a declarative language of predicates, whereas it is a priori more convenient to implement complex generation strategies in a specialised framework for random generators.
Conclusions. This paper advances the expressive power of bidirectional programming; we showed that the classic bidirectional patterns of parsers/printers and lenses can be restructured in terms of monadic profunctors to provide sequential composition, with associated reasoning techniques. This opens up a new area in the design of embedded domain-specific languages for BX programming, that does not restrict programmers to stylised interfaces. Our example of bigenerators broadened the scope of BX programming from transformations (converting between two data representations) to non-transformational applications.
To demonstrate the applicability of our approach to real code, we have developed two bidirectional libraries [12], one extending the attoparsec monadic parser combinator library to biparsers and one extending QuickCheck to bigenerators. One area for further work is studying biparsers with lookahead. Currently lookahead can be expressed in our extended attoparsec, but understanding its interaction with (quasi)compositional round-tripping is further work.
However, this is not the final word on sequentially composable BX programs. In all three applications, round-tripping properties are similarly split into weak round tripping, which is weaker than the original property but compositional, and purifiable, which is equationally friendly. An open question is whether an underlying structure can be formalised, perhaps based on an adjunction model, that captures bidirectionality even more concretely than monadic profunctors.

Acknowledgments

We thank the anonymous reviewers for their helpful comments. The second author was supported partly by EPSRC grant EP/M026124/1.
Open Access This chapter is licensed under the terms of the Creative Commons Attribution 4.0 International License (http://creativecommons.org/licenses/by/4.0/), which permits use, sharing, adaptation, distribution and reproduction in any medium or format, as long as you give appropriate credit to the original author(s) and the source, provide a link to the Creative Commons license and indicate if changes were made.
The images or other third party material in this chapter are included in the chapter's Creative Commons license, unless indicated otherwise in a credit line to the material. If material is not included in the chapter's Creative Commons license and your intended use is not permitted by statutory regulation or exceeds the permitted use, you will need to obtain permission directly from the copyright holder.
Fußnoten
2
As of GHC 8.6, the https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgw_HTML.gif extension allows universal quantification in constraints, written as forall u. Functor (p u), but for simplicity we use the constraint constructor https://static-content.springer.com/image/chp%3A10.1007%2F978-3-030-17184-1_6/MediaObjects/480721_1_En_6_Figgx_HTML.gif from the constraints package: http://​hackage.​haskell.​org/​package/​constraints.
 
3
Smart constructors (and dually smart deconstructors) are just functions that hide boilerplate code for constructing and deconstructing data types.
 
Literatur
2.
Zurück zum Zitat Bancilhon, F., Spyratos, N.: Update semantics of relational views. ACM Trans. Database Syst. 6(4), 557–575 (1981)CrossRef Bancilhon, F., Spyratos, N.: Update semantics of relational views. ACM Trans. Database Syst. 6(4), 557–575 (1981)CrossRef
3.
Zurück zum Zitat Barbosa, D.M.J., Cretin, J., Foster, N., Greenberg, M., Pierce, B.C.: Matching lenses: alignment and view update. In: International Conference on Functional Programming (ICFP), pp. 193–204. ACM (2010) Barbosa, D.M.J., Cretin, J., Foster, N., Greenberg, M., Pierce, B.C.: Matching lenses: alignment and view update. In: International Conference on Functional Programming (ICFP), pp. 193–204. ACM (2010)
4.
Zurück zum Zitat Bohannon, A., Foster, J.N., Pierce, B.C., Pilkiewicz, A., Schmitt, A.: Boomerang: resourceful lenses for string data. In: Symposium on Principles of Programming Languages (POPL), pp. 407–419. ACM (2008) Bohannon, A., Foster, J.N., Pierce, B.C., Pilkiewicz, A., Schmitt, A.: Boomerang: resourceful lenses for string data. In: Symposium on Principles of Programming Languages (POPL), pp. 407–419. ACM (2008)
5.
Zurück zum Zitat Bohannon, A., Pierce, B.C., Vaughan, J.A.: Relational lenses: a language for updatable views. In: Symposium on Principles of Database Systems (PODS), pp. 338–347. ACM (2006) Bohannon, A., Pierce, B.C., Vaughan, J.A.: Relational lenses: a language for updatable views. In: Symposium on Principles of Database Systems (PODS), pp. 338–347. ACM (2006)
6.
Zurück zum Zitat Bottu, G.-J., Karachalias, G., Schrijvers, T., Oliveira, B.C.d.S., Wadler, P.: Quantified class constraints. In: International Symposium on Haskell (Haskell), pp. 148–161. ACM (2017) Bottu, G.-J., Karachalias, G., Schrijvers, T., Oliveira, B.C.d.S., Wadler, P.: Quantified class constraints. In: International Symposium on Haskell (Haskell), pp. 148–161. ACM (2017)
7.
Zurück zum Zitat Boyapati, C., Khurshid, S., Marinov, D.: Korat: automated testing based on Java predicates. In: International Symposium on Software Testing and Analysis (ISSTA), pp. 123–133. ACM (2002) Boyapati, C., Khurshid, S., Marinov, D.: Korat: automated testing based on Java predicates. In: International Symposium on Software Testing and Analysis (ISSTA), pp. 123–133. ACM (2002)
9.
Zurück zum Zitat Claessen, K., Duregård, J., Palka, M.H.: Generating constrained random data with uniform distribution. J. Funct. Program. 25 (2015). Article e8 Claessen, K., Duregård, J., Palka, M.H.: Generating constrained random data with uniform distribution. J. Funct. Program. 25 (2015). Article e8
10.
Zurück zum Zitat Claessen, K., Hughes, J.: QuickCheck: a lightweight tool for random testing of Haskell programs. In: International Conference on Functional Programming (ICFP), pp. 268–279. ACM (2000) Claessen, K., Hughes, J.: QuickCheck: a lightweight tool for random testing of Haskell programs. In: International Conference on Functional Programming (ICFP), pp. 268–279. ACM (2000)
13.
Zurück zum Zitat Foster, J.N., Greenwald, M.B., Moore, J.T., Pierce, B.C., Schmitt, A.: Combinators for bidirectional tree transformations: a linguistic approach to the view-update problem. ACM Trans. Program. Lang. Syst. 29(3), 17 (2007)CrossRef Foster, J.N., Greenwald, M.B., Moore, J.T., Pierce, B.C., Schmitt, A.: Combinators for bidirectional tree transformations: a linguistic approach to the view-update problem. ACM Trans. Program. Lang. Syst. 29(3), 17 (2007)CrossRef
14.
Zurück zum Zitat Foster, N., Matsuda, K., Voigtländer, J.: Three complementary approaches to bidirectional programming. In: SSGIP, pp. 1–46 (2010) Foster, N., Matsuda, K., Voigtländer, J.: Three complementary approaches to bidirectional programming. In: SSGIP, pp. 1–46 (2010)
15.
Zurück zum Zitat Lampropoulos, L., Gallois-Wong, D., Hritcu, C., Hughes, J., Pierce, B.C., Xia, L.-y.: Beginner’s luck: a language for property-based generators. In: Symposium on Principles of Programming Languages (POPL), pp. 114–129. ACM (2017) Lampropoulos, L., Gallois-Wong, D., Hritcu, C., Hughes, J., Pierce, B.C., Xia, L.-y.: Beginner’s luck: a language for property-based generators. In: Symposium on Principles of Programming Languages (POPL), pp. 114–129. ACM (2017)
16.
Zurück zum Zitat Matsuda, K., Hu, Z., Nakano, K., Hamana, M., Takeichi, M.: Bidirectionalization transformation based on automatic derivation of view complement functions. In: International Conference on Functional Programming (ICFP), pp. 47–58. ACM (2007) Matsuda, K., Hu, Z., Nakano, K., Hamana, M., Takeichi, M.: Bidirectionalization transformation based on automatic derivation of view complement functions. In: International Conference on Functional Programming (ICFP), pp. 47–58. ACM (2007)
18.
Zurück zum Zitat Matsuda, K., Wang, M.: Applicative bidirectional programming with lenses. In: International Conference on Functional Programming (ICFP), pp. 62–74. ACM (2015) Matsuda, K., Wang, M.: Applicative bidirectional programming with lenses. In: International Conference on Functional Programming (ICFP), pp. 62–74. ACM (2015)
19.
Zurück zum Zitat Matsuda, K., Wang, M.: Embedding invertible languages with binders: a case of the FliPpr language. In: International Symposium on Haskell (Haskell), pp. 158–171. ACM (2018) Matsuda, K., Wang, M.: Embedding invertible languages with binders: a case of the FliPpr language. In: International Symposium on Haskell (Haskell), pp. 158–171. ACM (2018)
21.
Zurück zum Zitat Mayer, M., Kuncak, V., Chugh, R.: Bidirectional evaluation with direct manipulation. Proc. ACM Program. Lang. 2(OOPSLA), 127:1–127:28 (2018)CrossRef Mayer, M., Kuncak, V., Chugh, R.: Bidirectional evaluation with direct manipulation. Proc. ACM Program. Lang. 2(OOPSLA), 127:1–127:28 (2018)CrossRef
22.
Zurück zum Zitat McBride, C., Paterson, R.: Applicative programming with effects. J. Funct. Program. 18(1), 1–13 (2008)CrossRef McBride, C., Paterson, R.: Applicative programming with effects. J. Funct. Program. 18(1), 1–13 (2008)CrossRef
24.
Zurück zum Zitat Pacheco, H., Hu, Z., Fischer, S.: Monadic combinators for “Putback” style bidirectional programming. In: Workshop on Partial Evaluation and Program Manipulation (PEPM), pp. 39–50. ACM (2014) Pacheco, H., Hu, Z., Fischer, S.: Monadic combinators for “Putback” style bidirectional programming. In: Workshop on Partial Evaluation and Program Manipulation (PEPM), pp. 39–50. ACM (2014)
25.
Zurück zum Zitat Pacheco, H., Zan, T., Hu, Z.: BiFluX: a bidirectional functional update language for XML. In: International Symposium on Principles and Practice of Declarative Programming (PPDP). ACM (2014) Pacheco, H., Zan, T., Hu, Z.: BiFluX: a bidirectional functional update language for XML. In: International Symposium on Principles and Practice of Declarative Programming (PPDP). ACM (2014)
26.
Zurück zum Zitat Pickering, M., Gibbons, J., Wu, N.: Profunctor optics: modular data accessors. Art Sci. Eng. Program. 1(2) (2017). Article 7 Pickering, M., Gibbons, J., Wu, N.: Profunctor optics: modular data accessors. Art Sci. Eng. Program. 1(2) (2017). Article 7
27.
Zurück zum Zitat Pombrio, J., Krishnamurthi, S.: Resugaring: lifting evaluation sequences through syntactic sugar. In: Programming Language Design and Implementation (PLDI). ACM (2014) Pombrio, J., Krishnamurthi, S.: Resugaring: lifting evaluation sequences through syntactic sugar. In: Programming Language Design and Implementation (PLDI). ACM (2014)
29.
Zurück zum Zitat Rajkumar, R., Lindley, S., Foster, N., Cheney, J.: Lenses for web data. In: International Workshop on Bidirectional Transformations (BX) (2013) Rajkumar, R., Lindley, S., Foster, N., Cheney, J.: Lenses for web data. In: International Workshop on Bidirectional Transformations (BX) (2013)
30.
Zurück zum Zitat Rendel, T., Ostermann, K.: Invertible syntax descriptions: unifying parsing and pretty-printing. In: International Symposium on Haskell (Haskell), pp. 1–12 (2010) Rendel, T., Ostermann, K.: Invertible syntax descriptions: unifying parsing and pretty-printing. In: International Symposium on Haskell (Haskell), pp. 1–12 (2010)
33.
Zurück zum Zitat Voigtländer, J.: Bidirectionalization for free! (Pearl). In: Symposium on Principles of Programming Languages (POPL), pp. 165–176. ACM (2009) Voigtländer, J.: Bidirectionalization for free! (Pearl). In: Symposium on Principles of Programming Languages (POPL), pp. 165–176. ACM (2009)
34.
Zurück zum Zitat Wadler, P.: Theorems for free! In: FPCA, pp. 347–359 (1989) Wadler, P.: Theorems for free! In: FPCA, pp. 347–359 (1989)
Metadaten
Titel
Composing Bidirectional Programs Monadically
verfasst von
Li-yao Xia
Dominic Orchard
Meng Wang
Copyright-Jahr
2019
DOI
https://doi.org/10.1007/978-3-030-17184-1_6

Premium Partner