## 讀檔與蒐集

用了 `readFile` ，就脫離不了 `IO` 。於是只好把希望得到的，放在 `IO` 裡面：

```Haskell
readModule :: String -> IO (Maybe (Module SrcSpanInfo))
```

一開始先來個 `do` ，躲到 `IO` 裡面去：

```Haskell
readModule mod = do
  content <- readFile $ modName ++ ".hs"
  let res = parseModule content
  return $ case res of
    ParseOk mod -> Just mod
    ParseFailed _ msg -> Nothing
```

蒐集 AST 中 import 的 `Module` 時，要用到 `readModule` 的結果， `IO` 也跟著出現了：

```Haskell
collectModule :: Module SrcSpanInfo -> IO (M.Map String Bool)
```

這件事一直讓我想起 [What Color is Your Function][function-color] 。對了，別忘記 import `Map` ：

```Haskell
import Language.Haskell.Exts.Annotated
import qualified Data.Map.Strict as M
```

並在 `my-project.cabal` 中補上 `containers` ：

```
executable my-project-exe
  hs-source-dirs:      app
  main-is:             Main.hs
  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
  build-depends:       base
                     , my-project
                     , containers
                     , haskell-src-exts
  default-language:    Haskell2010
```

先試著蒐集不重複的 `Module` 就好，不遞迴讀檔：

```Haskell
collectModule mod = do
  retrun $ case mod of
    Module _ mModuleHead _ imports _ -> go M.empty imports where
      modName = case mModuleHead of
        Just (ModuleHead _ (ModuleName _ name) _ _) -> name
        Nothing -> "Main"
      go acc [] = acc
      go acc (m : ms) =
        let
          (Module _ name) = importModule m
        in
          case M.member name acc of
            False -> go (M.insert name True acc) ms
            True -> go acc ms
    _ -> M.empty
```

回到 `main` ，可以這樣印出單一個檔案中 import 的東西：

```Haskell
main = do
  mMod <- readModule "A"
  res <- case mMod of
    Just mod -> collectModule mod
    Nothing -> return M.empty
  putStrLn $ show res
```

[function-color]: http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
