
#16176: Let-insertion for template haskell -------------------------------------+------------------------------------- Reporter: mpickering | Owner: (none) Type: feature | Status: new request | Priority: normal | Milestone: Component: Template | Version: 8.6.3 Haskell | Keywords: | Operating System: Unknown/Multiple TypedTemplateHaskell | Architecture: | Type of failure: None/Unknown Unknown/Multiple | Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- 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. For example {{{ 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. http://okmij.org/ftp/meta-programming/#let-insert 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. For example, {{{ sqr :: Code Int -> Code Int sqr c = [|| $$c + $$c ||] sqr_let :: Code Int -> Code Int sqr_let c = let_locus (sqr (genlet c)) }}} Splicing `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. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16176 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler