2019-05-14 01:59:43 8 Comments

I'm reading through Some Tricks for List Manipulation, and it contains the following:

`zipRev xs ys = foldr f id xs snd (ys,[]) where f x k c = k (\((y:ys),r) -> c (ys,(x,y):r))`

What we can see here is that we have two continuations stacked on top of each other. When this happens, they can often “cancel out”, like so:

`zipRev xs ys = snd (foldr f (ys,[]) xs) where f x (y:ys,r) = (ys,(x,y):r)`

I don't understand how you "cancel out" stacked continuations to get from the top code block to the bottom one. What pattern do you look for to make this transformation, and why does it work?

### Related Questions

#### Sponsored Content

#### 3 Answered Questions

### [SOLVED] What optimizations can GHC be expected to perform reliably?

**2012-09-29 14:59:16****glaebhoerl****8436**View**176**Score**3**Answer- Tags: optimization haskell ghc

#### 13 Answered Questions

### [SOLVED] How can a time function exist in functional programming?

**2011-09-01 08:26:52****Nawaz****51535**View**618**Score**13**Answer- Tags: scala haskell f# functional-programming clean-language

#### 6 Answered Questions

### [SOLVED] What are Scala continuations and why use them?

**2009-10-03 05:54:37****Dave****21931**View**84**Score**6**Answer- Tags: scala scala-2.8 continuations delimited-continuations

#### 2 Answered Questions

### [SOLVED] Haskell foldl implementation with foldr

**2016-05-09 15:08:15****zaig****335**View**2**Score**2**Answer- Tags: haskell

#### 1 Answered Questions

### [SOLVED] Can switching in-and-out PyFrameObjects be a good implementation of continuations?

**2014-11-07 17:30:33****Noob Saibot****117**View**6**Score**1**Answer- Tags: python c python-c-api coroutine continuations

#### 1 Answered Questions

### [SOLVED] Desugaring rule for case expression within a do block.

**2014-11-01 14:52:03****jhegedus****63**View**1**Score**1**Answer- Tags: haskell

#### 0 Answered Questions

### How do continuations work in complex applications, like a web server or GUI app?

**2014-07-08 21:06:33****Bill****68**View**0**Score**0**Answer- Tags: scheme continuations

#### 1 Answered Questions

### [SOLVED] Scala continuations: many shifts in sequence

**2012-02-28 17:46:04****Jeremy****619**View**6**Score**1**Answer- Tags: scala continuations continuation-passing

#### 3 Answered Questions

### [SOLVED] how can I implement this monad transformer with a continuation?

**2011-12-04 22:16:20****gatoatigrado****378**View**10**Score**3**Answer- Tags: haskell monads continuations monad-transformers

#### 1 Answered Questions

### [SOLVED] Why am I getting "Non-exhaustive patterns in function..." when I invoke my Haskell substring function?

**2010-09-26 19:08:29****Daryl Spitzer****6563**View**10**Score**1**Answer- Tags: haskell non-exhaustive-patterns

## 5 comments

## @duplode 2019-05-14 21:32:26

Let's begin with a few cosmetic adjustments:

`f`

feeds a continuation (`c . g x`

) to another function (`k`

, a "double continuation", as user11228628 puts it).While we might reasonably expect that repeated usage of

`f`

as the fold proceeds will somehow compose the`g x`

endomorphisms made out of the elements of the list, the order in which the endomorphisms are composed might not be immediately obvious, so we'd better walk through a few fold steps to be sure:`ka`

, the initial value passed to foldr, is`id`

, which makes things quite a bit simpler:Since all we do with the

`c`

argument passed to`foldr f id xs`

is post-composing it with the endomorphisms, we might as well factor it out of the fold:Note how we have gone from

`c . g x`

to`g x . e`

. That can arguably be described as a collateral effect of the CPS trickery in the original implementation.The final step is noticing how

`h x e = g x . e`

corresponds exactly to what we would do to implement`foldr`

in terms of`foldMap`

for the`Endo`

monoid. Or, to put it more explicitly:That finally leads us to what we were looking for:

## @Will Ness 2019-05-14 12:02:57

Let's try to understand this code from an elementary point of view. What does it even do, one wonders?

Here we used lambda lifting to recover the

`g`

combinator.So then because

`f x k = k . g x`

were`k`

goesto the leftof`x`

, the input list is translated into a reversed chain of compositions,and thus, it just does what a left fold would do,

So we went to the deep end of the

`xs`

list, and then we come back consuming the`ys`

list left-to-right (i.e. top-down) on our way back right-to-left on the`xs`

list (i.e. bottom-up). This is straightforwardly coded as a right fold with strict reducer, so the flow is indeed right-to-left on the`xs`

. The bottom-most action (`snd`

) in the chain is done last, so in the new code it becomes the topmost (still done last):`g x c`

was used as a continuation in the original code, with`c`

as a second-tier continuation; but it's actually all just been a regular fold from the right, all along.So indeed it zips the reversed first list with the second. It's also unsafe; it misses a clause:

(update:)The answers by duplode (and Joseph Sible) do the lambda lifting a bit differently, in a way which is better suited to the task. It goes like this:so then

Simple. :) Flipping twice is no flipping at all.

## @Will Ness 2019-05-14 12:58:08

btw I used similar technique in one of my recent answers:

`rltake`

here builds a chain of compositions first (just in a different direction), then applies it further; very much like`zipRev`

here does.## @dfeuer 2019-05-14 13:37:25

See my comment on the question.

## @Will Ness 2019-05-14 14:13:48

what does

`fr`

stand for?## @dfeuer 2019-05-14 14:33:38

It stands for "final result".

## @Will Ness 2019-05-14 15:12:11

to me,

`z`

is more informative, invokes "zip", so adds something. "fr" adds nothing, just restates the given, and leaves me searching for the answer, futilely, because I search for the added meaning, and there's none. So that's why I was stumped. :) Names are important. :)## @Joseph Sible 2019-05-14 22:57:34

user11228628's answer led me to understanding. Here's a few insights I had while reading it, and some step-by-step transformations.

## Insights

`\k c -> k (c . f)`

(or if you love unreadable pointfree,`(. (. f))`

) for any`f`

(note that the`f`

isn't a parameter to the lambda).`obfuscate`

is their definition of`fmap`

.`foldr`

works for any function that could be a valid`fmap`

.## Full transformation from the first code block to the second

Pull

`c`

out of the lambdaSubstitute

`obfuscate`

for its definitionPull

`obfuscate`

out of the lambdaPull

`obfuscate`

out of`f`

Since

`obfuscate`

follows the Functor laws, we can pull it out of`foldr`

Inline

`obfuscate`

Beta-reduce

Simplify

## Justification for pulling functions that are valid

`fmap`

s out of`foldr`

Expand the

`foldr`

Inline the inner

`.`

sApply the Functor laws

Eta-expand the section in parentheses

Write the lambda body in terms of

`foldr`

Write the lambda body in terms of

`flip`

## @user11228628 2019-05-14 04:54:24

A function

`f :: a -> b`

can be "disguised" inside double continuations as a function`f' :: ((a -> r1) -> r2) -> ((b -> r1) -> r2)`

.`obfuscate`

has the nice property that it preserves function composition and identity: you can prove that`obfuscate f . obfuscate g === obfuscate (f . g)`

and that`obfuscate id === id`

in a few steps. That means that you can frequently use this transformation to untangle double-continuation computations that compose`obfuscate`

d functions together by factoring the`obfuscate`

out of the composition. This question is an example of such an untangling.The

`f`

in the top code block is the`obfuscate`

d version of the`f`

in the bottom block (more precisely, top`f x`

is the`obfuscate`

d version of bottom`f x`

). You can see this by noticing how top`f`

applies the outer continuation to a function that transforms its input and then applies the whole thing to the inner continuation, just like in the body of`obfuscate`

.So we can start to untangle

`zipRev`

:Since the action of

`foldr`

here is to compose a bunch of`obfuscate`

d functions with each other (and apply it all to`id`

, which we can leave on the right), we can factor the`obfuscate`

to the outside of the whole fold:Now apply the definition of

`obfuscate`

and simplify:QED!

## @duplode 2019-05-14 05:08:32

A footnote: for an immediate proof that

`obfuscate`

respects function composition, we just have to notice it is`fmap`

for the double continuation type.## @Anders Kaseorg 2019-05-14 05:09:57

Given a function

we can lift it to a function on continuations, switching the order:

This transformation is a contravariant functor, which is to say that it interacts with function composition by switching its order:

We can lift the lifted function again in the same way to a function on stacked continuations, with the order switched back:

Stacking two contravariant functors gives us a (covariant) functor:

This is exactly the transformation being reversed in your example, with

`g = \(y:ys, r) -> (ys, (x, y):r)`

. This`g`

is an endomorphism (`a₁ = a₂`

), and the`foldr`

is composing together a bunch of copies of it with various`x`

. What we’re doing is replacing the composition of double-lifted functions with the double-lift of the composition of the functions, which is just an inductive application of the functor laws: