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

いものやま。

雑多な知識の寄せ集め

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

技術 Haskell

昨日はGHCiでのモジュールのロードについて説明した。

今日はGHCiでのモジュールのインポートについて説明する。

なお、Main.hsとFibo.hsについては、昨日の記事を参照。

モジュールのインポート

パッケージに含まれるモジュールをGHCiの中で使いたい場合、Haskellのコードを書くときと同様に、importを使えばいい。

例えば、Data.Listをインポートしたければ、次のようにする。

Prelude> import Data.List
Prelude Data.List> 

プロンプトにData.Listが増えたけど、これはData.Listがインポートされていることを意味している。

モジュールのインポートを行えば、モジュールでエクスポートされている関数などを使うことが出来るようになる。

例えば、PerlRubyjoinと同じような働きをするData.List.intercalateを使ってみる。

Prelude Data.List> :l Main
[1 of 2] Compiling Fibo             ( Fibo.hs, interpreted )
[2 of 2] Compiling Main             ( Main.hs, interpreted )
Ok, modules loaded: Fibo, Main.
*Main Data.List> intercalate ", " $ map (show . fibo) [1..20]
"1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765"
*Main Data.List> 

インポートされたモジュールの一覧

インポートされたモジュールの一覧を確認するには、:show importsコマンドを使う。

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

見てみると、Data.Listがインポートされているのに追加して、Mainもインポートされていることが分かる。
これは、ロードを行ったときに自動的にインポートも行われるから。("added automatically"というのは、そういう意味)

:moduleコマンドによるコントロール

:moduleコマンド(省略形は:m)を使うと、モジュールのインポートをコントロールすることが出来る。

まず、次のようにモジュール名に+をつけると、モジュールをインポートすることが出来る。

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

逆に、モジュール名に-をつけると、モジュールのインポートを外すことが出来る。

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

あと、:moduleコマンドを引数なしで実行した場合、すべてのモジュールのインポートが外される。

*Main> :m
Prelude> :show imports
import Prelude -- implicit
Prelude> 

ちなみに、上の状態ではモジュールは何もインポートされていないのだけど、Preludeという基本的なモジュールは暗黙的にインポートされていることになっている。(それが"implicit"の意味)

ところで、モジュールのインポートをすべて外したわけだけど、じゃあ、ロードされていたモジュールはどうなっているのか?

実は、モジュールのインポートを外しても、ロードされていたモジュールがアンロードもされてしまうというわけではない。
なので、次のようにすると、ロードされているモジュールがちゃんと表示される。

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

ただし、インポートはされていないので、使うことは出来ない。

Prelude> :main 10

<interactive>:55:57:
    Not in scope: ‘main’
    Perhaps you meant ‘min’ (imported from Prelude)
Prelude> 

main関数を見えるようにするために、Mainをインポートしてみる。

Prelude> :m Main
Prelude Main> 

こうすると、main関数を実行できるようになる。

Prelude Main> :main 20
[1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]
Prelude Main> 

けど、よく見ると、プロンプトが"*Main> "ではなく"Prelude Main> "となっている。
この違いは、fibo関数を実行してみると現れてくる。

*Main> fibo 20
6765
*Main> 
Prelude Main> fibo 20

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

なんと、後者ではfibo関数が見つからない!

これがアスタリスクのついている意味で、アスタリスクがついている場合、そのモジュールがスコープ内に存在することになる。

どういうことかというと、Mainモジュールがスコープ内に存在している場合、Mainモジュールの中ではFiboモジュールもインポートされているので、Fiboモジュールでエクスポートされているfibo関数もスコープ内で見ることが出来る。
一方、Mainモジュールがスコープ内にない場合、スコープから見ることが出来るのはエクスポートされたMainモジュールの関数だけなので、Fiboモジュールでエクスポートされているfibo関数はスコープ内では見ることが出来ない。

つまり、簡単にいうと、次のようになっている。

  • アスタリスクがついてる:そのモジュール内で見えるもののがすべて見える
  • アスタリスクがついてない:そのモジュールからエクスポートされたものだけが見える

モジュールをアスタリスクつきの状態でインポートするには、:moduleコマンドでモジュール名に+*をつければいい。

Prelude> :m +*Main
*Main> 

ちなみに、Fiboモジュールをアスタリスクつきでインポートすれば、エクスポートされていないfibo'関数を呼び出すことも出来る。

*Main> fibo' 10 1 1

<interactive>:86:1:
    Not in scope: ‘fibo'’
    Perhaps you meant ‘fibo’ (imported from Fibo)
*Main> :m +*Fibo
*Main *Fibo> fibo' 10 1 1
55
*Main *Fibo> 

今日はここまで!