8

モジュールに次のコードがあります。

{-# LANGUAGE TemplateHaskell #-}

module Alpha where

import Language.Haskell.TH
import Data.List

data Alpha = Alpha { name :: String, value :: Int } deriving (Show)
findName n = find ((== n) . name)

findx obj = sequence [valD pat bod []]
    where
        nam = name obj
        pat = varP (mkName $ "find" ++ nam)
        bod = normalB [| findName nam |]

そして、メインファイルに次のものがあります。

{-# LANGUAGE TemplateHaskell #-}

import Alpha

one   = Alpha "One" 1
two   = Alpha "Two" 2
three = Alpha "Three" 3
xs    = [one, two , three]

findOne = findName "One"
findTwo = findName "Two"

$(findx three) -- This Fails
$(findx (Alpha "Four" 4)) -- This Works

main = putStrLn "Done"

$(findx three)に作成してもらいたいのですがfindThree = findName "Three"。しかし、代わりに、次のエラーが発生します。

GHC stage restriction: `three'
  is used in a top-level splice or annotation,
  and must be imported, not defined locally
In the first argument of `findx', namely `three'
In the expression: findx three

どうすればこれを克服できますか?onetwoなどを別のファイルで定義する必要はありません。

2番目の質問は、なぜ$(findx (Alpha "Four" 4))問題なく動作するのですか?

4

1 に答える 1

8

私自身は Template Haskell についてあまり詳しくありませんが、私の限られた理解に基づくと、 GHC が をコンパイルしようとしているときに、 のすべてのコンポーネント部分がすでに完全に定義されてthreeいるにもかかわらず、何らかの意味で「まだ定義されている」という問題があるということです。$(findx three)$(findx (Alpha "Four" 4))

基本的な問題は、同じモジュール内のすべての定義が相互の意味に影響を与えることです。これは、型推論と相互再帰によるものです。定義x = []は、コンテキストに応じて、さまざまなことを意味する可能性があります。xのリストInt、またはのリスト、またはその他のものにバインドできIO ()ます。GHC はモジュール全体を処理して、それが何を意味するか (または実際にはエラーであるか) を正確に把握する必要がある場合があります。

Template Haskell がコンパイル中のモジュールに発行するコードは、その分析で考慮する必要があります。つまり、テンプレート Haskell コードは、GHC がモジュール内の定義の意味を理解する前に実行する必要があり、論理的にはそれらのいずれも使用できません。

他のモジュールOTOHからインポートされたものは、GHCがそのモジュールをコンパイルしたときにすでに完全にチェックされています。このモジュールをコンパイルすることによって、それらについて学ぶ必要がある情報はこれ以上ありません。したがって、このモジュールのコードをコンパイルする前に、これらにアクセスして使用できます。

それについて考える別の方法:多分three実際にはタイプではないはずAlphaです。多分それはタイプミスで、コンストラクターはAlphz. 通常、GHC はモジュール内の他のすべてのコードをコンパイルしてthree、不整合が発生するかどうかを確認することによって、この種のエラーを検出します。しかし、そのコードが によってのみ発行されるものを使用している場合、または使用されている場合はどうなる$(findx three)でしょうか? 実行するまではどんなコードになるかさえわかりませんが、three正しく入力されているかどうかという問題は、実行してみるまで解決できません。

もちろん、場合によっては、この制限を少し解除することも可能です (それが簡単か実用的かはわかりません)。がインポートされた場合、または「早期に定義された」(そしておそらく明示的な型シグネチャを持っている)他のもののみを使用する場合、GHCに何かを「早期に定義された」と見なさせることができるかもしれません。おそらく、THコードを実行せずにモジュールのコンパイルを試みることができ、エラーが発生する前に完全にタイプチェックできれば、それをthreeTHコードにフィードしてからすべてを再コンパイルできます。マイナス面 (関連する作業以外) は、テンプレート Haskell に渡すことができるものに正確な制限が何であるかを述べるのがはるかに複雑になることです。

于 2013-05-04T00:34:29.433 に答える