13

このコードは正常にコンパイルされています:

data None = None { _f :: Int }
type Simpl = Env

type Env = Int

ただし、次のコードでエラーが発生しました。

{-# LANGUAGE TemplateHaskell #-}
import Control.Lens

data None = None { _f :: Int }

type Simpl = Env

makeLenses ''None

type Env = Int

エラー:

Not in scope: type constructor or class `Env'

makeLenses ''None型宣言の間に 1 行追加しただけです。
これは、TemplateHaskell コードが型コンストラクターのスコープを変更できることを意味しますか?

この問題の詳細 (またはこの問題を回避する方法) を知っている人はいますか?

4

1 に答える 1

16

次のようにコードを並べ替えると、機能します。

{-# LANGUAGE TemplateHaskell #-}
import Control.Lens

data None = None { _f :: Int }

type Simpl = Env

type Env = Int

makeLenses ''None

Template Haskell splices を使用して新しいトップレベルの宣言をコードに追加すると、コードmakeLenses内の宣言の順序が突然重要になります!

その理由は、通常、Haskell プログラムをコンパイルするには、最初にすべてのトップレベルの宣言を収集し、それらを内部で並べ替えて依存関係の順序に並べ替え、次にそれらを 1 つずつ (または相互に再帰的な宣言の場合はグループごとに) コンパイルする必要があるためです。

任意のコードを実行することによって新しい宣言が導入されます。GHC はどの宣言makeLensesを実行する必要があるのか​​ わからないためです。そのため、ファイル全体を依存関係の順序に並べることはできず、少なくともスプライスの前または後に宣言を行うかどうかを決定するために、あきらめてユーザーが自分でそれを行うことを期待しています。

これを説明している唯一のオンライン リファレンスは、元の Template Haskell paperのセクション 7.2 で、アルゴリズムは次のようになっています。

  • 宣言を次のようにグループ化します。
[d1,...,da]
splice ea
[da+2,...,db]
splice eb
...
splice ez
[dz+2,...,dN]

ここで、 splice 宣言は明示的に示されたものだけであるため、各 group[d1,...,da]などはすべて通常の Haskell 宣言になります。

  • 最初のグループに対して、従来の依存関係分析を実行し、続いて型チェックを実行します。その自由変数はすべてスコープ内にある必要があります。

したがって、ここでの問題は、スプライスの前の宣言の最初のグループが、スプライスの後の 2 番目のグループとは別に処理され、 の定義が見えないことですEnv

私の一般的な経験則は、可能であればこのようなスプライスをファイルの最後に置くことですが、これが常に機能するという保証はないと思います.

于 2014-01-02T05:44:37.580 に答える