読者です 読者をやめる 読者になる 読者になる

いものやま。

雑多な知識の寄せ集め

GHCの使い方を調べてみた。(その4)

技術 Haskell

昨日はGHCiでのインポートについて説明した。

ただ、ロードとインポートの違いが分かりにくいので、今日はその補足。

ロードとインポートの違い

ロードとインポートの違いを一言で言い表すと、次のようになる:

  • ロード
    ファイルやパッケージからコードをメモリに読み込むこと。
  • インポート
    GHCiのスコープから関数などを見えるようにすること。

図にすると、次のようになる。

f:id:yamaimo0625:20160122122640p:plain

そして、インポートについては、アスタリスクつきのインポートとアスタリスクなしのインポートがあるけど、その違いは次のようになっている:

  • アスタリスクつきのインポート
    GHCiのスコープからモジュール内のすべてが見える。
  • アスタリスクなしのインポート(普通のインポート)
    GHCiのスコープからは、モジュールからエクスポートされたものだけが見える。

図にすると、次のようになる。

f:id:yamaimo0625:20160122123605p:plain

以下では、個別のケースについて、上記を確認してみる。

GHCiからロードを行ったときの挙動

最初に、Main.hsをロードするときの挙動を追ってみる。

Main.hsは以下のように、パッケージに含まれるSystem.EnvironmentとControl.Monad、それと、ファイルのFibo.hsをインポートしている。

f:id:yamaimo0625:20160122141431p:plain

ここでMain.hsをロードしてみる。

Prelude> :l Main

すると、、パッケージに含まれるSystem.EnvironmentとControl.Monadが必要になるので、自動的にロードされる。

f:id:yamaimo0625:20160122141700p:plain

そして、Fibo.hsがバイトコードコンパイルされて、ロードされる。

f:id:yamaimo0625:20160122141728p:plain

最後に、Main.hsがバイトコードコンパイルされて、ロードされる。

f:id:yamaimo0625:20160122141750p:plain

ここでさらに、Mainはアスタリスクつきで自動的にインポートされる。

f:id:yamaimo0625:20160122141820p:plain

そうすると、Mainで見えるものはGHCiのスコープからも見えるようになるので、FiboやSystem.Environment、Control.Monadのエクスポートされた関数などもGHCiのスコープで見えるようになる。

f:id:yamaimo0625:20160122141945p:plain

これで、プロンプトは`*Main> "に変わり、

*Main> :show modules
Fibo             ( Fibo.hs, interpreted )
Main             ( Main.hs, interpreted )
*Main> :show imports
:module +*Main -- added automatically
*Main> 

となる。

なお、上の状態でGHCiのスコープからFiboやSystem.Environment、Control.Monadのエクスポートされた関数などを見ることが出来るけれど、:show importsにこれらが表示されないのは、あくまでこれらをインポートしているのはMain.hsであり、GHCiではないから。

GHCiからインポートを行ったときの挙動

次に、パッケージからData.Listをインポートしたときの挙動を追ってみる。

*Main> import Data.List

まず、パッケージに含まれるData.Listが必要になるので、自動的にロードされる。

f:id:yamaimo0625:20160122150637p:plain

そして、インポートされ、GHCiのスコープからエクスポートされた関数などが見えるようになる。

f:id:yamaimo0625:20160122150645p:plain

これで、プロンプトは"*Main Data.List> "に変わり、

*Main Data.List> :show imports
import Data.List
:module +*Main -- added automatically
*Main Data.List> 

となる。

:moduleコマンドによる挙動

最後に、:moduleコマンドでの挙動を追ってみる。

まず、Data.Listのインポートを外してみる。

*Main Data.List> :m -Data.List
*Main> 

f:id:yamaimo0625:20160123142519p:plain

これでData.ListがGHCiのスコープから見えなくなった。

そして、すべてのインポートを外してみる。

*Main> :m
Prelude> 

f:id:yamaimo0625:20160123143530p:plain

これでMainもGHCiのスコープから見えなくなったし、Mainから見えていたFiboやSystem.Environment、Control.MonadのエクスポートされたものもGHCiのスコープから見えなくなる。

ただし、ロードされたMainやFiboがアンロードされるわけではないので、メモリには残っている。
実際、:show modulesを実行してみると、次のようになる。

Prelude> :show modules
Fibo             ( Fibo.hs, interpreted )
Main             ( Main.hs, interpreted )
Prelude> 

ここから、Mainをインポートしてみる。

Prelude> :m Main
Prelude Main> 

すると、次のように、Mainがインポートされ、Mainのエクスポートされた関数など(この場合、Mainのトップレベルで定義されたものになるみたい)がGHCiのスコープから見えるようになる。

f:id:yamaimo0625:20160123143543p:plain

なので、次のように、main関数は実行できるけど、fibo関数などは見えないので使うことが出来ない。

relude Main> :main 10
[1,1,2,3,5,8,13,21,34,55]
Prelude Main> fibo 10

<interactive>:103:1: Not in scope: ‘fibo’
Prelude Main> 

最後に、Fiboをアスタリスクつきでインポートしてみる。

Prelude Main> :m +*Fibo
*Fibo Main> 

f:id:yamaimo0625:20160123143755p:plain

こうすると、Fiboで見えるものはGHCiのスコープから全部見えるようになるので、fibo関数はもちろん、fibo'関数も使えるようになる。

*Fibo Main> fibo 10
55
*Fibo Main> fibo' 10 1 1
55
*Fibo Main> 

今日はここまで!