Let-insertion for template haskell
When using Template Haskell to generate programs it's very easy to end up with a lot of duplication as splices are naively spliced in place.
foo x = [|| $$x + $$x ||]
Will generate a program which completely duplicates its argument. In this case I can manually remove the duplicate by inserting a let.
foo x = [|| let x' = $$x in x' + x' ||]
Not too bad but a bit annoying to have to do manually.
When constructing bigger programs however this process becomes tedious or impossible to do correctly by hand.
foo :: (Q (TExp (Bool)) -> Q (TExp Int)) -> Q (TExp Int) foo xf = [|| (let x = True in $$(xf [|| x ||])) + (let x = False in $$(xf [|| x ||]) ||]
Now if I pass a constant function to
foo, the resulting code won't mention
x so it could be floated out. However, there's not way I can tell that without running
xf so I can't perform the same transformation as I did for the earlier program and manually insert a let. In the case of splicing in fully static data you really want it to float to the top-level and turn into a CAF.
The proposal of this ticket is to implement something like the mechanism for let-insertion in metaocaml.
We add two new primitives:
genlet :: Q (TExp a) -> Q (TExp a) let_locus :: Q (TExp a) -> Q (TExp a)
genlet marks a code value that we want to float.
let_locus marks places where we want to insert a let. When we evaluate the code fragment and encounter a
genlet call, whatever the argument evaluates to is floated as far upwards as possible and inserted at the position of one of the loci.
sqr :: Code Int -> Code Int sqr c = [|| $$c + $$c ||] sqr_let :: Code Int -> Code Int sqr_let c = let_locus (sqr (genlet c))
sqr [|| 1 ||] will result in
1 + 1 but
sqr_let [|| c ||] will equal
let x = 1 in x + x ||].
It's important to do this earlier rather than later as a lot of duplication can take place which the simplifier does not like.