Combinators for polymorphic change structures

In document Optimizing and Incrementalizing Higher-order Collection Queries by AST Transformation (Page 186-188)

Earlier, we restricted our transformation on λ→terms, so that there could be a change dt from t1to t2only if t1and if t2have the same type. In this section we lift this restriction and define polymorphic change structures (also called change structures when no ambiguity arises). To do so, we sketch how to extend core change-structure operations to polymorphic change structures.

We also show a few combinators, combining existing polymorphic change structures into new ones. We believe the combinator types are more enlightening than their implementations, but we include them here for completeness. We already described a few constructions on non-polymorphic change structures; however, polymorphic change structures enable new constructions that insert or remove data, or apply isomorphisms to the source or destination type.

We conjecture that combinators for polymorphic change structure can be used to compose, out of small building blocks, change structures that, for instance, allow inserting or removing elements from a recursive datatype such as lists. Derivatives for primitives could then be produced using equational reasoning as described in Sec. 11.2. However, we leave investigation of such avenues to future work.

We describe change operations for polymorphic change structures via a Haskell record contain- ing change operations, and we define combinators on polymorphic change structures. Of all change operations, we only consider ⊕; to avoid confusion, we write ⊞ for the polymorphic variant of ⊕.

data CS τ1δτ τ2= CS { (⊞) :: τ1→ δτ → τ2 }

This code define a record type constructor CS with a data constructor also written CS, and a field accessor written (⊞); we use τ1, δτ and τ2(and later also σ1, δσ and σ2) for Haskell type variables. To follow Haskell lexical rules, we use here lowercase letters (even though Greek ones).

We have not formalized definitions of validity, or proofs that it agrees with ⊞, but for all the change structures and combinators in this section, this exercise appears no harder than the ones in Chapter 13.

In Sec. 11.1 and 17.2 change structures are embedded in Haskell using type class ChangeStruct τ . Conversely, here we do not define a type class of polymorphic change structures, because (apart from the simplest scenarios), Haskell type class resolution is unable to choose a canonical way to construct a polymorphic change structure using our combinators.

Chapter 18. Towards differentiation for System F 169 All existing change structures (that is, instances of ChangeStruct τ ) induce generalized change structures CS τ (∆τ) τ.

typeCS:: ChangeStruct τ ⇒ CS τ (∆τ) τ typeCS= CS (⊕)

We can also have change structures across different types. Replacement changes are possible: replCS:: CS τ1τ2τ2

replCS= CS $ λx1x2→ x2

But replacement changes are not the only option. For product types, or for any form of nested data, we can apply changes to the different components, changing the type of some components. We can also define a change structure for nullary products (the unit type) which can be useful as a building block in other change structures:

prodCS:: CS σ1δσ σ2→ CS τ1δτ τ2→ CS (σ1, τ1) (δσ, δτ) (σ2, τ2) prodCS scs tcs= CS $ λ(s1, t1) (ds, dt) → ((⊞) scs s1ds, (⊞) tcs t1dt) unitCS:: CS () () ()

unitCS= CS $ λ() () → ()

The ability to modify a field to one of a different type is also known as in the Haskell commu- nity as polymorphic record update, a feature that has proven desirable in the context of lens li- braries [O’Connor, 2012; Kmett, 2012].

We can also define a combinator sumCS for change structures on sum types, similarly to our earlier construction described in Sec. 11.6. This time, we choose to forbid changes across branches since they’re inefficient, though we could support them as well, if desired.

sumCS:: CS s1ds s2→ CS t1dt t2→ CS (Either s1t1) (Either ds dt) (Either s2t2) sumCS scs tcs= CS go


go (Left s1) (Left ds)= Left $ (⊞) scs s1ds go (Right t1) (Right dt)= Right $ (⊞) tcs t1dt go_ _ = error "Invalid changes"

Given two change structures from τ1to τ2, with respective change types δτ aand δτ b, we can also define a new change structure with change type Either δτ aδτ b, that allows using changes from either structure. We capture this construction through combinator mSumCS, having the following signature: mSumCS:: CS τ1δτ aτ2→ CS τ1δτ bτ2→ CS τ1(Either δτ a δτ b) τ2 mSumCS acs bcs= CS go where go t1(Left dt)= (⊞) acs t1dt go t1(Right dt)= (⊞) bcs t1dt

This construction is possible for non-polymorphic change structures; we only need change structures to be first-class (instead of a type class) to be able to internalize this construction in Haskell.

Using combinator lInsCS we can describe updates going from type τ1to type (σ, τ2), assuming a change structure from τ1to τ2: that is, we can prepend a value of type σ to our data while we modify it. Similarly, combinator lRemCS allows removing values:

170 Chapter 18. Towards differentiation for System F lInsCS:: CS t1dt t2→ CS t1(s, dt) (s, t2)

lInsCS tcs= CS $ λt1(s, dt) → (s, (⊞) tcs t1dt) lRemCS:: CS t1dt (s, t2) → CS t1dt t2 lRemCS tcs= CS $ λt1dt → snd$ (⊞) tcs t1dt

We can also transform change structures given suitable conversion functions. lIsoCS:: (t1→ s1) → CS s1dt t2→ CS t1dt t2

mIsoCS:: (dt → ds) → CS t1ds t2→ CS t1dt t2 rIsoCS :: (s2→ t2) → CS t1dt s2→ CS t1dt t2

isoCS:: (t1→ s1) → (dt → ds) → (s2→ t2) → CS s1ds s2→ CS t1dt t2

To do so, we must only compose ⊞ with the given conversion functions according to the types. Combinator isoCS arises by simply combining the other ones:

lIsoCS f tcs = CS $ λs1dt → (⊞) tcs (f s1) dt mIsoCS g tcs = CS $ λt1ds → (⊞) tcs t1(g ds) rIsoCS h tcs = CS $ λt1dt → h$ (⊞) tcs t1dt isoCS f g h scs= lIsoCS f $ mIsoCS g $ rIsoCS h scs

With a bit of datatype-generic programming infrastructure, we can reobtain only using combi- nators the change structure for lists shown in Sec. 11.3.3, which allows modifying list elements. The core definition is the following one:

listMuChangeCS:: CS a1da a2→ CS (Listµ a1) (Listµ da) (Listµ a2) listMuChangeCS acs= go where

go= isoCS unRollL unRollL rollL $ sumCS unitCS$ prodCS acs go The needed infrastructure appears in Fig. 18.1.

Section summary We have defined polymorphic change structures and shown they admit a rich combinator language. Now, we return to using these change structures for differentiating λ→and System F.

In document Optimizing and Incrementalizing Higher-order Collection Queries by AST Transformation (Page 186-188)