[GHC] #16176: Let-insertion for template haskell

GHC ghc-devs at haskell.org
Mon Jan 14 10:56:32 UTC 2019


#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


More information about the ghc-tickets mailing list