This code is compiled fine:
data None = None { _f :: Int }
type Simpl = Env
type Env = Int
However, I got an error with this code:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data None = None { _f :: Int }
type Simpl = Env
makeLenses ''None
type Env = Int
Error:
Not in scope: type constructor or class `Env'
I just added a single line makeLenses ''None
between type declarations.
This means TemplateHaskell code could change the scope of type constructor?
Does anyone know the detail about this issue(or how to avoid this problem)?
If you reorder your code as follows, it works:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data None = None { _f :: Int }
type Simpl = Env
type Env = Int
makeLenses ''None
When you use Template Haskell splices to add new top-level declarations to your code, as makeLenses
does, the order of declarations in your code suddenly matters!
The reason is that normally compiling a Haskell program involves first collecting all the top-level declarations and reordering them internally to put them in dependency order, and then compiling them one by one (or group by group for mutually recursive declarations).
With new declarations being introduced by running arbitrary code, because GHC doesn't know which declarations makeLenses
might need to run, and also it doesn't know which new declarations it will produce. So it can't put the whole file in dependency order and just sort of gives up and expects the user to do it themselves, at least for deciding whether declarations should go before or after the splice.
The only online reference I can find that explains this is in the original Template Haskell paper, section 7.2, where it says that the algorithm is:
- Group the declarations as follows:
[d1,...,da]
splice ea
[da+2,...,db]
splice eb
...
splice ez
[dz+2,...,dN]
where the only splice declarations are the ones indicated explicitly, so that each group
[d1,...,da]
, etc, are all ordinary Haskell declarations.
- Perform conventional dependency analysis, followed by type checking, on the first group. All its free variables should be in scope.
So the problem here is that the first group of declarations before the splice is being handled separately to the second group after the splice, and it can't see the definition of Env
.
My general rule of thumb is to put splices like this at the bottom of the file if possible, but I don't think it's guaranteed that this will always work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With