いものやま。

雑多な知識の寄せ集め

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

昨日はGHCのコマンドの基本的な使い方を説明した。

今日からは、対話環境であるGHCiの使い方を中心に説明していく。

まずは、モジュールのロードについて。

モジュールのロード

とりあえず、次のような2つのHaskellソースコードを用意してみる。

-- Main.hs

import System.Environment
import Control.Monad
import Fibo

main = do
    args <- getArgs
    when (length args > 0) $ do
      let count = read $ head args
      print $ map fibo [1..count]
-- Fibo.hs

module Fibo (fibo) where

fibo :: Integer -> Integer
fibo n = fibo' n 1 1

fibo' :: Integer -> Integer -> Integer -> Integer
fibo' 1 a b = a
fibo' n a b = fibo' (n - 1) b (a + b)

これらをGHCiからロードしてみる。
ロードには:loadコマンド(省略形は:l)を使う。

Prelude> :l Main
[1 of 2] Compiling Fibo             ( Fibo.hs, interpreted )
[2 of 2] Compiling Main             ( Main.hs, interpreted )
Ok, modules loaded: Fibo, Main.
*Main> 

ロード対象として指定したのはMain.hsだけど、Fibo.hsとMain.hsの両方がコンパイルされ、ロードされている。
このように、ロード対象として指定されたモジュールからインポートされているモジュールも、一緒にロードされる。

ただし、よく見てみれば、System.EnvironmentやControl.MonadというモジュールもMain.hsからはインポートされているのに、Fibo.hsとは違ってロードされていないように見える。(実際にはロードされている)
どうしてこうなるのかというと、これらはパッケージに含まれているモジュールで、パッケージに含まれているモジュールは、必要になったら(例えば、後述のモジュールのインポートを行ったときなど)自動的にロードされるから。
なので、ロードされたことが明示的には表示されない。

あと、プロンプトが変化していることに気がつくと思う。
これは、

  • Mainモジュールがインポートされている(※ロードではない)
  • Mainモジュールがスコープ内にある(※アスタリスクのついている意味)

ことを意味している。(詳細はまた後日)

ちなみに、プログラムを走らせてみると、次のような感じ。

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

フィボナッチ数列が指定された数だけ表示される。

ロードされたモジュールの一覧

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

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

これは、

ことを意味する。
(※Mainというモジュール名は、Main.hsというファイル名だからでなく、名前のついていないモジュールだからということに注意)

モジュールのリロード

:reloadコマンド(省略形は:r)を使うと、ロードされているモジュールが必要に応じてリロードされる。

例えば、Fibo.hsのタイムスタンプを更新して、リロードしてみる。

*Main> :show modules
Fibo             ( Fibo.hs, interpreted )
Main             ( Main.hs, interpreted )
*Main> :r
Ok, modules loaded: Fibo, Main.
*Main> :! touch Fibo.hs
*Main> :r
[1 of 2] Compiling Fibo             ( Fibo.hs, interpreted )
Ok, modules loaded: Fibo, Main.
*Main> 

:!コマンドを使うと任意のShellコマンドを実行できるので、touchを使ってFibo.hsのタイムスタンプを更新している。
すると、何もしないでリロードした場合には何も起きていないのに、Fibo.hsのタイムスタンプを更新したあとでは、Fiboがリロードされていることが分かると思う。

ちなみに、ファイルを編集したい場合、:editコマンド(省略形は:e)を使うといい。
EDITOR環境変数で指定されたエディタが起動して、ファイルを編集することが出来る。
(ファイルの編集を終えると、GHCiへ戻ってくる)

モジュールのアンロード

あまりやる意味はないのだけど、モジュールをアンロードすることも出来る。

アンロードするには、引数を与えずに:loadコマンドを実行すればいい。

*Main> :show modules
Fibo             ( Fibo.hs, interpreted )
Main             ( Main.hs, interpreted )
*Main> :l
Ok, modules loaded: none.
Prelude> :show modules
Prelude> 

見てのとおり、ロードされていたモジュールがすべてアンロードされる。

ちなみに、:cdコマンドを使うとディレクトリを移動することが出来て、この場合もロードされていたすべてのモジュールはアンロードされる。
(ディレクトリが変わるので、検索パスが変わり、ソースコードを見つけることが出来なくなるから)

今日はここまで!