Unbound record/field names in Template Haskell Quotes
In a Template Haskell expression quotation, unbound variables and constructors are allowed, and are represented using UnboundVarE
. However unbound names are not permitted in record construction (RecConE
) or update (RecUpdE
), and lead to scope errors in the quotation. This leads to strangely inconsistent behaviour:
GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
Prelude> :set -XTemplateHaskellQuotes
Prelude> import Language.Haskell.TH.Syntax as TH
Prelude TH> unQ [| foo |]
UnboundVarE foo
Prelude TH> unQ [| Foo |]
UnboundVarE Foo
Prelude TH> unQ [| Foo {} |]
<interactive>:5:8: error:
• Not in scope: data constructor ‘Foo’
• In the Template Haskell quotation [| Foo {} |]
Prelude TH> unQ [| Foo { foo = bar } |]
<interactive>:6:8: error:
• Not in scope: data constructor ‘Foo’
• In the Template Haskell quotation [| Foo {foo = bar} |]
<interactive>:6:14: error:
• Not in scope: ‘foo’
• In the Template Haskell quotation [| Foo {foo = bar} |]
Prelude TH> unQ [| Just { pi = bar } |]
RecConE GHC.Maybe.Just [(GHC.Float.pi,UnboundVarE bar)]
It seems odd that:
-
Foo
is accepted butFoo {}
is not. -
Foo { foo = bar }
leads to not-in-scope errors forFoo
andfoo
but notbar
. -
Just { pi = bar }
is accepted even though it is complete nonsense, becauseJust
is not a record constructor andpi
is not a field.
I'm not sure what the right behaviour is here. One possibility would be to drop UnboundVarE
and instead add a constructor to Name
for unbound names, which would allow us to represent arbitrary expressions involving out-of-scope names.
Thanks to @edsko for highlighting this. I've tested that this behaviour occurs with 8.6.5, 8.10.2 and a relatively recent HEAD.