5. GHCiを使う¶
GHCi [1] はGHCの対話環境であり,Haskellの式を対話方式で評価したり プログラムを解釈実行したりできます. Hugs の経験があるなら, すぐにでもGHCiに慣れることでしょう. しかしながら, GHCiはコンパイル済みのコードを対話的にロードすることができます. また,GHCが提供する言語拡張のすべて[2]_ をサポートしています. GHCi は対話方式のデバッガも備えています(GHCiのデバッガ 参照).
[1] | “i”は“Interactive”の i です. |
[2] | ただし今のところ foreign export は除きます. |
5.1. GHCi入門¶
GHCiセッションの例を見ていくことから始めましょう.
GHCiは ghci
コマンドで起動します.
$ ghci
GHCi, version 8.0.2: http://www.haskell.org/ghc/ :? for help
Prelude>
GHCiがプレリュードと標準ライブラリをロードするのにすこしかかるかもしれませんが,
それが完了するとプロンプトが現れます.
バナーにあるとおり,:?
をタイプすれば,利用可能なコマンド一覧と
それぞれの短い説明が表示されます.
これ以降ほとんどのコマンドを説明します.
すべてのコマンドの完全な説明は GHCi のコマンド群 にあります.
プロンプトにはHaskellの式をタイプします.
Prelude> 1+2
3
Prelude> let x = 42
Preldue> x / 9
4.666666666666667
Prelude>
GHCiは行全体を1つの式だと解釈し,これを評価します. 式は複数行にまたがることはできません. エンターキーを押したとたん,GHCiはそこまでにタイプされたものを評価しようとします.
Haskellでは let
式は in
をともないます.
しかし,GHCiでは,式は IO
モナドの中でも解釈されますので,上の例は
in
を伴わない let
束縛文であることは,行が表示されないことで示されています.
5.2. ソースファイルをロードする¶
次のようなHaskellのソースコードが Main.hs
というファイルに書かれているとしましょう.
main = print (fac 20)
fac 0 = 1
fac n = n * fac (n-1)
Main.hs
は好きな場所に置けますが,GHCiを起動したファイルがあるカレントディレクトリ
[3] 以外の場所に置いたときには,GHCiでディレクトリを正しく変更する必要があります.
Prelude> :cd dir
ここで ⟨dir⟩ は Main.hs
を保存したディレクトリ(あるいはフォルダ)です.
HaskellのソースファイルをGHCiにロードするには :load
コマンドを使います.
Prelude> :load Main
Compiling Main ( Main.hs, interpreted )
Ok, modules loaded: Main.
*Main>
GHCiは Main
モジュールをロードし,プロンプトが *Main>
に変りました.
このプロンプトは,ここにユーザが入力する式を評価するときの文脈が,たったいまロードした
Main
モジュールであることを示しています
(*
の意味については プロンプトのスコープにあるもの で説明します).
これで Main.hs
で定義した関数を含む式が評価できるようになりました.
*Main> fac 17
355687428096000
複数のモジュールからなるプログラムをロードするのも同様に簡単です.
「最上位の」モジュールの名前を :load
コマンドに指定すればいいだけです
(ヒント: load
は :l
に短縮できます).
最上位のモジュールはふつうは Main
ですが,必ずしもそうである必要はありません.
GHCiは最上位のモジュールから直接・間接に必要とされているモジュールを見つけ,
それらを依存関係の順にロードします.
[3] | コマンドラインから GHCi を起動した場合は GHCi のカレントディレクトリは
シェルから起動したときのカレントディレクトリと同じです.
Windowsの「スタート」メニューから GHCi を起動した場合は,
カレントディレクトリはおそく C:\Documents and Settings\user name あたりでしょう. |
5.2.1. モジュールとファイル名¶
問: GHCi はモジュール ⟨M⟩ がどのファイルにあるかをどうやって知るのですか.
答: M.hs
あるいは M.lhs
というファイルを探します.
したがって,大部分のモジュールでは,モジュール名とファイル名は一致している必要があります.
一致しなかった場合,GHCiはモジュールを見つけ出すことができません.
この規則には一つの例外があります.
:load
を使ってプログラムをロードするとき,
あるいは ghci
を起動するときには,モジュール名ではなくファイル名を指定することができます.
その名前のファイルがあれば,それをロードします.
このときそのファイルにはどのような名前のモジュールを含んでいてもかまいません.
これは,複数の Main
モジュールが1つのディレクトリにある場合,
全てを Main.hs
と呼ぶことはできませんので,特に便利です.
ソースファイルを探すときの探索パスは,次に示すように,
GHCiを起動するコマンドラインで -i
オプションで指定できます.
ghci -idir1:...:dirn
あるいは,GHCiの中で :set
コマンドで指定できます
(GHCiからGHCのコマンドラインオプションを設定する 参照) [4]
GHCiは,このように依存関係を追ってロードすべきモジュールを見つけようとするので,
モジュールごとに,1つのソースファイルがなければなりません.
この規則の唯一の例外はパッケージ由来のモジュールで,それには Prelude
のほか
IO
や Complex
といった標準ライブラリも含まれます.
モジュールをロードしようとしたとき,GHCiがソースファイルを見つけられなければ,
たとえ,そのモジュールのオブジェクトファイルやインターフェイスファイルがあったとしても,
エラーメッセージが表示されます.
[4] | GHCiや --make モードでは -i オプションは,
ソースファイル の探索パスを指定するのに対し,標準の一括コンパイルモードでは
-i オプションはインターフェイスファイルの探索パスを指定することに注意してください.
詳しくは The search path を参照してください. |
5.2.2. ソースコードの変更と再コンパイル¶
ソースコードに変更を加えて,GHCiに再コンパイルさせたいときは :reload
コマンドを使います.
プログラムは必要に応じて再コンパイルされます.
このとき,GHCiは依存関係の変化がないモジュールを実際に再コンパイルするのを避けようと最善をつくします.
これは一括コンパイル時に再コンパイルを避ける機構と同じです
(The recompilation checker 参照).
5.3. コンパイル済みコードをロードする¶
HaskellのソースモジュールをGHCiにロードすると,通常はバイトコードに変換され,インタプリタで実行されます.
しかし,GHCiでは解釈実行されるコードはコンパイル済みコードと一緒に実行することもできます.
実際,GHCiは起動すると通常は base
パッケージのコンパイル済みのものをロードします.
その中には Prelude
モジュールが含まれています.
なぜコンパイル済みのコードを使う必要があるのでしょうか. コンパイル済みコードは解釈実行されるコードに比べて大体10倍速いですが,生成するのに2倍の時間がかります (最適化が有効ならもっと長くなるでしょう). そのため、プログラムのあまり変更されない部分をコンパイルしておき, 活発に開発されている部分にはインタプリタを使ことにすればいいわけです.
:load
でソースモジュールをロードするとき,GHCiは通常対応する
コンパイル済みのオブジェクトファイルを探します.
可能ならソースコードの解釈実行よりも優先してそれを使います.
たとえば,A,B,C,Dという4つのモジュールからなるプログラムがあるとしましょう.
モジュールBとCはどちらもDのみをインポートしていて,AはBとCをインポートしているとしましょう.
A
/ \
B C
\ /
D
以下のように D
をコンパイルしてから,プログラム全体をロードすることができます.
Prelude> :! ghc -c -dynamic D.hs
Prelude> :load A
Compiling B ( B.hs, interpreted )
Compiling C ( C.hs, interpreted )
Compiling A ( A.hs, interpreted )
Ok, modules loaded: A, B, C, D (D.o).
*Main>
コンパイラのメッセージ中に D
についての行がありません.
これは,D
のソースファイルとその依存関係が,最後にコンパイルされたときから変更されていないので,
D
をコンパイルする必要ないからです.
-dynamic
フラグはGHCに渡すものです.
これによって,GHCiはダイナミックリンクオブジェクトコードを使うようになります
(もちろんそれをサポートしているプラットフォームでの話です).
したがって,GHCiでコンパイル済みのコードを利用するためには,そのコードは
ダイナミックリンク可能なようにコンパイルされていなければなりません.
:show modules
を使えば,いつでも,その時点でGHCiにロードされている
モジュールの一覧を表示できます.
*Main> :show modules
D ( D.hs, D.o )
C ( C.hs, interpreted )
B ( B.hs, interpreted )
A ( A.hs, interpreted )
*Main>
ここで D
を変更する(あるいは変更したふりをする: touch
というUnixのコマンドを
使うのが簡単)と,コンパイラはオブジェクトファイルを使えなくなります.
その理由は,オブジェクトファイルがすでに古くなっているに違いないからです.
*Main> :! touch D.hs
*Main> :reload
Compiling D ( D.hs, interpreted )
Ok, modules loaded: A, B, C, D.
*Main>
モジュール D
がコンパイルされました.
しかし,この例では実際にはソースは変更されていないので,インターフェイスは同じままで,
再コンパイル検査器が A
, B
, C
は再コンパイルする必要なしと判断したことに注意してください.
では,別のモジュールを1つコンパイルしてみましょう.
*Main> :! ghc -c C.hs
*Main> :load A
Compiling D ( D.hs, interpreted )
Compiling B ( B.hs, interpreted )
Compiling C ( C.hs, interpreted )
Compiling A ( A.hs, interpreted )
Ok, modules loaded: A, B, C, D.
C
のコンパイル済みのバージョンが使われていません.
何が起きたのでしょうか.
GHCi ではコンパイル済みのモジュールは別のコンパイル済みのモジュールにしか依存できません.
この場合は C
が D
に依存していますが D
にはオブジェクトファイルがないので
GHCiは C
のオブジェクトファイルを利用しなかったのです.
では D
もコンパイルしてみましょう.
*Main> :! ghc -c D.hs
*Main> :reload
Ok, modules loaded: A, B, C, D.
何も起こりません.
もう1つ賢くなりました.
新しくコンパイルされたモジュールは :reload
では拾えません.
:load
を使う必要があります.
*Main> :load A
Compiling B ( B.hs, interpreted )
Compiling A ( A.hs, interpreted )
Ok, modules loaded: A, B, C (C.o), D (D.o).
このようなオブジェクトファイルの自動ロードは混乱の原因になることがあります.
モジュールのエクスポートされていないトップレベルの定義をプロンプトの式で使えるのは,
そのモジュールが解釈実行されているときだけだからです
(プロンプトのスコープにあるもの 参照).
このため,GHCiにインタプリタを使ってモジュールのロードを強制したいことがあるかもしれません.
そうするには :load
を使うときにモジュール名またはファイル名の前に*を置きます.
たとえば,以下のようにします.
Prelude> :load *A
Compiling A ( A.hs, interpreted )
*A>
*
を使うと,GHCiはコンパイル済みオブジェクトコードがあっても無視し,モジュールを解釈実行します.
既にモジュールをいくつかオブジェクトコードとしてロードしていて,そのうち1つを解釈実行したいときには,
全部を再ロードせず :add *M
を使えば, M
だけを解釈実行することを指定できます.
(これによって別のモジュールも解釈実行されるかもしれないことに注意してください.
これは,コンパイル済みモジュールは解釈実行モジュールに依存できないためです.)
いつでも,すべてコンパイル済みのオブジェクトコードにしたければ,
インタプリタを使ってはいけません.
-fobject-code
オプションを使ってください(GHCi内でオブジェクトコードにコンパイルする 参照).
Hint
GHCi はコンパイル済みの版が最新であることが確かな場合にしか,
コンパイル済みオブジェクトファイルを使わないので,
大きいプロジェクトでは,ときどき ghc --make
を実行してプロジェクト全体をコンパイルし
(たとえば,昼食を食べに行く前にね),インタプリタを使って作業を続けるというのが良い方法です.
コードを変更したときは,そのモジュールは解釈実行されますが,プロジェクト中の他の部分は
変わらずコンパイル済みのものが使われます.
5.4. プロンプトで対話的に評価する¶
プロンプトに式を入力すると,GHCi はただちに評価して結果を表示します.
Prelude> reverse "hello"
"olleh"
Prelude> 5+5
10
5.4.1. プロンプトでのI/Oアクション¶
GHCi がプロンプトで行うのは単なる式の評価だけではありません.
ある型 a
に関して IO a
型の式を入力すると,GHCiはそれをIOコンピュテーションとして
実行 します.
Prelude> "hello"
"hello"
Prelude> putStrLn "hello"
hello
式の型がより一般的なものであっても IO a
に 具体化 することができる限り動作します.
それ例が以下です.
Prelude> return True
True
さらに,GHCiは以下の条件を満す場合(かつその限りにおいて),I/Oアクションの結果を表示します.
- 結果の型が
Show
のインスタンスの場合. - 結果の型が
()
ではない場合.
以下はその例です.ただし, putStrLn :: String -> IO ()
です.
Prelude> putStrLn "hello"
hello
Prelude> do { putStrLn "hello"; return "yes" }
hello
"yes"
5.4.2. プロンプトで do
記法を使う¶
実際には,GHCiはプロンプトで受け付けているのは単なる式ではなく文です. そのため,値や関数を名前に束縛して,後で式や文の中で使うことができます.
The syntax of a statement accepted at the GHCi prompt is exactly the GHCiがプロンプトで受け付ける文の構文は,Haskellのdo式における文の構文と全く同じです. ただし,こちらにはモナドの多重定義はありません. プロンプトに入力される文はIOモナドの中になければなりません.
Prelude> x <- return 42
Prelude> print x
42
Prelude>
x <- return 42
という文は「return 42
を IO
モナド内で実行し,
その結果で x
を束縛する」という意味です.
以降 x
は文の中で使用できます.
たとえば,上でしたように値を印字できます.
-
-fprint-bind-result
-fprint-bind-result
が設定されていれば,GHCiは次の場合(かつ,その場合に限り) 文の結果を表示します.- 当該の文が束縛ではないか,1つの変数だけを束縛するモナド束縛(
p <- e
)の場合. - 変数の型が多相的でなく,
()
でもなく,Show
のインスタンスである場合.
- 当該の文が束縛ではないか,1つの変数だけを束縛するモナド束縛(
もちろん,let
文を使って,ふつうの非IO式で束縛することもできます.
Prelude> let x = 42
Prelude> x
42
Prelude>
この2種類の束縛のもうひとつ重要な違いは,
モナド束縛(p <- e
)は 正格 (e
を評価)ですが,
let
形式では式はすぐには評価されないということです.
Prelude> let x = error "help!"
Prelude> print x
*** Exception: help!
Prelude>
let
束縛では,モナド束縛とは違い,束縛値が自動的に表示されることはありません.
プロンプトで関数を定義することもできます.
Prelude> add a b = a + b
Prelude> add 1 2
3
Prelude>
しかし,複数の節からなる関数を定義したり,相互再帰的な関数を定義したりしようとすると, このやりかたはすぐ面倒になります. レイアウト規則が使えず,定義全体を明示的なブレースとセミコロンを使って一行で与えないといけないからです.
Prelude> f op n [] = n ; f op n (h:t) = h `op` f op n t
Prelude> f (+) 0 [1..3]
6
Prelude>
この問題を軽減するために,GHCi コマンドは複数行に渡って書くことができるようになっています.
:{
および :}
で,それぞれ1行を使います.
Prelude> :{
Prelude| g op n [] = n
Prelude| g op n (h:t) = h `op` g op n t
Prelude| :}
Prelude> g (*) 1 [1..3]
6
このような複数行コマンドは任意のGHCiコマンドについて用いることができます. このときレイアウト規則が有効であることに注意してください. 複数行コマンドの主な目的は,モジュールのロードの代替にすることではなく, .ghciファイル(.ghci ファイルと .haskeline ファイル 参照)での定義を読みやすく保守しやするするためです.
文を評価または実行している間に発生した例外は,GHCiのコマンド行インターフェイスによって捕捉され,
表示されます(例外について詳しくはライブラリドキュメント Control.Exception
モジュールのライブラリ 文書 を参照してください).
新しい束縛は,同じ名前の既存の束縛をシャドウし(覆い隠し)ます. これは現在のモジュールの文脈でスコープにある実体もシャドウします.
Warning
プロンプトで導入さた束縛あ一時的なもので,
次に :load
あるいは :reload
コマンドが実行されるまでの間のことです.
これらのコマンドが実行されると,一時的な束縛は消えてしまいます.
ただし,:module
: で文脈を変更しても一時的は束縛は新しい場所へ移動し,
消えることはありません.
Hint
+t
オプションを指定すると,GHCi は文が束縛したそれぞれの変数の型を表示するようになります.
以下はその例です.
Prelude> :set +t
Prelude> let (x:xs) = [1..]
x :: Integer
xs :: [Integer]
5.4.3. 複数行入力¶
上で説明しあた :{ ... :}
構文による複数行入力のほかに,GHCiでは :set +m
とすると有効になる複数行モードがあります.
このモードでは,現在の文が入力途中であることをGHCiが自動的に検出します.
その先の入力ができるようになります.
複数行入力は空行で終端します.
実際の入力例は以下のとおりです.
Prelude> :set +m
Prelude> let x = 42
Prelude|
この let
文にはさらに束縛を追加できます.
GHCiのプロンプトが変り,直前の行の続きを入力できることが判ります.
レイアウト規則は有効ですので,この let
にさらに束縛を加えるには
束縛の先頭位置をそろえる必要があることに注意してください.
Prelude> :set +m
Prelude> let x = 42
Prelude| y = 3
Prelude|
Prelude>
レイアウトではなく明示的にブレースをセミコロンを使うこともできます.
Prelude> do {
Prelude| putStrLn "hello"
Prelude| ;putStrLn "world"
Prelude| }
hello
world
Prelude>
閉じブレースの後は,現在の文がおわっていることが判るので,空行は必要ありません.
複数行モードはモナドの do
文を入力するのに便利です.
Control.Monad.State> flip evalStateT 0 $ do
Control.Monad.State| i <- get
Control.Monad.State| lift $ do
Control.Monad.State| putStrLn "Hello World!"
Control.Monad.State| print i
Control.Monad.State|
"Hello World!"
0
Control.Monad.State>
複数行モードで入力中に中断してトップレベルのプロンプトに戻ることもできます.
Prelude> do
Prelude| putStrLn "Hello, World!"
Prelude| ^C
Prelude>
5.4.4. 型,クラス,その他の宣言¶
GHCi のプロンプトには,Haskellの任意のトップレベル宣言を入力できます.
これには data
, type
, newtype
, class
, instance
,
deriving
, foreign
宣言が含まれています.
例は以下のとおり.
Prelude> data T = A | B | C deriving (Eq, Ord, Show, Enum)
Prelude> [A ..]
[A,B,C]
Prelude> :i T
data T = A | B | C -- Defined at <interactive>:2:6
instance Enum T -- Defined at <interactive>:2:45
instance Eq T -- Defined at <interactive>:2:30
instance Ord T -- Defined at <interactive>:2:34
instance Show T -- Defined at <interactive>:2:39
As with ordinary variable bindings, later definitions shadow earlier 通常の変数束縛と同様に,後で定義されたものは古い定義をシャドウしてしまうので, 定義を再入力すれば,問題を修正したり拡張したりできます. ただし,落とし穴があります. 新しい型宣言が古い型宣言をシャドウするとき,古い型の定義を参照している別の宣言があるかもしれません. この古い型はまだ存在し,この別の宣言はまだ古い型を参照しているということを覚えておいてください. 古い型と新しい型は同じ名前ですが,GHCiはこれらを区別するということです. たとえば,
Prelude> data T = A | B
Prelude> let f A = True; f B = False
Prelude> data T = A | B | C
Prelude> f A
<interactive>:4:3: error:
• Couldn't match expected type ‘Ghci1.T’
with actual type ‘T’
NB: ‘T’ is defined at <interactive>:3:1-18
‘Ghci1.T’ is defined at <interactive>:1:1-14
• In the first argument of ‘f’, namely ‘A’
In the expression: f A
In an equation for ‘it’: it = f A
Prelude>
シャドウされた古いほうの T
は Ghci1.T
と表示されています.
これは,単に T
と表示されている新しい方と区別するためです.
クラスや型族のインスタンス宣言は,単に利用可能なインスタンスの一覧に追加されるだけです. ただし例外が一つあります. クラスや型族インスタンスは再定義したいこともあるので, 頭部あるいは左辺が同一であるインスタンスはそれぞれ新しいもので 置き換える ことになります (Type families 参照).
5.4.5. プロンプトのスコープにあるもの¶
プロンプトに式を入力するとき,どの識別子や型がスコープにあるのでしょうか. 以下のように,GHCiでは,式を評価する際の環境を構成する方法を正確に指定できます.
:load
,:add
,:reload
コマンド (スコープ内容に対する :load の影響).import
宣言(import によるスコープ制御).:module
コマンド(:module コマンドによるスコープ制御).
:show imports
を使えば,トップレベルのスコープにどのモジュールがあるか要約を表示できます.
Hint
GHCi ではスコープ内にある名前をタブ補完できます.
たとえば,GHCiを起動して J<tab>
と入力すると Just
と展開されます.
5.4.5.1. スコープ内容に対する :load
の影響¶
:load
, :add
, :reload
コマンド
(ソースファイルをロードする と コンパイル済みコードをロードする を参照) はトップレベルのスコープに影響します.
単純な場合から始めましょう.
GHCi を起動すると最初のプロンプトは以下のようになります.
Prelude>
これは現在のスコープにあるものはすべて Prelude
モジュール由来であるということを示しています.
ここで見える識別子は import
宣言のないHaskellのソースファイルから見える識別子と一致しています.
ここで,GHCiにファイルをロードすると,プロンプトは変化します.
Prelude> :load Main.hs
Compiling Main ( Main.hs, interpreted )
*Main>
新しいプロンプトは *Main> `` です.
これはプロンプトに入力した式の文脈が ``Main
モジュールのトップレベルの文脈であることを示しています.
ロードした Main
モジュールのトップレベルのスコープにあるものはすべて,
このプロンプトのスコープにあります
(Main
が明示的に隠蔽していなければ Prelude
も含まれます).
プロンプトの *module
という構文は,このプロンプトに入力した式のスコープは
⟨module⟩ のトップレベルのスコープであることを示しています.
*
が付かない場合は当該モジュールからエクスポートされたものだけが見えるということです.
Note
技術的理由により,GHCi が *
形式で表示できるのは解釈実行するモジュールに限られます.
コンパイル済みのモジュールおよびパッケージモジュールの場合は,それらから,エクスポート
されたものだけが現在のスコープに入ります.
GHCi が解釈実行版のモジュールをロードするようにするには,モジュールをロードするときに
*
を付けます.たとえば :load *M
のようにロードします.
一般に :load
コマンドが発行された後,直近にロードされた「ターゲット」モジュールに対する
インポートが自動的にスコープに追加されます.
このとき,可能なら *
形式が使われます.
たとえば :load foo.hs bar.hs
と入力したとき bar.hs
に Bar
というモジュールがあるとすると,
Bar
が解釈実行されているなら,スコープは *Bar
に設定され, Bar
がコンパイル済みなら,
スコープは Prelude Bar
になります
(GHCiは Prelude
が指定されておらず,しかも *
形式のモジュールが一つもなければ Prelude
を自動的に付け加えます).
これらの自動に追加されたインポートについては :show imports
で表示できます.
Prelude> :load hello.hs
[1 of 1] Compiling Main ( hello.hs, interpreted )
Ok, modules loaded: Main.
*Main> :show imports
:module +*Main -- added automatically
*Main>
この自動的に追加されたインポートは,次に :load
,
:add
あるいは :reload
を発行すると別のものに置き換えられます.
通常のインポートと同様に :module
で削除することもできます.
5.4.5.2. import
によるスコープ制御¶
GHCi が扱えるのは単一のモジュールだけではありません.
複数のモジュールからのスコープを組み合わせることもできます.
このとき *
形式とそうでない形式を混ぜて使えます.
GHCi はこのようなモジュールのスコープのを全て組み合わせて,プロンプトのスコープします.
モジュールをスコープに加えるには,通常はHaskellの import
構文を使います.
Prelude> import System.IO
Prelude System.IO> hPutStrLn stdout "hello"
hello
Prelude System.IO>
hiding
節および as
節を含む完全なHaskellのインポート構文がサポートされています.
プロンプトには現在インポートされているモジュールが表示されていますが,
hiding
や as
やその他の詳細は省略されています.
その部分を知りたければ :show imports
を使って下さい.
Prelude> import System.IO
Prelude System.IO> import Data.Map as Map
Prelude System.IO Map> :show imports
import Prelude -- implicit
import System.IO
import Data.Map as Map
Prelude System.IO Map>
Prelude
のインポートについては implicit (暗黙) と表示されることに注意してください.
明示的に Prelude
をインポートすれば,他のモジュールと同じように表示されます.
複数のモジュールがスコープにあるとき,特に複数の *
形式のモジュールがあるときは,
名前の衝突が起こりやすくなります.
Haskell では名前の衝突が起こったことが報告されるのは,実際に曖昧な名前が使われたときに限ると規定されています.
GHCi もプロンプトで入力される式についてはこれにならった振る舞いをします.
5.4.5.3. :module
コマンドによるスコープ制御¶
スコープを操作するもう1つの方法は :module
コマンドです.
構文は以下のとおりになります.
:module +|- *mod1 ... *modn
+
形式の module
コマンドでモジュールを現在のスコープに追加し,
-
形式でモジュールを現在のスコープから削除します.
+
形式でも -
形式でもない場合には,指定されたモジュール群が現在のスコープに置き換わります.
+
形式でも -
形式でもなく,Prelude
も指定に含まれていない場合は,
自動的に,暗黙に Prelude
をインポートします.
:module
コマンドでは通常の import
宣言ではできないことが2つ可能になります.
:module
コマンドでは,モジュールを*
で修飾できます.そうすると,単にモジュールがエクスポートしているものだけではなく,モジュールのトップレベルのスコープが完全にオープンになります.:module -M
構文を使うと,文脈からモジュールを削除できます.import
構文は累積的(Haskellのモジュール内と同様)なので,これがスコープからモジュールを取り除く唯一の方法です.
5.4.5.4. 修飾名¶
To make life slightly easier, the GHCi prompt also behaves as if there
手間をすこし省くことができるように,GHCiのプロンプトは全てのパッケージの全てのモジュールと,現在GHCiにロードされている全てのモジュールについて,暗黙の import qualified
宣言があるかのように振る舞います.
これは -fno-implicit-import-qualified
というフラグで無効にできます.
5.4.5.5. :module
と :load
¶
:module
/import
と :load
/:add
/:reload
とは同じものという気がするかもしれません.
どちらも,モジュールをスコープに入れるために使うものです.
しかし,この2つには大きな違いがあります.
GHCi は2種類のモジュール集合にかかわっています.
- 現在 ロード済み のモジュール集合.
このモジュール集合は
:load
,:add
,:reload
で変更し,:show modules
で表示できます. - 現在,プロンプトの スコープ内 にあるモジュール集合.
このモジュール集合は
import
および:module
で変更します.:load
,:add
,:reload
コマンドを発行すると このモジュール集合は,上述のように自動的に変更されます. 表示するためには:show imports
を使います.
(:module
あるいは import
経由)モジュールをスコープに追加できるのは,
(a) ロード済みのモジュール,(b) GHCiが知っているパッケージ由来のモジュール,のどちらかだけです.
:module
あるいは import
を使って,ロードされていないモジュールをスコープに
追加しようとすると module M is not loaded
というメッセージが表示されることでしょう.
5.4.6. :main
コマンドと :run
コマンド¶
プログラムをコンパイルして実行するとき getArgs
関数を使っていれば,コマンドライン引数にアクセスできます.
しかし main
は直接引数を取りませんので,ghci でテストしているときは単純にコマンドライン引数を渡すことができません.
その代りとして :main
コマンドを使います.
このコマンドはスコープ内にある main
を以下のようにコマンドライン引数を渡した状態にして起動します.
Prelude> main = System.Environment.getArgs >>= print
Prelude> :main foo bar
["foo","bar"]
スペースのような文字を含む文字列は引用符でかこって渡せます. 引数はそれぞれHaskellの文字列として扱われます. また,Haskellのリスト構文をそのまま使うこともできます.
Prelude> :main foo "bar baz"
["foo","bar baz"]
Prelude> :main ["foo", "bar baz"]
["foo","bar baz"]
また -main-is
フラグや :run
コマンドを使えば,その他の関数を呼べます.
Prelude> foo = putStrLn "foo" >> System.Environment.getArgs >>= print
Prelude> bar = putStrLn "bar" >> System.Environment.getArgs >>= print
Prelude> :set -main-is foo
Prelude> :main foo "bar baz"
foo
["foo","bar baz"]
Prelude> :run bar ["foo", "bar baz"]
bar
["foo","bar baz"]
5.4.7. it
変数¶
プロンプトに式(正確には非束縛文)を入力すると,
GHCi は暗黙のうちにその値で it
を束縛します.
以下はその一例です.
Prelude> 1+2
3
Prelude> it * 2
6
実際はなにが起こっているかというと,GHCi は型検査を行い,その式の型が IO
型でなければ,
次のように変形します.
すなわち,ここで当該の式を e
とすると,
let it = e;
print it
のように変形したのち,これをIOアクションとして実行します.
そういう訳で,元の式の型は Show
クラスのインスタンスでなければなりません.
Show
クラスのインスタンスでなかったら,GHCiは文句をいいます.
Prelude> id
<interactive>:1:0:
No instance for (Show (a -> a))
arising from use of `print' at <interactive>:1:0-1
Possible fix: add an instance declaration for (Show (a -> a))
In the expression: print it
In a 'do' expression: print it
このエラーメッセージから,内部の変形で何が起こったのか少しだけうかがい知ることができます.
式の型がなにがしかの型 a
について IO a
型である場合には,
it
はその IO
コンピュテーションの結果,つまり a
型の値で束縛されます.
Prelude> Time.getClockTime
Wed Mar 14 12:23:13 GMT 2001
Prelude> print it
Wed Mar 14 12:23:13 GMT 2001
IO型の式 e
に対する変形は,
it <- e
となります.
新しい式を評価するたびに it
の値は新しい値でシャドウされ,古い it
の値は失われることに注意してください.
5.4.8. GHCi でのデフォルト型設定¶
次のGHCiセッションを考えていみましょう.
ghci> reverse []
GHCi は何をすべきでしょうか.
厳密にいえば,このプログラムは曖昧です.
show (reverse [])
(ここでGHCiが計算するのはこれです)の型は,
Show a => String
であり,これをどのように表示するかは a
の型に依存します.
たとえば,
ghci> reverse ([] :: String)
""
ghci> reverse ([] :: [Int])
[]
のようになります.
しかし,ユーザがこの型を指定しなければならないというのは面倒なので,
GHCiはHaskellのデフォルト型設定規則(Haskell 2010 Report の 4.3.4 節)
を以下のように拡張しています.
標準の規則では,個々の型変数 a
についてのそれぞれ制約グループ (C1 a, C2 a, ..., Cn a)
を考え,
次の条件が満たされたとき,この型変数のデフォルトの型を設定します.
- 型変数
a
が他のどの制約にも現れない. - クラス
Ci
はすべて標準のクラスである. - クラス
Ci
の少くとも1つは数値である.
GHCiプロンプトあるいはGHCでは -XExtendedDefaultRules
フラグが設定されていると,
以下のような規則変更がおこなわれます.
- 規則2の緩和: クラス
Ci
は すべて 単一パラメータの型クラスである. - 規則3の緩和: クラス
Ci
のうち少くとも1つは数値であるかShow
,Eq
,Ord
,Foldable
,Traversable
のどれかである. - ユニット型
()
およびリスト型[]
がデフォルトの型として試されるリストの先頭に追加される.
最後の点は,たとえば,以下のプログラムに影響します.
main :: IO ()
main = print def
instance Num ()
def :: (Num a, Enum a) => a
def = toEnum 0
このプログラムは 0
ではなく ()
を表示します.
それは a
のデフォルトの型が Integer
ではなく ()
に設定されるからです.
このような変更を行う動機は IO a
アクションのデフォルトの型は IO ()
になるので,
これを実行したときghciは結果を表示する面倒がないというものです.
とくに printf
にとってはこれが重要で printf
のインスタンスで IO a
を返すものがありますが,
それができることといえば undefined
を返すこと以外ありません
(printf が型クラスシステムの拡張を必要としないようにというのがその理由).
したがって,もしここでデフォルトの型が Integer
だと,printfを走らせると,ghciがエラーになってしまいます.
計算を扱うモナドは,可能であるなら IO
がデフォルトであることについては プロンプトでのI/Oアクション を参照してください.
5.4.9. 独自の対話表示関数を使う¶
GHC 7.6.1 以降,GHCiはプロンプトに入力された式の結果を System.IO.print
を使って表示します.
この関数の型シグネチャは Show a => a -> IO ()
で,値を show
を使って String
に変換しています.
このやり方が理想的ではない場合があります. 出力が長い場合や非アスキー文字が含まれるというような場合です.
-interactive-print
フラグを使えば,何らかの制約を C
として,
C a => a -> IO ()
という型の関数を評価済みの式の値を表示する関数として指定できるようになります.
この関数はロード済みのモジュールまたは登録済みのパッケージに置いてあればよいのですが,
登録済みのパッケージに置いてある場合のみ :cd
, :add
, :load
, :reload
あるいは :set
というコマンドをくぐり抜けられます.
-
-interactive-print
⟨expr⟩
¶ GHCiが評価結果を表示するのに使う関数を設定します. このとき〈expr〉の型は
C a => a -> IO ()
でなければなりません.
例として,以下の特別な表示モジュールがあるとしましょう.
module SpecPrinter where
import System.IO
sprint a = putStrLn $ show a ++ "!"
sprint
関数は表示された値の最後に感嘆符を追加します.
以下のコマンド
ghci -interactive-print=SpecPrinter.sprinter SpecPrinter
でGHCiを起動すると,対話セッションが始まり,そこでは値は sprint
で表示されます.
*SpecPrinter> [1,2,3]
[1,2,3]!
*SpecPrinter> 42
42!
これには独自の整形表示関数が使えます. たとえばツリー構造や入れ子構造をよりよみやすい形式で表示できます.
-interactive-print
フラグはGHCを -e mode
で起動したときにも使えます.
% ghc -e "[1,2,3]" -interactive-print=SpecPrinter.sprint SpecPrinter
[1,2,3]!
5.4.10. GHCiのスタックトレース¶
[ この機能は GHC 8.0.1 で導入された実験的機能で,
-fexternal-interpreter
フラグで有効になります.
現時点ではWindowsでは利用できません.]
GHCi では解釈実行コードを走らせるときに,プロファイリングシステムを使って, スタックトレース情報を収集できます. スタックトレースにアクセスするには,GHCiを次のように起動します.
ghci -fexternal-interpreter -prof
こうすると,解釈実行コードは別プロセスで(インタプリタを別プロセスで走らせる 参照),
プロファイリングモードで走らせてコールスタック情報を収集します.
解釈実行コードをプロファイリングモードで走らせることになりますので,
使用するすべてのパッケージはプロファイリング用にコンパイルされている必要があります.
GHCi に対する -prof
フラグは -fexternal-interpreter
と同時に使うときだけ有効です.
現在のコールスタックにアクセスする方法は3つあります.
error
とundefined
は自動的にエラーメッセージにカレントスタックをアタッチします. これは通常HasCallStack
スタック(HasCallStack 参照)を補足するもので, その時には両方のスタックが表示されます.Debug.Trace.traceStack
はDebug.Trace.trace
の変形版で現在のコールスタックも表示します.GHC.Stack
にある関数を使って現在のスタックを取得し,表示できます.
解釈実行するモジュールについては -fprof-auto
を使う必要はありません.
注釈は自動的に細かく追加されますので,個別のコールサイトを区別できます.
しかしながら,コンパイル済みコードのコールスタック情報は,そのコードを
-fprof-auto
付きでコンパイルしているか,明示的に SCC
注釈
(Inserting cost centres by hand 参照)を付けていないかぎり見ることはできません.
5.5. GHCiのデバッガ¶
GHCi は単純な命令スタイルのデバッガを備えています. これを使うと,変数の値を確認するために進行中の計算を止められます. このデバッガはGHCiに統合されていて,デフォルトで有効になっています. デバッグ機能を有効にするのにフラグは必要ありません. 1つ重要な制限があります. それは,ブレイクポイントとステップ実行は解釈実行されているモジュールでしか使えないということです. コンパイル済みのコードはデバッガからは見えません [5] .
このデバッガが提供する機能は以下のとおりです.
- プログラム中の関数定義や式にブレイクポイントを設定する機能. 関数が呼ばれたとき,式が評価されたとき,GHCiは実行を中断しプロンプトに戻ります. このプロンプトで局所変数の値を調べたあと,実行を再開継続できます.
- ステップ実行機能. 評価器はほぼ簡約ごとに実行を中断し,局所変数の値を調べられるようにします. これはプログラムのあらゆるポイントにブレイクポイントを設定するのと同じことです.
- トレースモードでの実行機能. トレースモードで実行すると,評価器は発生した評価ステップをすべて記憶します. ただし,実際のブレイクポイントに到達するまでは,実行を中断することはありません. 実行が中断されたら,評価ステップの履歴を調べることができるようになります.
- 例外(たとえば,パターン照合の失敗あるいは
error
など)をブレイクポイントとして扱えます. これにより,プログラム中の例外発生源を特定しやすくなります.
現時点では「スタックトレース」を得る手段は提供されていませんが, トレース機能と履歴機能が次善の策として提供されており, エラー発生時の状況を知るには十分であることも多いのです. たとえコンパイル済みのコードから例外が投げられたときでも,自動的にブレイクするようにできます (例外のデバッグ 参照).
5.5.1. ブレイクポイントと変数内容の表示¶
実際に動く例としてクイックソートを使いましょう.以下がそのコードです.
qsort [] = []
qsort (a:as) = qsort left ++ [a] ++ qsort right
where (left,right) = (filter (<=a) as, filter (>a) as)
main = print (qsort [8, 4, 0, 3, 1, 23, 11, 18])
ます,このモジュールをGHCiにロードしましょう.
Prelude> :l qsort.hs
[1 of 1] Compiling Main ( qsort.hs, interpreted )
Ok, modules loaded: Main.
*Main>
次に,qsort の定義2つめの等式の右辺にブレイクポイントを設定します.
*Main> :break 2
Breakpoint 0 activated at qsort.hs:2:16-47
*Main>
:break 2
というコマンドは直近にロードしたモジュールの2行目にブレイクポイントを設定するものです.
この場合は qsort.hs
の2行目です.
詳しくいえば,ブレイクポイントを設定した行にある完全な部分式のうちもっとも左側にあるものが選ばれます.
この場合は (qsort left ++ [a] ++ qsort right)
です.
[–ここから– GHC Users Manual 原文の記述とghciの実際の挙動が異なるので,実際の挙動に沿って非公式に説明します.]
さて,このプログラムを走らせてみましょう.
*Main> main
Stopped in Main.qsort, qsort.hs:2:16-47
_result :: [Integer] = _
a :: Integer = 8
left :: [Integer] = _
right :: [Integer] = _
[qsort.hs:2:16-47] *Main>
[–ここまで– 実際の挙動に沿った非公式な記述]
ブレイクポイントのところで実行が中断されました.
プロンプトが変化して,ブレイクポイントで止っていること,
その場所が [qsort.hs:2:16-47]
であることが判ります.
その場所をさらに明確にするには :list
コマンドを使います.
[qsort.hs:2:16-47] *Main> :list
1 qsort [] = []
2 qsort (a:as) = qsort left ++ [a] ++ qsort right
3 where (left,right) = (filter (<=a) as, filter (>a) as)
:list
コマンドは,現在のブレイクポイントの周囲のコードを表示します.
出力デバイスがサポートしている場合は,注目している部分式がボールド体で表示されます.
GHCiは,ブレイクポイントを置いた式の自由変数 [6] (a
, left
, right
)
に対する束縛および当該式の結果(_result
)に対する束縛も提供しています.
これらの変数は,GHCi上で普通に定義する他の変数と同じです.
プロンプトで入力する式の中で使ったり, :type
コマンドで型を確認するなどが可能です.
[–ここから– GHC Users Manual 原文の記述とghciの実際の挙動が異なるので,実際の挙動に沿って非公式に説明します.]
例を見ましょう.
[qsort.hs:2:16-47] *Main> :type left
left :: [Integer]
[–ここまで– 実際の挙動に沿って非公式説明]
デバッガにはジェネリックな表示コマンド
:print
があり,これを使えば,変数の実行時の値と型を調べられます.
left
に対して使ってみましょう.
[qsort.hs:2:16-47] *Main> :print left
left = (_t1::[Integer])
あまり細かいことは判りません.
left
は未評価の計算(サスペンションあるいはサンク)に束縛されています.
これは left
は未評価ですが :print
が評価を強制しないからです.
:print
はブレイクポイントで値を検査する際に副作用を起こさないようにしてあるのです.
評価を強制しないので,通常の評価と違う結果になったり,例外が投げられたり,無限ループや別のブレイクポイントに遭遇することもありません
(ブレイクポイントのネスト 参照).
:print
は各サンクにアンダースコアで始まるフレッシュ(まだ使われていない)変数,ここでは _t1
を束縛します.
[–ここでは– GHC Users Manual 原文の記述とghciの実際の挙動が異なる部分に関連する説明を省いています.]
変数の評価状態を変えてしまってかまわないのなら :print
ではなく :force
を使うこともできます.
:force
コマンドはサンクのときは評価を強制する以外は :print
と同じ振る舞いになります.
[qsort.hs:2:16-47] *Main> :force left
left = [4,0,3,1]
[–ここから– GHC Users Manual 原文の記述とghciの実際の挙動が異なるので,実際の挙動に沿って非公式に説明します.]
ここで :show bindings
を使うと,関連する束縛を表示できます.
[qsort.hs:2:16-47] *Main> :show bindings
right :: [Integer] = _
left :: [Integer] = [4,0,3,1]
a :: Integer = 8
_result :: [Integer] = _
_t1 :: [Integer] = [4,0,3,1]
式全体を:forceで評価してしまうのではなく,個々のサンクを評価したい場合には,Haskellの
seq
関数が便利でしょう.以下のように使います.
[qsort.hs:2:16-47] *Main> :print right
right = (_t2::[Integer])
[qsort.hs:2:16-47] *Main> seq _t2 ()
()
[qsort.hs:2:16-47] *Main> :print right
right = 23 : (_t3::[Integer])
We evaluated only the _t1
thunk, revealing the head of the list, and
ここでは,サンク _t2
だけを評価して,リストの先頭が判明しました.
seq
関数はすこし使いにくいので :def
を使ってもっとよいインターフェイスを作るといいでしょう
(どうするかは練習問題にしておきます!).
そして,実行を再開することもできます.
[qsort.hs:2:16-47] *Main> :continue
Stopped in Main.qsort, qsort.hs:2:16-47
_result :: [Integer] = _
a :: Integer = 4
left :: [Integer] = _
right :: [Integer] = _
[qsort.hs:2:16-47] *Main>
実行が前に停止した点から再開し,同じブレイクで再び停止しました.
[–ここまで– 実際の挙動に沿った非公式説明.]
5.5.1.1. ブレイクポイントの設定¶
ブレークポイントを設定する方法はいくつかあります. おそらくもっとも簡単な方法は最上位の関数の名前を使うことです.
:break identifier
ここで ⟨identifier⟩ はGHCiにロードされて解釈実行されるモジュールのトップレベルにある関数の名前です (これには修飾名も使えます). ブレイクポイントは関数の本体部分に設定されます. 関数が完全に適用されパターン照合が行われる直前に設定されます.
行番号(および列番号)でブレイクポイントを設定することもできます.
:break line
:break line column
:break module line
:break module line column
ブレークポイントを特定の行に設定する場合,GHCiはその行で始まりその行で終わる部分式の中で もっとも左側にあるものに設定します. 2つの完全な部分式が同じカラムから始まっているなら長い方が選ばれます. その行に完全な部分式が無い場合,その行で始まっている部分式の中でもっとも左側にあるものが選ばれます. それも失敗したら,その行を一部あるいは全部覆う式の中でもっとも右側にあるものが選ばれます.
ブレークポイントを特定の行の特定のカラムに設定する場合,GHCiはその位置を含む式の中で最小のものを選びます.
注意: GHCはTAB文字を現れた位置に関わらず幅1とみなします.
言い換えれば,カラム数を数えるのではなく文字を数えます.
振る舞いと合うエディタもあり,合わないエディタもあります.
最善はそもそもソースコード中でタブ文字を使わないことです
(Warnings and sanity-checking にある -Wtabs
を参照してください).
モジュールが省略された場合,直近にロードされたモジュールが使われます.
ブレークポイントを設定できない部分式もあります. 単一の変数は通常ブレークポイント位置とはみなされません(ただし,その変数が関数定義かλかcaseの選択肢の右辺である場合は除きます). 大まかにいうと,ブレークポイントになるのは,全ての簡約基,関数やλ抽象の本体,caseの選択肢,束縛文です. 通常let式はブレークポイントになりませんが,その本体は常にブレークポイントになります. そのletで束縛された変数の値を調べたいと思うのが普通だからです.
5.5.1.2. ブレイクポイントの一覧と削除¶
現在有効になっているブレイクポイントを一覧するには
:show breaks
を使います.
*Main> :show breaks
[0] Main qsort.hs:1:12-13
[1] Main qsort.hs:2:16-47
To delete a breakpoint, use the command with the number
ブレイクポイントを削除するには :delete
コマンドを使い :show breaks
で出力されるブレイクポイント番号を指定します.
*Main> :delete 0
*Main> :show breaks
[1] Main qsort.hs:2:16-47
全てのブレイクポイントを一度に削除するには :delete *
とします.
5.5.2. ステップ実行¶
ステップ実行は,プログラムの実行を可視化する素晴しい方法であり,バグの原因を同定する手段としても有用である.
:step
コマンドを使うと,プログラム中の全てのブレークポイントが有効にされ、次のブレークポイントに達するまで実行される.
:steplocal
とすれば,現在のトップレベル関数の中にあるブレークポイントのみ有効になります.
同様に :stepmodule
とすると現在のモジュール中にあるブレークポイントのみ有効にします.
たとえば以下のようになる.
*Main> :step main
Stopped in Main.main, qsort.hs:5:8-48
_result :: IO () = _
:step expr
コマンドは⟨expr⟩のステップ実行を開始します.
⟨expr⟩ が省略されたときは,現在のブレイクポイントからステップ実行します.
:steplocal
および :stepmodule
も同様に動作します.
ステップ実行中は :list
コマンドが特に便利で,いまどこを実行しているかが判ります.
[qsort.hs:5:8-48] *Main> :list
4
5 main = print (qsort [8, 4, 0, 3, 1, 23, 11, 18])
6
[qsort.hs:5:8-48] *Main>
実は,GHCiにはブレイクポイントにきたときにコマンドを実行するという機能があり,
自動的に :list
するようにできます.
[qsort.hs:5:8-48] *Main> :set stop :list
[qsort.hs:5:8-48] *Main> :step
Stopped in Main.main, qsort.hs:5:15-47
_result :: [Integer] = _
4
5 main = print (qsort [8, 4, 0, 3, 1, 23, 11, 18])
6
[qsort.hs:5:15-47] *Main>
5.5.3. ブレイクポイントのネスト¶
GHCi がブレイクポイントで停止したとき,プロンプトに式を入力すると,次のブレイクポイントまで進みます. この新しいブレイクポイントが現在のブレイクポイントとなり,古いブレイクポイントはスタックに保存されます. 任意の数のブレイクポイント文脈はこうして作られます. 以下はその例です.
[qsort.hs:2:16-47] *Main> :step qsort [1,3]
Stopped in Main.qsort, qsort.hs:2:16-47
_result :: [t] = _
a :: t = _
left :: [t] = _
right :: [t] = _
... [qsort.hs:2:16-47] *Main>
前に設定した2行目のブレークポイントで停止したところで :step qsort [1,3]
として新しい評価を開始したしました.
この新しい評価は1ステップの後に(qsort
の定義で)停止しました.
ここで,プロンプトが変わって先頭に ...
が付きます.
これが現在のブレークポイントの他に保存されたブレークポイントがあることを示しています.
この文脈スタックを見るには :show context
を使えばいいでしょう.
... [qsort.hs:2:16-47] *Main> :show context
--> main
Stopped in Main.qsort, qsort.hs:2:16-47
--> qsort [1,3]
Stopped in Main.qsort, qsort.hs:2:16-47
... [qsort.hs:2:16-47] *Main>
現在の評価の結果を捨てるには :abandon
: を使います.
... [qsort.hs:2:16-47] *Main> :abandon
[qsort.hs:2:16-47] *Main> :abandon
*Main>
5.5.4. _result
変数¶
ブレークポイントで停止したときやステップ実行するとき,GHCiは _result
という変数を用意して,
現在注目されている式の結果に束縛します.
_result
の値はおそらくまだ存在しない(その評価を止めたので)が,
その評価を強制することはできます.
その型が既知で表示できるなら _result
とプロンプトに入力するだけで表示できます.
ただし,警告が一つあります.
_result
を評価すると,別のブレークポイントにあたる可能性が高いということです.
特に :step
ではなく真のブレークポイントで停止していたら,このブレークポイントに最初にあたります.
このため _result
を評価する時には,即座に :continue
を発行しなければならなく可能性が高くなります.
別の方法としては :force
はブレークポイントを無視するので,これを使うこともできます.
5.5.5. トレースとヒストリ¶
プログラムをデバッグしている時によく「どうやってここに来たの?」と思うことがあります. 伝統的な命令的デバッガは通常なにがしかのスタックトレース機能を持っていて, それを使ってアクティブな関数呼び出しのスタック(「レキシカルコールスタック」と呼ばれることも)を 確認できます. このスタックによって,現在の位置に至るまでのコード上の道のりが判ります. 残念ながら,これをHaskellで用意するのは難しいのです. 正格な言語と違って,実行は深さ優先ではなく必要に応じて進むからです. GHCの実行エンジンにある「スタック」は字句的呼び出しスタックとは大きく異なります. 理想的には,GHCがこの動的な呼び出しスタックに加えて,字句的呼び出しスタックを管理すれば良く, 実はプロファイルシステムはまさにこれを行っています(Profiling). 他のHaskellデバッガにもこれをしているものがある. しかし,現在のところGHCiは字句的呼び出しスタックを管理していません (克服せねばならない技術的困難がいくつかあります). 代わりにブレークポイントから直前の評価ステップに戻る方法を用意しています. これは要するにステップ実行を逆向きにするのと同じです. 多くの場合「どうやってここに来たの?」という疑問を解決するのに十分な情報がえられます.
トレース機能を使うには,式を :trace
コマンドで評価します.
たとえば qsort
のベースケースにブレイクポイントを設定します.
*Main> :list qsort
1 qsort [] = []
2 qsort (a:as) = qsort left ++ [a] ++ qsort right
*Main> :break 1
Breakpoint 0 activated at qsort.hs:1:12-13
*Main>
こうしておいて,小さいデータに qsort
をトレース付きで実行します.
*Main> :trace qsort [3,2,1]
Stopped in Main.qsort, qsort.hs:1:12-13
_result :: [a] = _
[qsort.hs:1:12-13] *Main>
これで評価ステップのヒストリを確認できます.
[qsort.hs:1:12-13] *Main> :history
-1 : qsort:(...) (qsort.hs:3:25-39)
-2 : qsort:(...) (qsort.hs:3:24-56)
-3 : qsort (qsort.hs:2:16-25)
-4 : qsort (qsort.hs:2:16-47)
-5 : qsort:(...) (qsort.hs:3:25-39)
-6 : qsort:(...) (qsort.hs:3:24-56)
-7 : qsort (qsort.hs:2:16-25)
-8 : qsort (qsort.hs:2:16-47)
-9 : qsort:(...) (qsort.hs:3:25-39)
-10 : qsort:(...) (qsort.hs:3:24-56)
-11 : qsort (qsort.hs:2:16-25)
-12 : qsort (qsort.hs:2:16-47)
<end of history>
ヒストリ中の特定のステップを調べるには :back
を使います.
[qsort.hs:1:12-13] *Main> :back
Logged breakpoint at qsort.hs:3:25-39
_result :: [Integer]
a :: Integer
as :: [Integer]
[-1: qsort.hs:3:25-39] *Main>
ヒストリ中の各ステップにおけるローカル変数は保存されており,
通常と同じように値を調べられるということに注目してください.
さらに,プロンプトが変わって -1
のように,ヒストリの最初のステップを調べていることを示しています.
:forward
コマンドを使えば,ヒストリを前方にたどれます.
:trace
コマンドには式を与えても与えなくてもかまいません.
式を与えなかった場合は :step
と同様,現在のブレイクポイントからトレースが始まります.
ヒストリは :trace
を使ったときにしか記録されません.
そのようになっているのは,全てのブレイクポイントをヒストリに記録することで性能が2倍以上に悪化することが判っているからである.
-
-fghci-hist-size
Default: 50 GHCi で追跡する評価ヒストリの深さを変更する
5.5.6. 例外のデバッグ¶
デバッグの際にもうひとつ思うことは「この例外はどこから来たの?」ということです.
error
や head []
などが引き起こすような例外には文脈情報がついていません.
プログラム中のどの head
呼び出しがエラーになったかを探すのは骨の折れる仕事です.
たいていの場合 Debug.Trace.trace
を仕込むか,プロファイル指定でコンパイルして
Debug.Trace.traceStack
を使うか +RTS -xc
(-xc
参照)を使うかです.
GHCiデバッガはソースコードを書き換えたり,再コンパイルしたりすることなく,
てばやく,この手のエラーに光を当てる方法を提供しています.
ひとつの方法は,ソースコード中で例外を投げる場所にブレークポイントを設定し :trace
と :history
を使って
文脈を把握することです.
しかし head
はライブラリ中にあり,そこに直接ブレークポイントを設定できません.
そういうわけで,GHCiには -fbreak-on-exception
フラグが用意されています.
これを使うと例外が投げられた時に評価器を停止できます.
-fbreak-on-error
も同様ですが,こちらは例外が捕捉されなかった場合のみ停止します.
例外で停止すると,GHCiはちょうどブレークポイントに当ったのと同じように振る舞います.
違うのは,ソースコード中の位置が表示されないということです.
したがって :trace
と組み合わせて,例外が発生する直前までのステップを記録するようにしないと
あまり役には立ちません.
たとえば,以下のようにします.
*Main> :set -fbreak-on-exception
*Main> :trace qsort ("abc" ++ undefined)
"Stopped in <exception thrown>, <unknown>
_exception :: e = _
[<unknown>] *Main> :hist
-1 : qsort:(...) (qsort.hs:3:25-39)
-2 : qsort:(...) (qsort.hs:3:24-56)
-3 : qsort (qsort.hs:2:16-25)
-4 : qsort (qsort.hs:2:16-47)
<end of history>
[<unknown>] *Main> :back
Logged breakpoint at qsort.hs:3:25-39
_result :: [Char]
a :: Char
as :: [Char]
[-1: qsort.hs:3:25-39] *Main> :force as
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err
undefined, called at <interactive>:2:17 in interactive:Ghci1
[-1: qsort.hs:3:25-39] *Main> :print as
as = 'b' : 'c' : (_t1::[Char])
新しい変数 _exception
が例外に束縛されます.
例外発生時にブレークする機能は,プログラムが無限ループしているとき,それが何をしているかを調べるのに特に便利です. Ctrl-C を叩いて,履歴を見て,何が起こっていたかを調べればいいのです.
-
-fbreak-on-exception
-
-fbreak-on-error
GHCiが,例外のイベントで,評価を停止して対話プロンプトに戻るようにします.
-fbreak-on-exception
はすべての例外でブレイクするのに対して,-fbreak-on-error
は捕捉されない例外でのみブレイクします.
5.5.7. 例:関数の調査¶
このデバッガを使って関数値を調べられます. ブレークポイントで停止し,スコープに関数があるとき,デバッガでその関数のソースコードを表示させることはできません. しかし,その関数をいくつかの引数に適用して結果を観察することで,いくらかの情報をえることはできます.
束縛が多相的な場合には,このプロセスはすこし複雑になります.
例で見ましょう.
簡単に考えるために,よく知られた map
関数を例にとります.
import Prelude hiding (map)
map :: (a->b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : map f xs
map
にブレイクポイントを設定して,呼び出してみましょう.
*Main> :break 5
Breakpoint 0 activated at map.hs:5:16-29
*Main> map Just [1..5]
Stopped in Main.map, map.hs:5:16-29
_result :: [b] = _
f :: Integer -> b = _
x :: Integer = 1
xs :: [Integer] = _
[map.hs:5:16-29] *Main>
GHCi の表示を見れば f
がスコープにあることがわかります.
しかし,その型は完全に判明しているわけではありません.
とはいうものの,最初の引数の型を見れば,これが x
の型と同じ Integer
であることも,
_result
の型が同じ Integer
を要素とするリストであることも判ります.
[–ここでは– GHC Users Guide 原文の記述とghciの実際の挙動が異なる部分に関連する説明を省いています.]
以降 f
を Integer
型の任意の引数に適用してその結果を観察できます.
[–ここから– GHC Users Manual 原文の記述とghciの実際の挙動が異なるので,実際の挙動に沿って非公式に説明します.]
[map.hs:5:16-29] *Main> :abandon
*Main> let b = f 10
*Main> :t b
b :: b
*Main> :p b
b = (_t1::b)
*Main> seq b ()
()
*Main> :t b
b :: Maybe Integer
*Main> :t f
f :: Integer -> Maybe Integer
*Main> f 20
Just 20
*Main> :delete *
*Main> map f [1..5]
[Just 1,Just 2,Just 3,Just 4,Just 5]
[–ここまで– 実際の挙動に沿った非公式説明.]
最初の f
の適用では f
の結果の型を復元するために,型の再構築をもう少し行う必要がありました.
しかし,それ以降は f
を通常の方法で自由に使うことができます.
5.5.8. 制限¶
ブレークポイントで停止したとき,既に評価中の変数を評価しようとすると,2回目の評価はハングする. その変数が評価中であることをGHCが知っていて,後の評価は先の結果を待ってからでないと続けられないからです. 先の評価はブレークポイントで停止しているので,もちろん結果は得られません. 評価がハングしたときは Ctrl-C で中断すれば,プロンプトに戻ることができます.
ありがちなのは,CAF(たとえば
main
)を評価していて,ブレークポイントで停止し,そのCAFの値を再びプロンプトで要求するという場合です.暗黙パラメータ(Implicit parameters 参照)がブレイクポイントで利用できるのは,型が明示されている場合だけです.
5.6. GHCi の起動¶
GHCiは ghci
または ghc --interactive
というコマンドで起動します.
1つまたは複数のモジュールやファイル名をコマンド行で指定することもできます.
そうすると,GHCiはプロンプトで :load モジュール名
と入力したとき同じように(GHCi のコマンド群 参照),指定されたモジュールやファイル(と,それらが依存するモジュール)をロードします.
たとえば、GHCiを起動して(Main.hsに最上位モジュールがある)プログラムをロードするには,
次のようにすればよい.
$ ghci Main.hs
GHCが受け付けるコマンドラインオプション(Using GHC 参照)の大部分は対話モードでも有効です. GHCiで有効でないものは見れば判ります.
5.6.1. パッケージ¶
ほとんどのパッケージ(Using Packages 参照)は追加でフラグを指定しなくても利用できます. 最初に必要になったときに自動的ロードされます.
一方で,隠されたパッケージについては -package
フラグを使ってロードを要求する必要があります.
$ ghci -package ghc-8.0.2
GHCi, version 8.0.2: http://www.haskell.org/ghc/ :? for help
Prelude> :show packages
active package flags:
-package ghc-8.0.2
以下のコマンドを使えば起動中のGHCiに新しいパッケージをロードできます.
*Main> :set -package ghc-8.0.2
package flags have changed, resetting and loading new packages...
Prelude>
ただし,すでにロードされているモジュールが全て未ロードになり Prelude
に戻されることになるので注意が必要です.
5.6.2. 追加のライブラリ¶
追加のライブラリは,コマンドラインから通常の -llib
オプションを使って指定できます.
(ここでいうライブラリとは,他言語のオブジェクトコードのライブラリのことです.
Haskellソースのライブラリを使うことについては モジュールとファイル名 を参照してください.)
たとえば,「m」ライブラリをロードするには,次のようにします.
$ ghci -lm
.so
-形式の共有ライブラリを使うシステムでは,実際にロードされるライブラリは liblib.so
です.
GHCiは以下の順にライブラリを探します.
-L
コマンドラインオプションで指定したパス.- システムの標準ライブラリ検索パス.これはシステムによっては環境変数
LD_LIBRARY_PATH
を設定することで変更可能です.
.dll
-形式の共有ライブラリを使うシステムでは,実際にロードされるライブラリは lib.dll
です.
GHCi指定のライブラリが見つからなければ,ここでもエラーになります.
GHCi は単なるオブジェクトファイル(プラットフォームによって .o
か .obj
のどちらか)をコマンドラインで指定してロードできます.オブジェクトファイル名をコマンドラインに追加するだけです.
-l
オプションの順序は重要です.ライブラリ指定は,そのライブラリが依存しているライブラリよりも 前に 書いておく必要があります(Options affecting linking 参照).
5.7. GHCi のコマンド群¶
GHCi のコマンドはすべて「 :
」ではじまり,1つのコマンド名と0個以上のパラメータからなります.
コマンド名は短縮可能です.
短縮の結果曖昧になった場合は,よりよく使われるコマンドを優先します.
-
:abandon
¶ 現在の評価を破棄します(これはブレイクポイントで停止しているときのみ有効).
-
:add[*] ⟨module⟩
¶ ⟨module⟩ を現在のターゲット集合に追加しリロードを実行します. 通常,可能ならそのモジュールのコンパイル済みコードをロードし,そうでなければ,そのモジュールはバイトコードにコンパイルします.
*
接頭辞を使えば,強制的にバイトコードとしてロードできます.
-
:all-types
¶ (
:set +c
をアクティブにしておいてからロードされた)現在ロードしている式や(局所)束縛の型を収集したものを一覧にします. このとき,それぞれの式の始点と終点を含むソースコード上の位置情報が同時に表示します. 以下はその表示例です.GhciTypes> :all-types GhciTypes.hs:(38,13)-(38,24): Maybe Id GhciTypes.hs:(45,10)-(45,29): Outputable SpanInfo GhciTypes.hs:(45,10)-(45,29): (Rational -> SpanInfo -> SDoc) -> Outputable SpanInfo
-
:back ⟨n⟩
¶ 履歴を ⟨n⟩ ステップ戻ります.⟨n⟩ の指定が省略されれば,1ステップ戻ります. GHCi のデバッグ機能に関してより詳しくは トレースとヒストリ ,
:trace
,:history
,:forward
などを参照してください.
-
:break [⟨identifier⟩ | [⟨module⟩] ⟨line⟩ [⟨column⟩]]
¶ 関数あるいは行を指定してそこにブレイクポイントを設定します. ブレイクポイントの設定 を参照してください.
-
:browse[!] [[*] ⟨module⟩]
¶ ⟨module⟩ からエクスポートされている識別子を表示します. ⟨module⟩ はGHCiにロードされているか,パッケージの要素でなければなりません. ⟨module⟩ が省略されれば,直近にロードされたモジュールを使います.
他のGHCiのコマンドと同様,出力は現在のGHCiのスコープ(プロンプトのスコープにあるもの)で表示されます.
browseコマンドには2つの変種があります.
*
記号がモジュール名の前に置くと,その⟨module⟩のスコープにある すべての 識別子が(エクスポートされたものだけではなく)表示されます.*
-形式の指定は解釈実行するモジュールにのみ有効です. コンパイル済みモジュール(パッケージ由来のものを含めて)については:browse
は*
-形式ではない指定のみ有効です.データ構成子とクラスメソッドは通常そのデータ型あるいはクラス宣言の文脈で表示されます. しかし
!
記号をコマンドの後に付けると,すなわち:browse!
のようにすると個別の一覧表示になります.!
-形式にするとコメントで各エントリーグループごとにコメント付きで表示します. 以下はその例です.Prelude> :browse! Data.Maybe -- not currently imported Data.Maybe.catMaybes :: [Maybe a] -> [a] Data.Maybe.fromJust :: Maybe a -> a Data.Maybe.fromMaybe :: a -> Maybe a -> a Data.Maybe.isJust :: Maybe a -> Bool Data.Maybe.isNothing :: Maybe a -> Bool Data.Maybe.listToMaybe :: [a] -> Maybe a Data.Maybe.mapMaybe :: (a -> Maybe b) -> [a] -> [b] Data.Maybe.maybeToList :: Maybe a -> [a] -- imported via Prelude maybe :: b -> (a -> b) -> Maybe a -> b Just :: a -> Maybe a data Maybe a = Nothing | Just a Nothing :: Maybe a
この出力は現在のセッションの文脈(すなわち
Prelude
の有効範囲)で, 最初のData.Maybe
由来の項目グループは有効範囲にないことを示しています (ただし,GHCiのセッションでは完全修飾すれば,これらの項目は利用可能です. プロンプトのスコープにあるもの を参照). しかし,2つめの項目グループは(Prelude
経由で)有効範囲内にあり, 修飾しなくても,あるいはPrelude.
という修飾をつけて使えるということを示しています.
-
:cd ⟨dir⟩
¶ 現在の作業ディレクトリを ⟨dir⟩ に変更します.”
~
“記号を ⟨dir⟩ の先頭に付ければ この記号は環境変数HOME
の内容に置き換ります. 現在の作業ディレクトリを表示する:show paths
コマンドを参照してください.注意: 現在の作業ディレクトリを変更すると,現在ロードされているモジュールはすべてアンロードされます. このようになっている理由は,検索パスは相対ディレクトリで表現されるのが普通で, セッション途中での検索パス変更はサポートされていないからです.
-
:cmd ⟨expr⟩
¶ ⟨expr⟩ を
IO String
型のコンピュテーションを実行し, 実行結果の文字列をGHCiのコマンドのリストとして実行します. 複数のコマンドは改行で区切られます.:cmd
コマンドは:def
や:set stop
とともに使うのが便利です.
-
:complete ⟨type⟩ [⟨n⟩-][⟨m⟩] ⟨string-literal⟩
¶ このコマンドを使えば,端末からではなくパイプでGHCiと繋いているときでも,コマンド補完を要求できます. この機能はGHCiの補完をテキストエディタやIDEに統合するために設計してあります.
:complete
は ⟨n⟩ 番目から ⟨m⟩ 番目までの補完候補を表示します. 補完の対象となるのは ⟨type⟩ で指定された補完ドメインの部分入力 ⟨string-literal⟩ です. 現時点ではrepl
ドメインのみサポートしています. このドメインはGHCiの入力プロンプトでのみ対話的に提供するものです.⟨n⟩ および ⟨m⟩ は省略するとデフォルトでそれぞれ最初の補完候補および最後の補完候補を指します. 引数で指定した範囲よりも実際にその範囲にある候補数が小さい場合は,⟨n⟩ および ⟨m⟩ は暗黙に実際の候補数に丸められます.
:complete
の出力は,空白で区切られた以下の3つのフィールドを含むヘッダ行で始まります.- 表示される候補の数
l
を表す整数 - 利用可能な全ての補完の数を表す整数
- 返される補完後方に付加される共通接頭辞を表す文字リテラル
ヘッダ行のあとに ⟨l⟩ 行が続き,各行は1つの補完候補が引用符付きの文字列リテラルとしてエンコードされています. いろいろな場合の起動例を以下に示しましょう.
Prelude> :complete repl 0 "" 0 506 "" Prelude> :complete repl 5 "import For" 5 21 "import " "Foreign" "Foreign.C" "Foreign.C.Error" "Foreign.C.String" "Foreign.C.Types" Prelude> :complete repl 5-10 "import For" 6 21 "import " "Foreign.C.Types" "Foreign.Concurrent" "Foreign.ForeignPtr" "Foreign.ForeignPtr.Safe" "Foreign.ForeignPtr.Unsafe" "Foreign.Marshal" Prelude> :complete repl 20- "import For" 2 21 "import " "Foreign.StablePtr" "Foreign.Storable" Prelude> :complete repl "map" 4 4 "" "map" "mapM" "mapM_" "mappend"
- 表示される候補の数
-
:continue
¶ ブレイクポイントで停止しているとき,現在の評価を再開します.
:ctags
はVi系エディタ用タグファイルを生成します.:etags
はEmacs系エディタ用です. ⟨filename⟩ を指定しなかった場合は,デフォルトでそれぞれtags
あるいはTAGS
をファイル名として使います. 現在ロードしているモジュール内のすべての関数,構成子,型のタグを生成します. これらのコマンドが有効に働くためにはすべてのモジュールが解釈実行されているものでなければなりません.
-
:def[!] ⟨name⟩ ⟨expr⟩
¶ :def
はGHCi内で新しいコマンド(あるいはマクロ)を定義するのに使います.:def ⟨name⟩ ⟨expr⟩
というコマンドはHaskellの式 ⟨expr⟩ で実装した新しいGHCiコマンド:name
を定義します. この式 ⟨expr⟩ の型はString -> IO String
でなくてはなりません. GHCiのプロンプトで:name args
を入力すると,式(name args)
が実行されます. その結果のString
をとり,それを再度GHCiにコマンド列として戻します. 結果のコマンド文字列は “\n
” で区切られていなければなりません.これは少々ややこしいので,例をいくつか挙げましょう. まず,次に示す新しいコマンドは引数をとらず,結果も生成せず,単に現在の日時を出力するだけのものです.
Prelude> import Data.Time Prelude Data.Time> let date _ = getCurrentTime >>= putStrLn . formatTime defaultTimeLocale rfc822DateFormat >> return "" Prelude Data.Time> :def date date Prelude Data.Time> :date Thu, 23 Mar 2017 06:21:10 UTC
次は,引数を1つとるコマンドの例です. これは
:cd
: コマンドの再実装です.Prelude> import System.Directory Prelude System.Directory> let mycd d = setCurrentDirectory d >> return "" Prelude System.Directory> :def mycd mycd Prelude System.Directory> :mycd ..
あるいは,現在のディレクトリで「
ghc -o foo Main
」を起動する単純な方法を定義できます.Prelude> :def make (\_ -> return ":! ghc -o foo Main")
GHCiへの入力をファイルから読み込むコマンドを定義することもできます. これは,あらかじえ決った束縛を繰り返しGHCiセッションにロードしたいとうとき便利でしょう.
Prelude> :def . readFile Prelude> :. cmds.ghci
このコマンドを
:.
としたのは同じことを Unix シェルの「.
」とのアナロジーです.:def
を単独で入力すると,現在定義されているマクロの一覧が表示されます. 既に存在するコマンド名を再定義しようとするとエラーになりますが,:def!
のように感嘆符をつけると古い方は黙って上書きされます.
-
:delete * | ⟨num⟩ ...
¶ 1つ以上のブレイクポイントを番号で指定して削除します. (それぞれのブレイクポイントの番号を知るには
:show breaks
を使います.)*
を使えば,すべてのブレイクポイントが削除されます.
-
:edit ⟨file⟩
¶ エディタを開いてファイル ⟨file⟩ を編集します. ⟨file⟩ を省略した場合は直近にロードされたモジュールを編集します. 直近のロードでエラーが起きたときは,カーソルが最初のエラーが起きた箇所に置かれます. エディタは,環境変数
EDITOR
で指定されるものか,環境変数の設定がない場合はシステムのデフォルトエディタが起動します 使用するエディタは:set editor
を使って変更できます.
:ctags
を参照してください.
-
:force ⟨identifier⟩ ...
¶ :print
と同じように ⟨identifier⟩ の値を表示します.:print
とは違い:force
は値をたどっていく過程で出会ったサンクを評価しますので, 例外あるいは無限ループが発生したり,次のブレイクポイントに当たる(これは無視しますが,その旨を表示する)ことがあります.
-
:forward ⟨n⟩
¶ 履歴を ⟨n⟩ ステップ前進します. ⟨n⟩ の指定が省略されれば,1ステップ前進します. GHCi のデバッグ機能に関してより詳しくは トレースとヒストリ ,
:trace
,:history
,:back
を参照してください.
-
:
¶ 直近のコマンドを反復します.
-
:history [num]
¶ 評価ステップの履歴を表示します.数を指定すると,その数ぶんのステップを表示します(デフォルトは20).
:trace
(トレースとヒストリ 参照)と組み合わせて使います. GHCi が保存する履歴のエントリー数を設定するには-fghci-hist-size
フラグを使います.
-
:info[!] ⟨name⟩
¶ 与えられた名前についての情報を表示します. たとえば, ⟨name⟩ がクラスなら,そのクラスのメソッドとその型が表示される. ⟨name⟩ が型構成子の場合はその定義が表示され,関数なら型が表示されます. また ⟨name⟩ がソースファイルからロードさればものなら,GHCiはその定義のソースコードの位置も表示します.
型およびクラスについては、それに言及するインスタンスもまとめて表示します. 無関係な情報が表示されることがないよう,インスタンスは,(a)その頭部が ⟨name⟩ に言及しており, (b)そのインスタンスで言及されているものが全て
:load
あるいは:module
コマンドの結果として(修飾されているかいないかにかかわらず)スコープにある場合にのみ表示されます.:info!
コマンドも同様に動作しますが,(b)の制限はなく ⟨name⟩ に言及するインスタンスでスコープにあるものを全て表示します.
-
:issafe [⟨module⟩]
¶ 与えられたモジュール(省略された場合は現在のモジュール)に間する Safe Haskell 情報を表示します. モジュールとそれを含むパッケージの信頼のタイプの情報が表示されます.
-
:kind[!] ⟨type⟩
¶ ⟨type⟩ のカインドを推論し表示します. ⟨type⟩ は任意の型式で
Either Int
のような型構成子の部分適用であってもかまいません. 実は:kind
では通常はできない型シノニムの部分適用が書けるので以下のようなことができます.ghci> type T a b = (a,b,a) ghci> :k T Int Bool T Int Bool :: * ghci> :k T T :: * -> * -> * ghci> :k T Int T Int :: * -> *
追加で「
!
」を指定すると GHCi は型関数適用を評価しその結果を表示します.
-
:list ⟨identifier⟩
¶ ⟨identifier⟩ が与えられればその定義周辺を表示し,与えられなければ現在のブレイクポイント周辺を表示します. この識別子は解釈実行するモジュールで定義されている必要があります. 出力デバイスが対応していれば,GHCiは注目している部分式を太字で強調表示します.
-
:list [⟨module⟩] ⟨line⟩
¶ ⟨module⟩ の与えられた行番号周辺のソースコードを表示します. このモジュールは解釈実行するものでなければなりません. 出力デバイスが対応していれば,GHCiは注目している部分式を太字で強調表示します.
-
:load[!] [*]⟨module⟩
¶ 指定した ⟨module⟩ および,それが依存するすべてのモジュールを再帰的にロードします. 個々の ⟨module⟩ はモジュール名またはファイル名でなければならない. また,パッケージ内のモジュールの名前を指定することはできません.
以前にロードだれていたモジュールは,パッケージ中のものを除いて,忘れられてしまいます. この新しいモジュールの集合をターゲット集合といいます.
:load
を引数なしで使うと,現在ロードされているモジュールおよび束縛を全て未ロード状態にできることに注意してください.通常可能ならばコンパイル済みのコードをロードしますが,そうでなければ,そのモジュールはバイトコードにコンパイルします. 接頭辞
*
を使えば,強制的にモジュールをバイトコードとしてロードします.「
!
」を追加するとロード中の型エラーが警告になります. こうすることで,型エラーになる定義を含むモジュールであっても,正しい部分だけを使うことができます. 実際には,ロード前に「-fdefer-type-errors」フラグを設定して, ロード以前にそのフラグが設定されていなければ,ロード後このフラグを未設定にもどしています. 動機と詳細については Deferring type errors to runtime を参照してください.:load
コマンドを発行後,現在の文脈は以下のようになります.- ⟨module⟩ のロードが成功したらその ⟨module⟩.
- そうでなければ,今回の
:load
コマンドの結果ロードが成功した他のモジュールがあればそのモジュール. - そうでなければ
Prelude
-
:loc-at ⟨module⟩ ⟨line⟩ ⟨col⟩ ⟨end-line⟩ ⟨end-col⟩ [⟨name⟩]
¶ 与えられたソースコードの範囲にある ⟨name⟩ の定義場所を探します. 以下はその例です.
X> :loc-at X.hs 6 14 6 16 mu X.hs:(8,7)-(8,9)
このコマンドはGHCiとテキストエディタやIDEを統合したときに,定義位置へ移動する機能を提供するのに使えます.
:loc-at
コマンドを使うためには:set +c
を設定しておく必要があります.
-
:main ⟨arg1⟩ ... ⟨argn⟩
¶ プログラムをコンパイルし実行するときに
getArgs
を使ってコマンドライン引数にアクセスできます. しかし,GHCiでテストしているときにはmain
にコマンドライン引数を簡単には渡せません. それはmain
は直接引数を取らないからです.そこで
:main
コマンドを使います. このコマンドは有効範囲にあるmain
をとにかく実行し,任意の引数をコマンドライン引数とします. 以下がその実行例です.Prelude> main = System.Environment.getArgs >>= print Prelude> :main foo bar ["foo","bar"]
2重引用符を使うことで,スペースを含む引数を扱えます. 引数はHaskellの文字列のリストになり,Haskell内でそのように処理できます.
Prelude> :main foo "bar baz" ["foo","bar baz"] Prelude> :main ["foo", "bar baz"] ["foo","bar baz"]
-main-is
フラグを設定するか:run
コマンドを使えばmain
以外も実行できます.Prelude> foo = putStrLn "foo" >> System.Environment.getArgs >>= print Prelude> bar = putStrLn "bar" >> System.Environment.getArgs >>= print Prelude> :set -main-is foo Prelude> :main foo "bar baz" foo ["foo","bar baz"] Prelude> :run bar ["foo", "bar baz"] bar ["foo","bar baz"]
-
:module +|- [*]⟨mod1⟩ ...
¶
-
import ⟨mod⟩
¶ プロンプトに入力する文(statement)用の文脈を設定または変更します.
import mod
の形式は:module +mod
と同等です. 詳しいことについては プロンプトのスコープにあるもの を参照してください.
-
:print ⟨names⟩
¶ 評価を強制することなく値を表示します.
:print
は型が不明もしくは部分的にしか判明していない値についても使用できます. ブレイクポイントにおける,多相型の局所変数がこれにあたります.:print
は実行時の値を調査しつつ,その値を型を再構成しようとします. そして可能であれば型を精密にしようとします. 未評価の部分(すなわちサンク)に出会うと:print
は_t
で始まる名前の新しい変数をそれぞれのサンクに束縛します. 詳しくは ブレイクポイントと変数内容の表示 を参照してください.:sprint
という新しい変数は束縛しませんが,それ以外は同じコマンドも参照してください.
-
:quit
¶ GHCiを終了します.プロンプトで
Control-D
をタイプしても終了できます.
-
:reload[!]
¶ 現在のターゲット集合(
:load
参照)とそれらが依存するモジュールのうち, 変更のあったものがあれば,ターゲット集合を再ロードしようと試みます. 結果として,新しいモジュールがロードされたり,ターゲットから間接的に必要とされなくなったモジュールが外れたりする可能性がある ことに注意してください.「
!
」を追加するとロード中の型エラーが警告になります. こうすることで,型エラーになる定義を含むモジュールであっても,正しい部分だけを使うことができます. 実際には,ロード前に「-fdefer-type-errors」フラグを設定して, ロード以前にそのフラグが設定されていなければ,ロード後このフラグを未設定にもどしています. 動機と詳細については Deferring type errors to runtime を参照してください.
-
:set [⟨option⟩ ...]
¶ さまざまなオプションを設定します. 利用可能なオプション一覧については :set コマンドと :seti コマンド を参照して下さい. また,GHCi固有のフラグ一覧については Interactive-mode options を参照してください.
:set
コマンドを単独で使うと,現在設定されているオプションが表示されます. また,それとは分けて,動的フラグの設定状況も一覧表示します.
-
:set args ⟨arg⟩
¶ プログラムが
System.getArgs
を呼んだときに返される引数のリストを設定します.
-
:set prog ⟨prog⟩
¶ プログラムが
System.getProgName
を呼んだときに返される文字列を設定します.
-
:set prompt ⟨prompt⟩
¶ GHCiのプロンプトとして使う文字列を設定します. ⟨prompt⟩ の中では
%s
という並びは現在のスコープにあるモジュール名に置き換え,%l
は現在のプロンプトの行番号(コンパイラメッセージで参照されるもの)に置き換え,%%
は%
に置き換えます. ⟨prompt⟩ が"
で始まる場合,HaskellのString
としてパースします. そうでない場合はそのまま文字列として扱います.
-
:set prompt2 ⟨prompt⟩
¶ (
:{
コマンドを使うときに使う)GHCiの継続プロンプトとして使う文字列を設定します.
-
:set stop ⟨num⟩ ⟨cmd⟩
¶ ブレイクポイントに当たったとき,または履歴中で新しい項目が選択したときに実行するコマンドを設定します.
:set stop
の最もよくある使い方は,現在の位置のソースコードを表示するこです. たとえば:set stop :list
のようにします.コマンドの前に数値を指定すると,その番号のブレイクポイントにあたったときにだけそのコマンドを実行します. これは便利な機能です.たとえば
:set stop 1 :continue
は1番のブレイクポイントを無効にするのと同じことです. 1番のブレイクポイントに当ったときはいつも:continue
が実行されるからです (ただし,ブレイクポイントに当ったとのメッセージは出力されます). さらに:def
と:cmd
をうまく使って:set stop
で条件付ブレイクポイントを実装することもできます.*Main> :def cond \expr -> return (":cmd if (" ++ expr ++ ") then return \"\" else return \":continue\"") *Main> :set stop 0 :cond (x < 3)
同様の技法を使えば,指定した回数だけブレイクポイントを無視することもできます.
-
:seti [⟨option⟩ ...]
¶ :set
と似ていますが:seti
で設定されたオプションはプロンプトに入力された式とコマンドのみに影響し,:load
でロードされたモジュールには影響しません (対照的に:set
で設定されたオプションはあらゆるところで適用されます). 対話式評価についてのみのオプションを設定する を参照してください.引数がなければ,プロンプトに入力される式とコマンドに適用されるオプションの集合を表示します.
-
:show bindings
¶ プロンプトで導入した束縛とその型を表示します.
-
:show breaks
¶ 現在有効なブレイクポイントを一覧表示します.
-
:show context
¶ ブレイクポイントで停止している,有効な評価の一覧を表示します.
-
:show modules
¶ 現在ロードされているモジュールの一覧を表示する.
-
:show packages
¶ 現在ロードされているパッケージの一覧と現在有効なパッケージフラグを表示します.
-
:show language
¶ ソースファイルに対して現在有効になっている言語フラグを表示する.
-
:step [⟨expr⟩]
¶ すべてのブレイクポイントを有効にして,式を単一ステップモードでの評価を開始します. このモードでは,簡約1ステップごとに評価が停止し局所変数を確認できます. ⟨expr⟩ が与えなかった場合は,最後のブレイクポイントから評価を開始します. ステップ実行 を参照してください.
-
:steplocal
¶ 現在の最上位束縛の中にあるブレイクポイントのみを有効にした状態で最後のブレイクポイントから評価を再開します.
-
:stepmodule
¶ 現在のモジュール内にあるブレイクポイントのみを有効にした状態で,最後のブレイクポイントから評価を再開します.
-
:trace ⟨expr⟩
¶ 与えられた式を評価(式を与えられなかった場合は直近のブレークポイントから再開)しますが, 後で
:history
で観察できるように評価ステップのログを残します. トレースとヒストリ を参照してください.
-
:type ⟨expression⟩
¶ ⟨expression⟩ の型を推論し表示します. 多相型には明示的な全称量化が加えられます. 推論に際して,単相制限は 適用されません .
-
:type-at ⟨module⟩ ⟨line⟩ ⟨col⟩ ⟨end-line⟩ ⟨end-col⟩ [⟨name⟩]
¶ 当該モジュール内で与えられた範囲の型の推論結果を報告表示します.
*X> :type-at X.hs 6 6 6 7 f Int -> Int
このコマンドはGHCiとテキストエディタあるいはIDEを統合するさいに指定場所の型を示す機能として使えます.
最後の文字列パラメータはその範囲がすでに変更済みの場合,たとえば,ファイルが変更されコードが移動した場合に役に立ちます. 検索と同じように
:type-at
はより一般的な:type
にフォールバックします.
-
:unset ⟨option⟩
¶ ある種のオプションを未設定にします.利用可能なオプション一覧については :set コマンドと :seti コマンド を参照してください.
-
:uses ⟨module⟩ ⟨line⟩ ⟨col⟩ ⟨end-line⟩ ⟨end-col⟩ [⟨name⟩]
¶ 指定したモジュール中の与えられた位置にあるものの当該モジュール内での使われている位置を報告します.
:uses GhciFind.hs 53 66 53 70 name GhciFind.hs:(46,25)-(46,29) GhciFind.hs:(47,37)-(47,41) GhciFind.hs:(53,66)-(53,70) GhciFind.hs:(57,62)-(57,66)
このコマンドは,エディタやIDEで指定した識別子をハイライト表示して使用場所を示すのに便利です.
-
:! ⟨command⟩
¶ シェルコマンド ⟨command⟩ を実行します.
5.8. :set
コマンドと :seti
コマンド¶
:set
コマンドでは2種類のオプションを設定できます.
「 +
」で始まるオプションと「 -
」で始まる「コマンドライン」オプションです.
Note
現在のところ :set
コマンドは引数における引用符の使用を一切サポートしていません.
引用符は削除されませんし,複数の単語を一つにまとめるのに使うこともできません.
たとえば :set -DFOO='BAR BAZ'
は期待した通りには動かないでしょう.
5.8.1. GHCi のオプション¶
GHCi のオプションは :set
で有効 :unset
で無効にできます.
利用可能な GHCi オプションは以下のとおりです.
-
:set +c
¶ モジュールをロードした後に型と位置情報を収集します.
:all-types
,:loc-at
,:type-at
,:uses
の各コマンドを使うには+c
が有効になっていなければなりません.
-
:set +r
¶ 通常,ロードしたモジュールにあるトップレベルの式(CAFあるいは定数適用形式ともいう) を評価した結果は,複数回の評価をまたがって保持されます.
+r
を有効にすると,トップレベルの式の評価結果は評価が終了するごとに捨てるようになります (それでも1回の評価の 間は 保持されます).このオプションは,評価済みのトップレベル式が大量のメモリを消費するときや,再現性のある実行性能を計測したいときに便利です.
-
:set +s
¶ 式を1つ評価するごとに,経過時間や確保されたバイト数の統計情報を表示します. 注意: 確保されたバイト数はGC毎に計算されるので,記憶領域管理器の確保領域の大きさ程度の精度しかありません. そういうわけ,GCが起らなかったら,値として0が表示されることもあります.
-
:set +t
¶ プロンプトに文を入力したとき,束縛された変数それぞれの型を表示します. 入力されたのが単一の式なら,束縛されるのは変数
it
だけです.
5.8.2. GHCiからGHCのコマンドラインオプションを設定する¶
通常のGHCのコマンドラインオプションを :set
を使って設定することもできます.
たとえば -Wmissing-signatures
を有効にするには以下のようにします.
Prelude> :set -Wmissing-signatures
GHCのコマンドラインオプションのうち,動的なオプション(Flag reference にある一覧表を参照してください)
として設計されているものは :set
を使って有効にすることができます.
オプションを無効にするには,逆の効果を持つオプションを有効すればできます.
Prelude> :set -Wno-incomplete-patterns -XNoMultiParamTypeClasses
Flag reference には,可能なオプション全てについて逆の効果を持つオプションが一覧してあります.
いくつかの静的なオプション(特に -package
, -I
, -i
, -l
)も使えるが,次にリロードするまで効果を発揮しないものもある.
5.8.3. 対話式評価についてのみのオプションを設定する¶
GHCiは実は 2つ のオプション集合を保持しています.
- モジュールのロード時に適用する ロード時オプション
- GHCiのプロンプトに入力された式やコマンドを評価するときに適用する 対話時オプション
:set
コマンドは両方を変更しますが
:seti
コマンドは(“set interactive”の略)は対話時オプションにのみ影響します.
このコマンドは,ロード済みモジュールには適用することなく,対話時オプションを変更するのに便利です. たとえば,
:seti -XMonoLocalBinds
-XMonoLocalBinds
がロード済みモジュールに適用されてしまうと
コンパイルエラーが起こるので望ましくありません.
もっとよくあるのは,フラグが変ったのでモジュールの再コンパイルが必要とGHCが発生するというものです.
.ghci
ファイルで言語オプション設定する場合は,本当にGHCiにロードするモジュール全てに適用したい
というわけでない限り :set
ではなく :seti
を使うのが良い習慣です.
この2つのオプションの集合は :set
と :seti
を引数なしで使うことでそれぞれ確認できます.
たとえば,まっさらなGHCiセッションでは次のような表示がでるでしょう.
Prelude> :seti
base language is: Haskell2010
with the following modifiers:
-XNoMonomorphismRestriction
-XNoDatatypeContexts
-XNondecreasingIndentation
-XExtendedDefaultRules
GHCi-specific dynamic flag settings:
other dynamic, non-language, flag settings:
-fimplicit-import-qualified
warning settings:
2つのオプション集合は以下のように初期化します. まず .ghci ファイルと .haskeline ファイル にあるように両方のオプション集合を初期化します. その後,対話時オプションを以下のように変更します.
-XExtendedDefaultRules
を有効にし,これは特殊デフォルト化ルールをプロンプトに入力された式に適用するためです (GHCi でのデフォルト型設定 参照).- 単相性制限を無効にする(Switching off the dreaded Monomorphism Restriction 参照).
5.9. .ghci
ファイルと .haskeline
ファイル¶
5.9.1. .ghci
ファイル¶
GHCiは起動するとき -ignore-dot-ghci
フラグが指定されていなければ,以下の各ファイルを順に探し,
存在すれば,そのファイルに書かれているコマンドを読み込み実行します.
./.ghci
appdata/ghc/ghci.conf
ただし ⟨appdata⟩ はシステムに依存します. 通常はC:Documents and SettingsuserApplication Data
のようなところです.- Unixでは
$HOME/.ghc/ghci.conf
$HOME/.ghci
ghci.conf
ファイルは,お気に入りのオプション(たとえば :set +s
)を有効にしたり便利なマクロを定義するのに向いています.
Note
このファイルで言語オプションを設定する場合は,通常は :seti
のほうが
:set
より適しています(対話式評価についてのみのオプションを設定する 参照).
.ghci
ファイルをHaskellプロジェクトのディレクトリに置いて,プロジェクトで使う共通オプションを設定するようにしておくと,
GHCiを立ち上げるたびにそれを打ち込まなくて済むので便利です.
たとえば,プロジェクトが多引数の型クラスとスコープのある型変数とCPPを使い,ソースファイルがA,B,Cという3つのサブディレクトリに置いているなら,
次のような行を .ghci
: に書くことになるでしょう.
:set -XMultiParamTypeClasses -XScopedTypeVariables -cpp
:set -iA:B:C
(厳密には -i
フラグは静的オプションですが,このように :set
コマンドでも有効にできます.
ただし,この変更は,次に :load
実行されてはじめて有効になります.)
GHCiのマクロライブラリを持つようになれば,別のファイルから読み込みたくなります.
あるいはデバッグに .ghci
ファイルをGHCiセッションから読み込みたくなります.
そういうときは,
:def source readFile
というマクロを .ghci
ファイルに置いておけば
:source file
とやるだけでGHCiコマンドを file
から読み込めます.
Haskell wikiのページ GHC/GHCi
では,他にも .ghci に関する助言が読めますし,貢献もできます.
さらに,標準ファイルが読み込まれた後に -ghci-script
フラグで指定されたファイルが読み込まれます.
これによって,自分専用の .ghci ファイルが使えます.
どの開始時ファイルを読み込むかを制御するためのコマンドラインオプションが2つあります.
-
-ignore-dot-ghci
開始時に
./.ghci
および他の開始時ファイルを読み込みません.
-
-ghci-script
通常の開始時ファイルを読み込んだ後に,指定したファイルを読み込みます. 複数の入力は指定を繰り返せば可能です.
GHCiマクロを定義する場合,名前が組み込みのコマンドと衝突する可能性があります. そのときの振る舞いについて,特にTAB補完に関して知っておくべきことがあります.
たとえば :time
というマクロを定義したとして,プロンプトで :t 3
とタイプしたとき何が起るべきでしょう.
コマンド補完のアルゴリズムは現在のところ以下のようになっています.
- 定義済みマクロの中から完全一致のものを探索.
- 組み込みコマンド一覧の中から完全一致のものを探索.
- 組み込みコマンド一覧の中から前方一致のものを探索. 該当する組み込みコマンドがあり,さらにそれと同名のマクロも見つかれば,マクロのほうを選択.
- 組み込みコマンド一覧の中から前方一致のものを探索.
- 定義済みマクロの一覧の中から前方一致のものを探索.
例をいくつか示します.
:time
というマクロがあり:t 3
と入力すると:type 3
となります.:type
というマクロがあり:t 3
と入力すると組み込みのものではなく,定義されたマクロを使って
:type 3
となります.:time
マクロと:type
マクロの両方があり:t 3
と入力すると定義されたマクロを使って
:type 3
となります.
5.9.2. .haskeline
ファイル¶
GHCi は内部で Haskeline を使っています. 他のものと同じように,これを設定することで,GHCiの履歴中の重複を取り除けます. Haskeline user preferences を参照してください.
5.10. GHCi内でオブジェクトコードにコンパイルする¶
デフォルトでは,GHCiはHaskellのソースをバイトコードにコンパイルして,ランタイムシステムがそれを解釈実行します.
GHCiはHaskellのコードをオブジェクトコードにコンパイルすることもできます.それには -fobject-code
フラグを,
コマンドラインで使うか :set
で設定します
(-fbyte-code
を設定すると元のバイトコードコンパイルが復帰します).
オブジェクトコードへのコンパイルには時間が余分にかかりますが,通常オブジェクトコードの実行は,バイトコードの実行の10〜20倍速くなります.
GHCi内でオブジェクトコードにコンパイルする機能はコンパイル形式のアプリケーションを開発する場合に特に便利です.
通常 :reload
を使うほうが,コマンドラインでGHCを --make
オプション付きで再起動するよりずっと速いからです.
これはインターフェイスファイルがすべてメモリにキャッシュされているからです.
オブジェクトコードにコンパイルしてしまうことには欠点もあります. たとえば,オブジェクトコードモジュールにはブレイクポイントを設定することはできません. オブジェクトコードモジュールではエクスポートされたものしか見えませんが, 解釈実行されているモジュールでは,トップレベルの束縛はすべてGHCiでは可視になります.
5.11. インタプリタを別プロセスで走らせる¶
通常GHCiは解釈実行されるコードをGHCそれ自身と同じプロセスで走らせます.
すなわち,GHCと同じRTS上で走らせ,同じヒープ領域を共有します.
しかし -fexternal-interpreter
フラグを与えると,GHCは別プロセスを起こしてそちらで解釈実行コードを走らせます.
そのプロセスとの通信にはパイプ上のメッセージを使います.
-
-fexternal-interpreter
Since: 8.0.1 別プロセスで(GHCi,Template Haskell,準クォートあるいはアノテーション)の解釈実行コードを走らせます. インタプリタは
-prof
フラグが有効になっていれば,プロファイルモードで動作し,-dynamic
フラグが有効になっていれば,動的リンクモードで動作します.このオプションには欠陥がいくつか残っています(将来,除去されるでしょう). 現時点ではこのオプションはWindowsでは実現していません(指定しても何も起こりません). また,別プロセスできどうしたインタプリタはGHCiデバッガをサポートしていませんので,
-fexternal-interpreter
を指定してもブレイクポイントを設定したり,ステップ実行を行うことはできません.-pgmi
(Replacing the program for one or more phases) および-opti
(Forcing options to a particular phase) フラグも参照してください.
なぜこの機能が必要なのでしょうか. 主な理由は,解釈実行するコードを走らせる RTS と GHC そのものとでは(プロファイリングや動的リンクかどうかなど)異なる性質のものだからです. たとえば,
- GHCi使用時にはプロファイラを使ってスタックトレースを収集できます(GHCiのスタックトレース 参照).
- Template Haskell のコードを
-prof
でコンパイルする場合,先にモジュールを-prof
なしで コンパイルする必要はありません(Using Template Haskell with Profiling 参照). インタプリタでプロファイル設定されたオブジェクトコードを走らせることが可能だからです.
GHC 8.0.x ではこの機能は実験的なものですが,将来のリリースではデフォルト機能になる予定です.
5.12. FAQ と注意事項¶
- インタプリタはforeign export宣言のあるモジュールをロードできません!
- 残念ながらその通りです.まだその機能は実装していません.その問題のあるモジュールは手でコンパイルしてからGHCiにロードしてください.
GHCiで -O
が効きません!
技術的な理由から,バイトコードコンパイラは最適化過程と上手くやりとりできないので, インタプリタを使う場合には最適化を無効にしてあります. だからといって大した問題にはなりません. 高速実行が必要になるようなコードはコンパイルしておけば,最適化を有効にして解釈実行するよりずっと速いからです.
- GHCiで非ボックス化タプルが使えません.
- その通りです.ただし,非ボックス化タプルを使うモジュールをコンパイルしてからGHCiにロードすることはできます.
(ちなみに,上記の
-O
がGHCiで使えなにのは,バイトコードコンパイラが非ボックス化タプルを扱えないからです.)
- GHCiが入力待ちのとき,裏で並行スレッドが走っててくれない.
- GHCiが
-threaded
スイッチを有効(デフォルト)にしてコンパイルされたものであれば,ちゃんと機能するはずです. GHCiをインストールしてくれた人に相談してください.
getContents
を使うと,その後:load
あるいは:reload
しないとstdin
が使えません.これは
getContents
定義どおりの振る舞いです.getContents
はstdin
ハンドルをセミクローズドという状態にします. この状態のハンドル上ではいかなるI/O操作もできません. 計算と計算の間ではI/Oの状態は保持されるので,次に:load
コマンド,あるいは,:reload
コマンドを実行するまではセミクローズド状態が続きます.GHCiの
:set +r
というコマンドを使えばstdin
が毎回元の状態に復元するようにできます. これがうまく行くのはstdin
が単なるトップレベルの式で,他のトップレベルの式(CAF)と同様の方法で未評価状態に戻せるからです.
- Windows で
Control-C
を使って計算を中断できません. - Running GHCi on Windows を参照してください.
- GHCi と GHC とで,デフォルトのバッファリングモードが異なります.
GHC では
stdout
ハンドルは行バッファモードになっています. 他方,GHCi ではstdout
のバッファリングはオフになっています. 出力がすぐに見えるというのが,インタプリタに期待する動作だからです.GHCiで行バッファモードが必要ときは,プログラムを以下のように始めるとよいでしょう.
main = do { hSetBuffering stdout LineBuffering; ... }
[5] | パッケージはコンパイル済みのコードだけを含んでいるので,パッケージのデバッグにはソースコードを探してそれを直接ロードしなければならないことに注意してください. |
[6] | もともと,当該式の自由変数だけではなく,スコープ内にあるすべての変数の束縛を提供していました. しかし,実行性能に大いに影響することが判明したので,それ以来,自由変数のみに制限しています. |