使用模板 Haskell 时键入同义词 "not in scope"

Type synonyms "not in scope" when using Template Haskell

我在使用模板 Haskell 时收到有关数据类型 "not in scope" 的奇怪错误。

这是我的 Main.hs 文件:

{-# LANGUAGE TemplateHaskell #-}

module Main where

import Control.Lens
import Data.Aeson
import Data.Aeson.TH

type Foo = Bar

data Baz = Baz
$(deriveJSON defaultOptions ''Baz)
-- $(makeLenses ''Baz)

data Bar = Bar

main :: IO ()
main = print "hello"

尝试编译时,出现以下错误:

test-0.1.0.0: configure
Configuring test-0.1.0.0...
test-0.1.0.0: build
Building test-0.1.0.0...
Preprocessing executable 'test' for test-0.1.0.0...
[1 of 1] Compiling Main             ( Main.hs, .stack-work/dist/x86_64-linux/Cabal-1.22.2.0/build/test/test-tmp/Main.o )

Main.hs:9:12:
    Not in scope: type constructor or class ‘Bar’

--  While building package test-0.1.0.0 using:
      /usr/bin/runhaskell -package=Cabal-1.22.2.0 -clear-package-db -global-package-db -package-db=/home/illabout/.stack/snapshots/x86_64-linux/nightly-2015-06-17/7.10.1/pkgdb/ /tmp/stack1699/Setup.hs --builddir=.stack-work/dist/x86_64-linux/Cabal-1.22.2.0/ build
    Process exited with code: ExitFailure 1

无论我使用deriveJSON还是makeLenses都会出现这个错误。

如果我将 type Foo = Bar 行向下移动到使用模板 Haskell 之后,文件可以正常编译。

导致此错误的原因是什么?

这里有一个 .cabal 文件和 stack.yaml 文件来编译它:

test.cabal:

name:                test
version:             0.1.0.0
build-type:          Simple
cabal-version:       >=1.10

executable test
  main-is:             Main.hs
  build-depends:       base >=4.8 && <4.9
                     , aeson >= 0.8 && < 0.9
                     , lens >= 4 && < 5
  default-language:    Haskell2010

stack.yaml:

flags: {}
packages:
- '.'
extra-deps: []
resolver: nightly-2015-06-17

这是使用 ghc-7.10.1。

data Bar = ... 声明移到模板 Haskell 之前,它将起作用:

type Foo = Bar

data Baz = Baz
data Bar = Bar

$(deriveJSON defaultOptions ''Baz)
$(makeLenses ''Baz)

这是由于使用模板 Haskell 时 声明组 引起的。以下是 GHC Manual 的摘录:

Top-level declaration splices break up a source file into delcaration groups. A declaration group is the group of declarations created by a top-level declaration splice, plus those following it, down to but not including the next top-level declaration splice. The first declaration group in a module includes all top-level definitions down to but not including the first top-level declaration splice.

Each declaration group is mutually recursive only within the group. Declaration groups can refer to definitions within previous groups, but not later ones.

在我的原始代码中,创建了两个声明组:

-- This is the start of the first declaration group.

type Foo = Bar

data Baz = Baz
$(deriveJSON defaultOptions ''Baz)

-- This is the start of the second declaration group.

data Bar = Bar

第一个声明组看不到 Bar,这是导致此错误的原因。