Composing Bidirectional Programs Monadically
 8.4k Downloads
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 roundtripping 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 wellknown example solves the viewupdate 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 wellknown language family for BX programming is lenses [13]. A lens captures transformations between sources S and views V via a pair of functions Open image in new window \(: S \rightarrow V\) and Open image in new window \(: V \rightarrow S \rightarrow S\). The Open image in new window function extracts a view from a source and Open image in new window takes an updated view and a source as inputs to produce an updated source. The asymmetrical nature of Open image in new window and Open image in new window makes it possible for Open image in new window to recover some of the source data that is not present in the view. In other words, Open image in new window does not have to be injective to have a corresponding Open image in new window .
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 unidirectional 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 welltried 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 nonroundtripping BXs. We tackle this issue by proposing a new compositional reasoning framework that is flexible enough to characterise a variety of roundtripping properties, and simplifies the necessary reasoning.

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 applicationspecific 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 wellexplored 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 roundtripping 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 machinechecked 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 “ Open image in new window ”.
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.
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 rightassociated syntax trees.
Thus, parsing and printing follows a pattern of inverselike 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 inverselike 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. Propertybased testing (e.g., QuickCheck) [10] expresses program properties as executable predicates. For instance, the following property checks that an insertion function Open image in new window , given a sorted list—as checked by the predicate Open image in new window —produces another sorted list. The combinator Open image in new window represents implication for properties.
To test this property, a testing framework generates random inputs for Open image in new window and Open image in new window . The implementation of Open image in new window applied here first checks whether Open image in new window is sorted, and if it is, checks that Open image in new window 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 Open image in new window 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 Open image in new window 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: Open image in new window .
One can expect two complementary properties of such a generator. A generator is sound with respect to the predicate Open image in new window if it generates only values satisfying Open image in new window ; soundness means that no tests are discarded, hence the tested property is better exercised. A generator is complete with respect to Open image in new window if it can generate all satisfying values; completeness ensures the correctness of testing a property with Open image in new window 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 roundtripping 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 Open image in new window will also be called Open image in new window . Similarly, for some type constructor Open image in new window , a variable of type Open image in new window will be called Open image in new window . A function Open image in new window (a Kleisli arrow for a monad Open image in new window ) will be called Open image in new window .
Bind first runs the parser Open image in new window on an input string Open image in new window , resulting in a value Open image in new window which is used to create the parser Open image in new window , which is in turn run on the remaining input Open image in new window to produce parsed values of type Open image in new window . The Open image in new window operation creates a trivial parser for any value Open image in new window which does not consume any input but simply produces Open image in new window .
In practice, parsers composed with Open image in new window 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 Open image in new window .
However, this type of printer Open image in new window (shown also in Sect. 1.1) cannot form a monad because it is contravariant in its type parameter Open image in new window . Concretely, we cannot implement the bind ( Open image in new window ) operator for values with types of this form:
We are stuck trying to fill the hole ( Open image in new window ) as there is no way to get a value of type Open image in new window to pass as an argument to Open image in new window (first printer) and Open image in new window (second printer which depends on a Open image in new window ). Subsequently, we cannot construct a monadic biparser by simply taking a product of the parser monad and Open image in new window and leveraging the result that the product of two monads is a monad.
This is closer to the monadic form, where Open image in new window resolves the difficulty of contravariance by “contextualizing” the printers. Thus, the first printer is no longer just “a printer of Open image in new window ”, but “a printer of Open image in new window extracted Open image in new window Open image in new window ”. 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 Open image in new window are contained within the values returned by the second parser for Open image in new window , thus a notion of projection can be defined and used here to recover a Open image in new window in order to build the corresponding printer compositionally.
If we fix the first parameter type Open image in new window , then the type Open image in new window of printers for Open image in new window values is indeed monadic, combining a reader monad (for some global readonly parameter of type Open image in new window ) and a writer monad (for strings), with implementation:
The printer Open image in new window ignores its input and prints nothing. For bind, an input Open image in new window is shared by both printers and the resulting strings are concatenated.
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 Open image in new window are the same it is easy to interpret this type: a biparser Open image in new window is a parser from strings to Open image in new window values and printer from Open image in new window values to strings. We refer to biparsers of this type as aligned biparsers. What about when the type parameters differ? A biparser of type Open image in new window provides a parser from strings to Open image in new window values and a printer from Open image in new window values to strings, but where the printers can compute Open image in new window values from Open image in new window values, i.e., Open image in new window is some common broader representation which contains relevant Open image in new window typed subcomponents. A biparser Open image in new window can be thought of as printing a certain subtree Open image in new window from the broader representation of a syntax tree Open image in new window .
The corresponding monad for Open image in new window is the product of the previous two monad definitions for Open image in new window and Open image in new window , 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]
In the rest of this section, we use the alias “ Open image in new window ” for Open image in new window with flipped parameters where we read Open image in new window as applying the printer of Open image in new window on a subpart of an input of type Open image in new window calculated by Open image in new window , thus yielding a biparser of type Open image in new window .
An example biparser. Let us write a biparser, Open image in new window , 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 Open image in new window is printed as its singleletter string (a singleton list) paired with Open image in new window .
Next, we define a biparser Open image in new window for an integer followed by a single space. An auxiliary biparser Open image in new window (on the right) parses an integer one digit at a time into a string. Note that in Haskell, the Open image in new window notation statement Open image in new window desugars to “ Open image in new window ...” which uses Open image in new window and a function binding Open image in new window in the scope of the rest of the desugared block.
On the right, Open image in new window extracts a Open image in new window consisting of digits followed by a single space. As a parser, it parses a character ( Open image in new window ); if it is a digit then it continues parsing recursively ( Open image in new window ) appending the first character to the result ( Open image in new window ). Otherwise, if the parsed character is a space the parser returns Open image in new window . As a printer, Open image in new window expects a nonempty string of the same format; Open image in new window extracts the first character of the input, then Open image in new window prints it and returns it back as Open image in new window ; if it is a digit, then Open image in new window 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 Open image in new window uses Open image in new window to convert an input string of digits (parsed by Open image in new window ) into an integer, and Open image in new window to convert an integer to an output string printed by Open image in new window . A safer implementation could return the Open image in new window type when parsing but we keep things simple here for now.
After parsing an integer Open image in new window , we can parse the string following it by iterating Open image in new window times the biparser Open image in new window . This is captured by the Open image in new window combinator below, defined recursively like Open image in new window but with the termination condition given by an external parameter. To iterate Open image in new window times a biparser Open image in new window : if Open image in new window , there is nothing to do and we return the empty list; otherwise for Open image in new window , we run Open image in new window once to get the head Open image in new window , and recursively iterate Open image in new window times to get the tail Open image in new window .
Note that although not reflected in its type, Open image in new window expects, as a printer, a list Open image in new window of length Open image in new window : if Open image in new window , there is nothing to print; if Open image in new window , Open image in new window extracts the head of Open image in new window to print it with Open image in new window , and Open image in new window extracts its tail, of length Open image in new window , to print it recursively.
Interestingly, if we erase applications of Open image in new window , i.e., we substitute every expression of the form Open image in new window with Open image in new window 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 Open image in new window is the identity on the parser component of Open image in new window . Thus the biparser code closely resembles standard, idiomatic monadic parser code but with “annotations” via Open image in new window expressing how to apply the backwards direction of printing to subparts of the parsed string.
Despite its simplicity, the syntax of lengthprefixed strings is notably contextsensitive. 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. Contextsensitivity is standard for parser combinators in contrast with parser generators, e.g., Yacc, and applicative parsers, which are mostly restricted to contextfree 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 Open image in new window and the Open image in new window operation (also called Open image in new window , with flipped arguments). We describe a type as being a monadic profunctor when it has both a monadic structure and a Open image in new window 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 ( Open image in new window ) which is functorial and monadic in the second parameter and contravariantly functorial in the first parameter (provided by the Open image in new window operation). In standard terminology, a twoparameter type Open image in new window 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 programmingoriented terminology, capturing the Open image in new window operation via a class Open image in new window . In the preceding section, some uses of Open image in new window involved a partial function, e.g., Open image in new window . We make the possibility of partiality explicit via the Open image in new window type, yielding the following definition.
Definition 1
where Open image in new window composes partial functions (lefttoright), captured by Kleisli arrows of the Maybe monad.
The constraint Open image in new window captures a universally quantified constraint [6]: for all types Open image in new window then Open image in new window has an instance of the Open image in new window class.^{2}
The requirement for Open image in new window to take partial functions is in response to the frequent need to restrict the domain of bidirectional transformations. In combinatorbased 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 Open image in new window .
Since the contravariant part of the bifunctor applies to functions of type Open image in new window , 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 ( Open image in new window ) monad.
Definition 2
Corollary 1
Biparsers form a monadic profunctor as there is an instance of Open image in new window and Open image in new window 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 Open image in new window of a profunctor Open image in new window is called aligned if Open image in new window = Open image in new window .
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 Open image in new window (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., Open image in new window ; the second is as a function type from the contravariant parameter Open image in new window to Open image in new window , i.e., Open image in new window . These are standard mathematical constructions, and the latter appears in the Haskell profunctors package with the name Open image in new window . Our core construction is based on these two ways of creating a profunctor, which we call Open image in new window and Open image in new window respectively:
This provides an interface for monads which can internalise a notion of failure, as captured at the toplevel by Open image in new window in Open image in new window .
3.2 Deriving Biparsers as Monadic Profunctor Pairs
In a similar manner, we will use this monadic profunctor construction to define monadic bidirectional transformations for lenses (Sect. 5) and bigenerators (Sect. 6).
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 ( Open image in new window ) to the one above, and provided Open image in new window and Open image in new window 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 Open image in new window 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 roundtripping laws that relate the two components of a bidirectional program. For every bidirectional transformation we can define dual properties: backward round tripping (going backwardsthenforwards) and forward round tripping (going forwardsthenbackwards). In each BX domain, such properties also capture additional domainspecific 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 Open image in new window as an alias for Open image in new window .
Definition 4
That is, if a biparser Open image in new window when used as a printer (going backwards) on an input value Open image in new window produces a string Open image in new window , then using Open image in new window as a parser on a string with prefix Open image in new window and suffix Open image in new window yields the original input value Open image in new window and the remaining input Open image in new window .
Note that backward round tripping is defined for aligned biparsers (of type Open image in new window ) since the same value Open image in new window is used as both the input of the printer (typed by the first type parameter of Open image in new window ) and as the expected output of the parser (typed by the second type parameter of Open image in new window ).
The dual property is forward round tripping: a source string Open image in new window is parsed (going forwards) into some value Open image in new window which when printed produces the initial source Open image in new window :
Definition 5
A biparser Open image in new window is forward round tripping if for every Open image in new window and Open image in new window we have that:
Proposition 1
The biparser Open image in new window (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 prettyprinted code is lexically different to the original source. However, different notions of equality enable more reasonable forward roundtripping properties.
Although one can check roundtripping properties of biparsers by expanding their definitions and the underlying monadic profunctor operations, a more scalable approach is provided if a roundtripping 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 roundtripping 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 Open image in new window is a family of subsets Open image in new window of Open image in new window indexed by types Open image in new window and Open image in new window .
Definition 6
Unfortunately for biparsers, forward and backward round tripping as defined above are not compositional: Open image in new window is not backward round tripping and Open image in new window does not preserve forward round tripping. Furthermore, these two properties are restricted to biparsers of type Open image in new window (i.e., aligned biparsers) but compositionality requires that the two type parameters of the monadic profunctor can differ in the case of Open image in new window and Open image in new window . 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 Open image in new window , the input type of the printer Open image in new window differs from the output type of the parser Open image in new window , so we cannot compare them. But our intent for printers is that what we actually print is a fragment of Open image in new window , 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
Removing backward round tripping’s restriction to aligned biparsers and using the result Open image in new window 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 Open image in new window is weak backward round tripping.
Corollary 2
Propositions 2 & 3 imply Open image in new window is weak backward round tripping.
Ultimately, when a biparser is aligned ( Open image in new window ) we want an input to the printer to be returned in its output, i.e, Open image in new window should equal Open image in new window . If this is the case, we recover the original backward round tripping property:
Theorem 1
If Open image in new window is weak backward round tripping, and for all Open image in new window . Open image in new window , then Open image in new window is backward round tripping.
Thus, for any biparser Open image in new window , we can get backward round tripping by proving that its atomic subcomponents are weak backward round tripping, and proving that Open image in new window . The interesting aspect of the purification condition here is that it renders irrelevant the domainspecific effects of the biparser, i.e., those related to manipulating source strings. This considerably simplifies any proof. Furthermore, the definition of Open image in new window is a monadic profunctor homomorphism which provides a set of equations that can be used to expedite the reasoning.
Definition 8
Proposition 4
The Open image in new window operation for biparsers (above) is a monadic profunctor homomorphism between Open image in new window and the monadic profunctor Open image in new window .
Corollary 3
(of Theorem 1 with Corollary 2 and Proposition 4) The biparser Open image in new window is backward round tripping.
Proof
Combining Open image in new window with Corollary 2 ( Open image in new window is weak backward round tripping) enables Theorem 1, proving that Open image in new window is backward round tripping.
The other two core examples in this paper also permit a definition of Open image in new window . We capture the general pattern as follows:
Definition 9
A purifiable monadic profunctor is a monadic profunctor Open image in new window with a homomorphism Open image in new window from Open image in new window to the monadic profunctor of partial functions Open image in new window . We say that Open image in new window is the pure projection of Open image in new window .
Definition 10
A pure projection Open image in new window is called the identity projection when Open image in new window for all Open image in new window .
Here and in Sects. 5 and 6, identity projections enable compositional roundtripping properties to be derived from more general noncompositional 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 metaproperty 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 roundtripping property (assuming f is the forwards direction). We can lift the notion of injectivity to the monadic profunctor setting and capture forward roundtripping properties that are preserved by the monadic profunctor operations, given some additional injectivitylike restriction. We first formalise the notion of an injective arrow.
Informally, an injective arrow Open image in new window produces an output from which the input can be recalculated:
Definition 11
Next, we define quasicompositionality which extends the compositionality metaproperty with the requirement for Open image in new window to be applied to injective arrows:
Definition 12
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 Open image in new window yields a value Open image in new window and a remaining string Open image in new window , and the printer outputs that same value Open image in new window along with a string Open image in new window , then Open image in new window is the prefix of Open image in new window that was consumed by the parser, i.e., Open image in new window .
Definition 13
Proposition 5
Weak forward round tripping is quasicompositional.
Proof
We sketch the qcompbind case, where Open image in new window for some Open image in new window and Open image in new window that are weak forward roundtripping. From Open image in new window , it follows that there exists Open image in new window , Open image in new window such that Open image in new window and Open image in new window . Similarly Open image in new window implies there exists Open image in new window , Open image in new window such that Open image in new window Open image in new window Open image in new window and Open image in new window Open image in new window Open image in new window and Open image in new window Open image in new window Open image in new window . Because Open image in new window is an injective arrow, we have Open image in new window (see appendix). We then use the assumption that Open image in new window and Open image in new window are weak forward roundtripping on Open image in new window and on Open image in new window , and deduce that Open image in new window and Open image in new window therefore Open image in new window .
Proposition 6
The Open image in new window biparser is weak forward round tripping.
Corollary 4
Propositions 5 and 6 imply that Open image in new window 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 Open image in new window in the definition of Open image in new window are injective arrows, apart from Open image in new window at the end of the auxiliary Open image in new window biparser. Indeed, the Open image in new window function is not injective since multiple strings may parse to the same integer: Open image in new window . But the precondition to the proposition (no redundant leading zero digits) restricts the input strings so that Open image in new window 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 Open image in new window is weak forward roundtripping, and for all Open image in new window , Open image in new window , then Open image in new window is forward round tripping.
Corollary 5
The biparser Open image in new window 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 roundtripping properties: forwardsthenbackwards and backwardsthenforwards, called just forward and backward here respectively. Whilst combinatorbased approaches can guarantee roundtripping by construction, we have made a tradeoff to get greater expressivity in the monadic approach. However, we regain the ability to reason about bidirectional transformations in a manageable, scalable way if roundtripping properties are compositional. Unfortunately, due to the monadic profunctor structuring, this tends not to be the case. Instead, weakened roundtripping 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
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, Open image in new window and Open image in new window 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 wellbehaved 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 Open image in new window and a predicate on sources in Open image in new window for additional dynamic checking. This is provided by the following Open image in new window and Open image in new window monadic profunctors:
Going forwards, getting a view Open image in new window from a source Open image in new window may fail if there is no view for the current source. Going backwards, putting a preview Open image in new window updates some source Open image in new window (via the state transformer Open image in new window ), but with some further structure returned, provided by Open image in new window (similar to the writer transformer used for biparsers, Sect. 3.2). The Open image in new window here captures the possibility that Open image in new window can fail. The Open image in new window 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 Open image in new window sets bit i to 1, then the returned predicate will return Open image in new window for all bit vectors where bit i is 1, and Open image in new window otherwise. This predicate can then be used to test whether further Open image in new window operations on the source have modified bit i.
LPutGet and LGetPut 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 Open image in new window when the above properties only hold when Open image in new window is an element of Open image in new window .
A simple lens example operates on keyvalue maps. For keys of type Open image in new window and values of type Open image in new window , we have the following source type and a simple lens:
The Open image in new window component of the Open image in new window lens does a lookup of the key Open image in new window in a map, producing Open image in new window of a Open image in new window . The Open image in new window component inserts a value for key Open image in new window . When the key already exists, Open image in new window overwrites its associated value.
Due to our approach, multiple calls to Open image in new window can be composed monadically, giving a lens that gets/sets multiple keyvalue 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 Open image in new window some value Open image in new window in a source Open image in new window , resulting in a source Open image in new window , then what we Open image in new window from Open image in new window is Open image in new window . However two important changes are needed to adapt to our generalised type of lenses and to ensure compositionality. First, the value Open image in new window that was put is now to be found in the output of Open image in new window , whereas there is no way to constrain the input of Open image in new window because its type Open image in new window is abstract. Second, by sequentially composing lenses such as in Open image in new window , the output source Open image in new window of Open image in new window will be further modified by Open image in new window , so this roundtripping property must constrain all potential modifications of Open image in new window . In fact, the predicate Open image in new window ensures exactly that the view Open image in new window has not changed and is still Open image in new window . It is not even necessary to refer to Open image in new window , which is just one source for which we expect Open image in new window to be Open image in new window .
Definition 14
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
Theorem 4
If a lens Open image in new window is weak backward round tripping and has identity projections on some subset Open image in new window (i.e., for all Open image in new window , Open image in new window then Open image in new window ) then Open image in new window is also backward round tripping on all Open image in new window .
To demonstrate, we apply this result to Open image in new window .
Proposition 8
The lens Open image in new window is weak backward round tripping.
Proposition 9
The lens Open image in new window has identity projection: Open image in new window Open image in new window Open image in new window Open image in new window .
Our lens Open image in new window is therefore weak backward round tripping by construction. We now interpret/purify Open image in new window as a partial function, which is actually the identity function when restricted to lists of the same length as Open image in new window .
Proposition 10
For all Open image in new window such that Open image in new window , and for all Open image in new window then Open image in new window .
Corollary 6
By the above results, Open image in new window for all Open image in new window is backward round tripping on lists of length Open image in new window .
The other direction, forward round tripping, follows a similar story. We first restate it as a quasicompositional property.
Definition 15
Theorem 5
Weak forward round tripping is a quasicompositional property.
Along with identity projection, this gives the original forward LGetPut property.
Theorem 6
If a lens Open image in new window is weak forward round tripping and has identity projections on some subset Open image in new window (i.e., for all Open image in new window , Open image in new window then Open image in new window ) then Open image in new window is also forward round tripping on Open image in new window .
We can thus apply this result to our example (details omitted).
Proposition 11
For all Open image in new window , the lens Open image in new window is forward round tripping on lists of length Open image in new window .
6 Monadic Bidirectional Programming for Generators
Lastly, we capture the novel notion of bidirectional generators (bigenerators) extending random generators in propertybased 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:
Due to space limitations, we refer readers to Appendix E [36] for an example of a compositionallydefined 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 Open image in new window , we write Open image in new window when Open image in new window is a possible output of the generator. The generator of a bigenerator Open image in new window should match its predicate Open image in new window . This requirement equates to roundtripping 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 Open image in new window is complete (backward round tripping) when Open image in new window implies Open image in new window .
Definition 17
A bigenerator Open image in new window is sound (forward round tripping) if for all Open image in new window , Open image in new window implies that Open image in new window .
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
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 Open image in new window is complete if it is weakcomplete and its checker satisfies a pure projection property: Open image in new window
Thus to prove completeness of a bigenerator Open image in new window , we first have weakcompleteness by construction, and we can then show that Open image in new window 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 Open image in new window be a random uniform bigenerator of booleans, then consider for example, Open image in new window and Open image in new window , where Open image in new window and Open image in new window . Both satisfy any quasicompositional property satisfied by Open image in new window , and both have the same pure projection Open image in new window , and yet the former is unsound—it can generate Open image in new window , which is rejected by Open image in new window —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.
7 Discussion and Related Work
Bidirectional transformations are a widely applicable technique used in many domains [11]. Among languagebased 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 roundtripping properties.
The framework of applicative lenses [18] uses a function representation of lenses to lift the pointfree restriction of the combinatorbased 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 roundtripping. 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 Open image in new window and a view type Open image in new window is a function of type Open image in new window , for an abstract profunctor Open image in new window . 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 roundtripping 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 Open image in new window , Open image in new window and Open image in new window 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 domainspecific language blending narrowing and constraint solving to specify generators as predicates with userprovided 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 tradeoffs: highlevel 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 domainspecific 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 nontransformational 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 roundtripping is further work.
However, this is not the final word on sequentially composable BX programs. In all three applications, roundtripping 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.
Footnotes
 1.
 2.
As of GHC 8.6, the Open image in new window extension allows universal quantification in constraints, written as forall u. Functor (p u), but for simplicity we use the constraint constructor Open image in new window 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.
Notes
Acknowledgments
We thank the anonymous reviewers for their helpful comments. The second author was supported partly by EPSRC grant EP/M026124/1.
References
 1.AbouSaleh, F., Cheney, J., Gibbons, J., McKinna, J., Stevens, P.: Reflections on monadic lenses. In: Lindley, S., McBride, C., Trinder, P., Sannella, D. (eds.) A List of Successes That Can Change the World. LNCS, vol. 9600, pp. 1–31. Springer, Cham (2016). https://doi.org/10.1007/9783319309361_1CrossRefGoogle Scholar
 2.Bancilhon, F., Spyratos, N.: Update semantics of relational views. ACM Trans. Database Syst. 6(4), 557–575 (1981)CrossRefGoogle Scholar
 3.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)Google Scholar
 4.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)Google Scholar
 5.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)Google Scholar
 6.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)Google Scholar
 7.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)Google Scholar
 8.Chilton, P.: Codec library. https://hackage.haskell.org/package/codec
 9.Claessen, K., Duregård, J., Palka, M.H.: Generating constrained random data with uniform distribution. J. Funct. Program. 25 (2015). Article e8Google Scholar
 10.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)Google Scholar
 11.Czarnecki, K., Foster, J.N., Hu, Z., Lämmel, R., Schürr, A., Terwilliger, J.F.: Bidirectional transformations: a crossdiscipline perspective. In: Paige, R.F. (ed.) ICMT 2009. LNCS, vol. 5563, pp. 260–283. Springer, Heidelberg (2009). https://doi.org/10.1007/9783642024085_19CrossRefGoogle Scholar
 12.Xia et al.: Further implementations, November 2018. https://github.com/Lysxia/profunctormonad
 13.Foster, J.N., Greenwald, M.B., Moore, J.T., Pierce, B.C., Schmitt, A.: Combinators for bidirectional tree transformations: a linguistic approach to the viewupdate problem. ACM Trans. Program. Lang. Syst. 29(3), 17 (2007)CrossRefGoogle Scholar
 14.Foster, N., Matsuda, K., Voigtländer, J.: Three complementary approaches to bidirectional programming. In: SSGIP, pp. 1–46 (2010)Google Scholar
 15.Lampropoulos, L., GalloisWong, D., Hritcu, C., Hughes, J., Pierce, B.C., Xia, L.y.: Beginner’s luck: a language for propertybased generators. In: Symposium on Principles of Programming Languages (POPL), pp. 114–129. ACM (2017)Google Scholar
 16.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)Google Scholar
 17.Matsuda, K., Wang, M.: FliPpr: a prettier invertible printing system. In: Felleisen, M., Gardner, P. (eds.) ESOP 2013. LNCS, vol. 7792, pp. 101–120. Springer, Heidelberg (2013). https://doi.org/10.1007/9783642370366_6CrossRefGoogle Scholar
 18.Matsuda, K., Wang, M.: Applicative bidirectional programming with lenses. In: International Conference on Functional Programming (ICFP), pp. 62–74. ACM (2015)Google Scholar
 19.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)Google Scholar
 20.Matsuda, K., Wang, M.: HOBiT: programming lenses without using lens combinators. In: Ahmed, A. (ed.) ESOP 2018. LNCS, vol. 10801, pp. 31–59. Springer, Cham (2018). https://doi.org/10.1007/9783319898841_2CrossRefGoogle Scholar
 21.Mayer, M., Kuncak, V., Chugh, R.: Bidirectional evaluation with direct manipulation. Proc. ACM Program. Lang. 2(OOPSLA), 127:1–127:28 (2018)CrossRefGoogle Scholar
 22.McBride, C., Paterson, R.: Applicative programming with effects. J. Funct. Program. 18(1), 1–13 (2008)CrossRefGoogle Scholar
 23.Mu, S.C., Hu, Z., Takeichi, M.: An algebraic approach to bidirectional updating. In: Chin, W.N. (ed.) APLAS 2004. LNCS, vol. 3302, pp. 2–20. Springer, Heidelberg (2004). https://doi.org/10.1007/9783540304777_2CrossRefGoogle Scholar
 24.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)Google Scholar
 25.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)Google Scholar
 26.Pickering, M., Gibbons, J., Wu, N.: Profunctor optics: modular data accessors. Art Sci. Eng. Program. 1(2) (2017). Article 7Google Scholar
 27.Pombrio, J., Krishnamurthi, S.: Resugaring: lifting evaluation sequences through syntactic sugar. In: Programming Language Design and Implementation (PLDI). ACM (2014)Google Scholar
 28.Purely Agile. Opaleye library. https://hackage.haskell.org/package/opaleye
 29.Rajkumar, R., Lindley, S., Foster, N., Cheney, J.: Lenses for web data. In: International Workshop on Bidirectional Transformations (BX) (2013)Google Scholar
 30.Rendel, T., Ostermann, K.: Invertible syntax descriptions: unifying parsing and prettyprinting. In: International Symposium on Haskell (Haskell), pp. 1–12 (2010)Google Scholar
 31.Schuster, C., Disney, T., Flanagan, C.: Macrofication: refactoring by reverse macro expansion. In: Thiemann, P. (ed.) ESOP 2016. LNCS, vol. 9632, pp. 644–671. Springer, Heidelberg (2016). https://doi.org/10.1007/9783662494981_25CrossRefGoogle Scholar
 32.Stevens, P.: A landscape of bidirectional model transformations. In: Lämmel, R., Visser, J., Saraiva, J. (eds.) GTTSE 2007. LNCS, vol. 5235, pp. 408–424. Springer, Heidelberg (2008). https://doi.org/10.1007/9783540886433_10CrossRefGoogle Scholar
 33.Voigtländer, J.: Bidirectionalization for free! (Pearl). In: Symposium on Principles of Programming Languages (POPL), pp. 165–176. ACM (2009)Google Scholar
 34.Wadler, P.: Theorems for free! In: FPCA, pp. 347–359 (1989)Google Scholar
 35.Wadler, P.: Monads for functional programming. In: Jeuring, J., Meijer, E. (eds.) AFP 1995. LNCS, vol. 925, pp. 24–52. Springer, Heidelberg (1995). https://doi.org/10.1007/3540594515_2CrossRefGoogle Scholar
 36.Xia, L.Y., Orchard, D., Wang, M.: Composing bidirectional programs monadically (with appendices) (2019). https://arxiv.org/abs/1902.06950
Copyright information
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.