Haskell:模板Haskell和范围 [英] Haskell: Template Haskell and the scope

查看:96
本文介绍了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 可能需要运行,也不知道它会产生哪些新的声明。所以它不能把整个文件放在依赖顺序中,只是放弃并期望用户自己做,至少是决定声明应该在拼接之前还是之后。



我唯一可以找到的在线参考解释了这一点,在原始模板Haskell论文,第7.2节,它说这个算法是:



  • 将声明分组如下:



  [d1,...,da] 
拼接ea
[da + 2,...,db]
拼接eb
...
splice ez
[dz + 2,...,dN]




其中唯一的拼接声明是明确指出的拼接声明,因此每个组 [d1,...,da] 等都是普通的Haskell声明。




  • 执行con对第一组进行传统的依赖性分析,然后进行类型检查。所有的自由变量应该在范围内。


所以这里的问题是,在拼接之前将拼接单独处理到第二组之前的声明,并且它看不到 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:

  • 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.

这篇关于Haskell:模板Haskell和范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆