本文介绍了Haskell:模板Haskell和范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
这段代码编译得很好:
data无=无{_f :: Int}
类型Simpl = Env
Env = Int
然而,代码:
{ - #LANGUAGE TemplateHaskell# - }
导入Control.Lens
数据None = None {_f :: Int}
类型Simpl = Env
makeLenses''None
类型Env = Int
错误:
不在范围中:类型构造函数或类`Env'
我只添加了一行 makeLenses''None
类型声明。
这意味着TemplateHaskell代码可以改变构造函数的类型范围吗?
有人知道关于这个问题的细节(或者如何避免这个问题)吗? 解决方案
如下所示:
{ - #LANGUAGE TemplateHaskell# - }
导入Control.Lens
数据None =无{_f :: Int}
类型Simpl = Env
类型Env = Int
makeLenses''None
当您使用模板Haskell拼接添加新代码中的顶级声明,如 makeLenses
所做的,代码中声明的顺序突然变得很重要!
原因在于通常编译Haskell程序首先需要收集所有顶级声明并在内部对它们进行重新排序以使它们依赖于顺序,然后逐个编译它们(或者按照相互递归声明分组编译)。 p>
通过运行任意代码引入新的声明,因为GHC不知道哪些声明 makeLenses
可能需要运行,也不知道它会产生哪些新的声明。所以它不能把整个文件放在依赖顺序中,只是放弃并期望用户自己做,至少是决定声明应该在拼接之前还是之后。
我唯一可以找到的在线参考解释了这一点,在,第7.2节,它说这个算法是:
[d1,...,da]
拼接ea
[da + 2,...,db]
拼接eb
...
splice ez
[dz + 2,...,dN]
所以这里的问题是,在拼接之前将拼接单独处理到第二组之前的声明,并且它看不到 Env
的定义。
我的一般经验法则是如果可能的话,将这样的拼接放在文件的底部,但我不认为这可以保证它始终有效。
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:
[d1,...,da]
splice ea
[da+2,...,db]
splice eb
...
splice ez
[dz+2,...,dN]
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.
这篇关于Haskell:模板Haskell和范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!