Over the last few posts, we explored foldr/build
fusion
in the context of staging and partial evaluation. We figured out that:
Using these insights, we encoded lists as FoldLeft
, its CPS-encoding. By staging
FoldLeft
we were able to achieve fusion.
We then added multiple producer functions to the abstraction, namely partition
and groupBy
. To keep everything
on a single pipeline we had to add extra boxes. But then, these boxes could also
be CPS-encoded, and we were able to get rid of them as well.
Over the last few weeks, I packed all of this up into a two column PDF. Note: it’s under submission. During the process, we (along with Sandro) figured out better (and even safer) ways to implement the library. In this shortish post, I mention these improvements. If you read through the PDF you might not notice every one of them, but if you have been following the past posts (and have been doing some of the exercises), it is hopefully useful.
Initially, we gave the following signature to FoldLeft
:
With this signature, we had to signal very early in the pipeline what the eventual
data structure we folded into would be. In many of our examples, we turned S
to List[Int]
. Because the fold is applied only at the end, it would make more
sense to specify S
only then. With the following type signature for FoldLeft
we get exactly that:
Now FoldLeft
is parametrized over A
, the type of elements it folds over. Its
apply
function is paremtrized over the eventual fold type S
. Thanks to this
modification we can write fold pipelines without having to worry about the final
type until the end.
Recall that we had to define a specific conditional combinator over EitherCPS
:
This was because EitherCPS
is a code generator, and we needed to push the evaluation
of the conditional expression to code generation. Our previous implementation was
naive however:
The problem with this implementation is that it does not scale with respect to
code generation, especially if nested EitherCPS
instances are used.
Exercise: What does the following code generate :
We get the following evaluation trail:
(if (c1) { if (c2) t else e } else { if (c2) t2 else e2 }).apply(lf, rf)
↪ if (c1) { if (c2) t else e }.apply(lf, rf) else { if (c2) t2 else e2 }.apply(lf, rf)
↪ if (c1) {
if (c2) t.apply(lf, rf) else e.apply(lf, rf)
} else {
if (c2) t2.apply(lf, rf) else e2.apply(lf, rf)
}
The apply
function is inlined 4 times!! This is not a problem specific to EitherCPS
,
but applies to conditional expressions and code generation in general. LMS provides
a generic way to solve the problem, with the use of so called
FatIfThenElseExp
.
In our case, we can also provide an alternate implementation for conditional
:
The idea is to call a continuation which stores temporarily stores results. In the conditional expression we just pass these temporarily stored values.
That’s it! In this fairly short post we improved the library look and feel, and
the code generation for the staged FoldLeft
API. Code generation for conditional
expressions can get tricky, it is important to keep the above problem in mind!
The code now lives in its own little repo here. Follow the instructions on the README if you want to run the code!
No references today, sorry!
Manohar Jonnalagedda 17 April 2015