
There is a check in `RnSplice` which errors on the following program with nested brackets. ``` prog = [| [| True |] |] T.hs:4:11: error: • Template Haskell brackets cannot be nested (without intervening splices) • In the Template Haskell quotation [| True |] In the Template Haskell quotation [| [| True |] |] | 4 | prog = [| [| True |] |] | ^^^^^^^^^^ ``` As far as I can see the check was added in 2013 in this commit, https://github.com/ghc/ghc/commit/d0d47ba76f8f0501cf3c4966bc83966ab38cac27#d... But there is no note, no tests and no comment about why it was added. I removed the check and added a `BracketE` constructor to the template-haskell AST and the code compiles fine. I can also construct a program which needs to be spliced twice and this also works fine. ``` func Add = [| (+) |] func Mul = [| (*) |] f1 "+" = [| Add |] f1 "*" = [| Mul |] comb s = [| func $(f1 s) |] ``` ``` res = $($(comb "*")) ``` So it seems the restriction is quite arbitrary but I was wondering if I was missing some limitation which meant this check was added. I would not be surprised if something more complicated goes wrong with splicing. Cheers, Matt

I think Geoff was primarily concerned with typed Template Haskell, not the untyped variety. I, too, have wondered if there was a technical reason behind this restriction, or if merely it was assumed that nested brackets were not worthwhile. One question: how would staging work between nesting levels of brackets? Richard
On Jan 24, 2019, at 12:42 PM, Ben Gamari
wrote: Matthew Pickering
writes: There is a check in `RnSplice` which errors on the following program with nested brackets.
It might be good to explicitly include Geoff Mainland in this thread. I'm not sure he'll see it otherwise.
Cheers,
- Ben
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

I don't think that cross stage persistence will work as it is
currently implemented which is probably why the check exists.
1. The normal case
foo x = [| x |] ===>
foo x = [| $(lift x) |]
2. x is defined at stage 0, and used at stage 2.
One option is:
foo x = [| [| x |] |] ===>
foo x = [| [| $($(lift (lift x))) |] |]
or
foo x = [| [| x |] |] ===>
foo x = [| let x' = $(lift x) in [| $(lift [| x' |]) |]
We need to think a bit how to `lift` something of type `Q Exp` because
of the `Q` monad. Lifting an `Exp` seems trivial as it's a normal ADT
(I tested and this works after deriving 40 instances).
You can define `lift2` which lifts an expression twice as follows.
```
lift2 :: Lift a => a -> Q Exp
lift2 a = lift a >>= \e -> [| return $ $(lift e) |]
```
3. x is defined at stage 1 and used in stage 2
foo = [| \x -> [| x |] |] ===>
foo = [| \x -> [| $(lift x) |] |]
Desugared with a single call to `lift` like normal.
4. x is defined in stage 2 and used in stage 1
foo = [| [| \x -> $(x) |] |]
Rejected just like usual. `x` won't be bound when the splice is run.
It seems that with some suitable care that things will work out when
lifting across multiple levels but that is the point where care needs
to be taken.
Matt
On Thu, Jan 24, 2019 at 5:46 PM Richard Eisenberg
I think Geoff was primarily concerned with typed Template Haskell, not the untyped variety.
I, too, have wondered if there was a technical reason behind this restriction, or if merely it was assumed that nested brackets were not worthwhile.
One question: how would staging work between nesting levels of brackets?
Richard
On Jan 24, 2019, at 12:42 PM, Ben Gamari
wrote: Matthew Pickering
writes: There is a check in `RnSplice` which errors on the following program with nested brackets.
It might be good to explicitly include Geoff Mainland in this thread. I'm not sure he'll see it otherwise.
Cheers,
- Ben
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

Interesting. I don’t recall a specific reason why nested brackets were outlawed. I think it was just that we didn't think we needed them, so it seemed simplest not to have them. TH does not do runtime codegen, so there are really only two stages: compile time and run time.
Do you have a compelling use-case?
Simon
| -----Original Message-----
| From: ghc-devs

It's possible to have multiple compile time phases (as evidenced by
the possibility of multiple splices). Metaocaml supports k phases so
we should as well!
Fwiw, It is also possible to implement run-time code generation, see
https://github.com/mainland/th-new
http://johnlato.blogspot.com/2012/10/runtime-meta-programming-in-haskell.htm...
On Fri, Jan 25, 2019 at 12:49 PM Simon Peyton Jones
Interesting. I don’t recall a specific reason why nested brackets were outlawed. I think it was just that we didn't think we needed them, so it seemed simplest not to have them. TH does not do runtime codegen, so there are really only two stages: compile time and run time.
Do you have a compelling use-case?
Simon
| -----Original Message----- | From: ghc-devs
On Behalf Of Matthew | Pickering | Sent: 25 January 2019 11:57 | To: Richard Eisenberg | Cc: GHC developers | Subject: Re: Why are nested brackets disallowed? | | I don't think that cross stage persistence will work as it is currently | implemented which is probably why the check exists. | | 1. The normal case | | foo x = [| x |] ===> | foo x = [| $(lift x) |] | | 2. x is defined at stage 0, and used at stage 2. | | One option is: | foo x = [| [| x |] |] ===> | foo x = [| [| $($(lift (lift x))) |] |] or foo x = [| [| x |] |] ===> | foo x = [| let x' = $(lift x) in [| $(lift [| x' |]) |] | | We need to think a bit how to `lift` something of type `Q Exp` because of | the `Q` monad. Lifting an `Exp` seems trivial as it's a normal ADT (I | tested and this works after deriving 40 instances). | | You can define `lift2` which lifts an expression twice as follows. | | ``` | lift2 :: Lift a => a -> Q Exp | lift2 a = lift a >>= \e -> [| return $ $(lift e) |] ``` | | 3. x is defined at stage 1 and used in stage 2 | | foo = [| \x -> [| x |] |] ===> | foo = [| \x -> [| $(lift x) |] |] | | Desugared with a single call to `lift` like normal. | | 4. x is defined in stage 2 and used in stage 1 | | foo = [| [| \x -> $(x) |] |] | | Rejected just like usual. `x` won't be bound when the splice is run. | | It seems that with some suitable care that things will work out when | lifting across multiple levels but that is the point where care needs to | be taken. | | Matt | | | | On Thu, Jan 24, 2019 at 5:46 PM Richard Eisenberg | wrote: | > | > I think Geoff was primarily concerned with typed Template Haskell, not | the untyped variety. | > | > I, too, have wondered if there was a technical reason behind this | restriction, or if merely it was assumed that nested brackets were not | worthwhile. | > | > One question: how would staging work between nesting levels of | brackets? | > | > Richard | > | > > On Jan 24, 2019, at 12:42 PM, Ben Gamari | wrote: | > > | > > Matthew Pickering writes: | > > | > >> There is a check in `RnSplice` which errors on the following | > >> program with nested brackets. | > >> | > > It might be good to explicitly include Geoff Mainland in this thread. | > > I'm not sure he'll see it otherwise. | > > | > > Cheers, | > > | > > - Ben | > > | > > _______________________________________________ | > > ghc-devs mailing list | > > ghc-devs@haskell.org | > > https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fmai | > > l.haskell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-devs&data=02% | > > 7C01%7Csimonpj%40microsoft.com%7Cdf0aa539bb1041dca42308d682bc3d4b%7C | > > 72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636840142327169830&sd | > > ata=oIiA768uqGGGBJh7ogpymmPvuKBDLj%2BiqJCZpig6SPg%3D&reserved=0 | > | _______________________________________________ | ghc-devs mailing list | ghc-devs@haskell.org | https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fmail.has | kell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc- | devs&data=02%7C01%7Csimonpj%40microsoft.com%7Cdf0aa539bb1041dca42308d | 682bc3d4b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636840142327169830 | &sdata=oIiA768uqGGGBJh7ogpymmPvuKBDLj%2BiqJCZpig6SPg%3D&reserved= | 0

This typechecks: instance Lift a => Lift (Q a) where lift :: Q a -> Q Exp lift x = join (fmap lift x) And it looks correct. Do you agree? In general, it sounds like you've thought this through. To me, this doesn't rise to the need of a ghc-proposal; I would say to go for it. Richard
On Jan 25, 2019, at 6:56 AM, Matthew Pickering
wrote: I don't think that cross stage persistence will work as it is currently implemented which is probably why the check exists.
1. The normal case
foo x = [| x |] ===> foo x = [| $(lift x) |]
2. x is defined at stage 0, and used at stage 2.
One option is: foo x = [| [| x |] |] ===> foo x = [| [| $($(lift (lift x))) |] |] or foo x = [| [| x |] |] ===> foo x = [| let x' = $(lift x) in [| $(lift [| x' |]) |]
We need to think a bit how to `lift` something of type `Q Exp` because of the `Q` monad. Lifting an `Exp` seems trivial as it's a normal ADT (I tested and this works after deriving 40 instances).
You can define `lift2` which lifts an expression twice as follows.
``` lift2 :: Lift a => a -> Q Exp lift2 a = lift a >>= \e -> [| return $ $(lift e) |] ```
3. x is defined at stage 1 and used in stage 2
foo = [| \x -> [| x |] |] ===> foo = [| \x -> [| $(lift x) |] |]
Desugared with a single call to `lift` like normal.
4. x is defined in stage 2 and used in stage 1
foo = [| [| \x -> $(x) |] |]
Rejected just like usual. `x` won't be bound when the splice is run.
It seems that with some suitable care that things will work out when lifting across multiple levels but that is the point where care needs to be taken.
Matt
On Thu, Jan 24, 2019 at 5:46 PM Richard Eisenberg
wrote: I think Geoff was primarily concerned with typed Template Haskell, not the untyped variety.
I, too, have wondered if there was a technical reason behind this restriction, or if merely it was assumed that nested brackets were not worthwhile.
One question: how would staging work between nesting levels of brackets?
Richard
On Jan 24, 2019, at 12:42 PM, Ben Gamari
wrote: Matthew Pickering
writes: There is a check in `RnSplice` which errors on the following program with nested brackets.
It might be good to explicitly include Geoff Mainland in this thread. I'm not sure he'll see it otherwise.
Cheers,
- Ben
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

That definition typechecks but doesn't do what you intend I think.
If you `lift (lift Bool)` then result of splicing this is of type
`Exp` and not `Q Exp` as you intended so you can't splice it again.
That is why my definition is more complicated and involves `return`.
Cheers,
Matt
On Fri, Jan 25, 2019 at 12:50 PM Richard Eisenberg
This typechecks:
instance Lift a => Lift (Q a) where lift :: Q a -> Q Exp lift x = join (fmap lift x)
And it looks correct. Do you agree?
In general, it sounds like you've thought this through. To me, this doesn't rise to the need of a ghc-proposal; I would say to go for it.
Richard
On Jan 25, 2019, at 6:56 AM, Matthew Pickering
wrote: I don't think that cross stage persistence will work as it is currently implemented which is probably why the check exists.
1. The normal case
foo x = [| x |] ===> foo x = [| $(lift x) |]
2. x is defined at stage 0, and used at stage 2.
One option is: foo x = [| [| x |] |] ===> foo x = [| [| $($(lift (lift x))) |] |] or foo x = [| [| x |] |] ===> foo x = [| let x' = $(lift x) in [| $(lift [| x' |]) |]
We need to think a bit how to `lift` something of type `Q Exp` because of the `Q` monad. Lifting an `Exp` seems trivial as it's a normal ADT (I tested and this works after deriving 40 instances).
You can define `lift2` which lifts an expression twice as follows.
``` lift2 :: Lift a => a -> Q Exp lift2 a = lift a >>= \e -> [| return $ $(lift e) |] ```
3. x is defined at stage 1 and used in stage 2
foo = [| \x -> [| x |] |] ===> foo = [| \x -> [| $(lift x) |] |]
Desugared with a single call to `lift` like normal.
4. x is defined in stage 2 and used in stage 1
foo = [| [| \x -> $(x) |] |]
Rejected just like usual. `x` won't be bound when the splice is run.
It seems that with some suitable care that things will work out when lifting across multiple levels but that is the point where care needs to be taken.
Matt
On Thu, Jan 24, 2019 at 5:46 PM Richard Eisenberg
wrote: I think Geoff was primarily concerned with typed Template Haskell, not the untyped variety.
I, too, have wondered if there was a technical reason behind this restriction, or if merely it was assumed that nested brackets were not worthwhile.
One question: how would staging work between nesting levels of brackets?
Richard
On Jan 24, 2019, at 12:42 PM, Ben Gamari
wrote: Matthew Pickering
writes: There is a check in `RnSplice` which errors on the following program with nested brackets.
It might be good to explicitly include Geoff Mainland in this thread. I'm not sure he'll see it otherwise.
Cheers,
- Ben
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

I put up a patch which removes this restriction (for untyped expressions).
https://gitlab.haskell.org/ghc/ghc/merge_requests/259
On Thu, Jan 24, 2019 at 11:46 AM Matthew Pickering
There is a check in `RnSplice` which errors on the following program with nested brackets.
``` prog = [| [| True |] |]
T.hs:4:11: error: • Template Haskell brackets cannot be nested (without intervening splices) • In the Template Haskell quotation [| True |] In the Template Haskell quotation [| [| True |] |] | 4 | prog = [| [| True |] |] | ^^^^^^^^^^
```
As far as I can see the check was added in 2013 in this commit, https://github.com/ghc/ghc/commit/d0d47ba76f8f0501cf3c4966bc83966ab38cac27#d...
But there is no note, no tests and no comment about why it was added.
I removed the check and added a `BracketE` constructor to the template-haskell AST and the code compiles fine.
I can also construct a program which needs to be spliced twice and this also works fine.
``` func Add = [| (+) |] func Mul = [| (*) |]
f1 "+" = [| Add |] f1 "*" = [| Mul |]
comb s = [| func $(f1 s) |] ``` ``` res = $($(comb "*")) ```
So it seems the restriction is quite arbitrary but I was wondering if I was missing some limitation which meant this check was added. I would not be surprised if something more complicated goes wrong with splicing.
Cheers,
Matt
participants (4)
-
Ben Gamari
-
Matthew Pickering
-
Richard Eisenberg
-
Simon Peyton Jones