5. GHCi を使う¶
GHCi [1] はGHCの対話環境であり,Haskellの式を対話方式で評価したり プログラムを解釈実行したりできます. Hugs の経験があるなら, すぐにでもGHCiに慣れることでしょう. しかしながら, GHCiはコンパイル済みのコードを対話的にロードすることができます. また,GHCが提供する言語拡張のすべて[2]_ をサポートしています. GHCi は対話方式のデバッガも備えています(GHCiのデバッガ 参照).
| [1] | “i”は“Interactive”の i です. |
| [2] | ただし今のところ foreign export は除きます. |
5.1. Introduction to GHCi¶
GHCiセッションの例を見ていくことから始めましょう.
GHCiは ghci コマンドで起動します.
$ ghci
GHCi, version 8.y.z: 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 束縛文であることは,行が表示されないことで示されています.
GHC 8.0.1 以降 let 文を使わなくても,値や関数で名前を束縛できます.
Prelude> x = 42
Prelude> x
42
Prelude>
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内でオブジェクトコードにコンパイルする 参照).
ヒント
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はプロンプトで受け付けているのは単なる式ではなく文です. そのため,値や関数を名前に束縛して,後で式や文の中で使うことができます.
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
モジュールのライブラリ 文書 を参照してください).
新しい束縛は,同じ名前の既存の束縛をシャドウし(覆い隠し)ます. これは現在のモジュールの文脈でスコープにある実体もシャドウします.
警告
プロンプトで導入さた束縛あ一時的なもので,
次に :load あるいは :reload コマンドが実行されるまでの間のことです.
これらのコマンドが実行されると,一時的な束縛は消えてしまいます.
ただし,:module: で文脈を変更しても一時的は束縛は新しい場所へ移動し,
消えることはありません.
ヒント
+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
通常の変数束縛と同様に,後で定義されたものは古い定義をシャドウしてしまうので, 定義を再入力すれば,問題を修正したり拡張したりできます. ただし,落とし穴があります. 新しい型宣言が古い型宣言をシャドウするとき,古い型の定義を参照している別の宣言があるかもしれません. この古い型はまだ存在し,この別の宣言はまだ古い型を参照しているということを覚えておいてください. 古い型と新しい型は同じ名前ですが,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 参照).たとえば,
Prelude> type family T a b
Prelude> type instance T a b = a
Prelude> let uc :: a -> T a b; uc = id
Prelude> type instance T a b = b
<interactive>:3:15: error:
Conflicting family instance declarations:
T a b = a -- Defined at <interactive>:3:15
T a b = b -- Defined at <interactive>:5:15
-- Darn! We have to re-declare T.
Prelude> type family T a b
-- This is a brand-new T, unrelated to the old one
Prelude> type instance T a b = b
Prelude> uc 'a' :: Int
<interactive>:8:1: error:
• Couldn't match type ‘Char’ with ‘Int’
Expected type: Int
Actual type: Ghci1.T Char b0
• In the expression: uc 'a' :: Int
In an equation for ‘it’: it = uc 'a' :: Int
5.4.5. プロンプトのスコープにあるもの¶
プロンプトに式を入力するとき,どの識別子や型がスコープにあるのでしょうか. 以下のように,GHCiでは,式を評価する際の環境を構成する方法を正確に指定できます.
:load,:add,:reloadコマンド (スコープ内容に対する :load の影響).import宣言(import によるスコープ制御).:moduleコマンド(:module コマンドによるスコープ制御).
:show imports を使えば,トップレベルのスコープにどのモジュールがあるか要約を表示できます.
ヒント
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⟩ のトップレベルのスコープであることを示しています.
* が付かない場合は当該モジュールからエクスポートされたものだけが見えるということです.
注釈
技術的理由により,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. 修飾名¶
手間をすこし省くことができるように,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> Data.Time.getZonedTime
2017-04-10 12:34:56.93213581 UTC
Prelude> print it
2017-04-10 12:34:56.93213581 UTC
IO型の式 e に対する変形は,
it <- e
となります.
新しい式を評価するたびに it の値は新しい値でシャドウされ,古い it の値は失われることに注意してください.
5.4.8. GHCi でのデフォルト型設定¶
-
-XExtendedDefaultRules¶ 数値クラス以外のものに対してもデフォルト設定を行うことができます.
次の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 フラグが設定されていると,
型の解決には以下の方法が使われます.
未解決の制約をすべて見つけます.そのあとは以下のようにします.
aが型変数 であるような(C a)という形式の未解決制約を見つけ,共通する型変数aを共有するグループに分割します.- 少なくとも1つのクラスが インタラクティブクラス (以下で定義)であるグループのみを維持します.
- ここで,残りのグループGごとに,デフォルトのタイプのリストから順番に各タイプ
tyを試してます.a = tyを設定すると,Gの制約を完全に解くことができるかを確かめます.そうであれば,デフォルトではaをtyとします. - ユニット型
()およびリスト型[]がデフォルトの型として試されるリストの先頭に追加される.
マルチパラメータ制約 (D a b) または (D [a] Int) はプロセスに関与しないことに注意してください(助けにも妨げにもなりません).
しかし,デフォルトプロセスが完了したら、それらはもちろん解決できていなければなりません.
最後の点は,たとえば,以下のプログラムに影響します.
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.8.1. インタラクティブクラス¶
インタラクティブクラスとは(-XExtendedDefaultRules が有効のときのみ),すべての数値クラス,
型変数がこれらのクラスの1つに制約されている限り,上で概説したようにデフォルト化が起ります.
5.4.8.2. default 宣言まわりの拡張されたルール¶
デフォルト化のルールは -XExtendedDefaultRules があると緩和されるので,
default 宣言のルールも緩和されます.
Haskell 2010 Report の 4.3.4 節によれば default 宣言は default (t1, ..., tn) のような形式になります.
ここで,それぞれの ti に関して Num ti が満たされていなければなりません.
これをそれぞれの ti に関して C ti を満たすようななインタラクティブクラス C が存在しなければならないと緩和します.
これは,型 構成子 が一覧にできるということです.
たとえば Foldable 制約のデフォルトを Maybe したければ以下で機能しますが
Num 制約のデフォルトは Integer または Double に設定されます.
default (Maybe, Integer, Double)
5.4.9. 独自の対話表示関数を使う¶
GHC 7.6.1 以降,GHCiはプロンプトに入力された式の結果を System.IO.print を使って表示します.
この関数の型シグネチャは Show a => a -> IO () で,値を show を使って String に変換しています.
このやり方が理想的ではない場合があります. 出力が長い場合や非アスキー文字が含まれるというような場合です.
-interactive-print ⟨expr⟩ フラグを使えば,何らかの制約を C として,
C a => a -> IO () という型の関数を評価済みの式の値を表示する関数として指定できるようになります.
この関数はロード済みのモジュールまたは登録済みのパッケージに置いてあればよいのですが,
登録済みのパッケージに置いてある場合のみ :cd , :add , :load ,
:reload あるいは :set というコマンドをくぐり抜けられます.
-
-interactive-print⟨expr⟩¶ Set the function used by GHCi to print evaluation results. Expression must be of type
C a => a -> IO ().
例として,以下の特別な表示モジュールがあるとしましょう.
module SpecPrinter where
import System.IO
sprint a = putStrLn $ show a ++ "!"
sprint 関数は表示された値の最後に感嘆符を追加します.
以下のコマンド
ghci -interactive-print=SpecPrinter.sprint 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])
ここでは,サンク _t2 だけを評価して,リストの先頭が判明しました.
seq 関数はすこし使いにくいので :def を使ってもっとよいインターフェイスを作るといいでしょう
(どうするかは練習問題にしておきます!).
ここでは,サンク _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とみなします.
言い換えれば,カラム数を数えるのではなく文字を数えます.
振る舞いと合うエディタもあり,合わないエディタもあります.
最善はそもそもソースコード中でタブ文字を使わないことです
(警告と整合性検査 にある -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
ブレイクポイントを削除するには :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=⟨n⟩¶ 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が受け付けるコマンドラインオプション(GHCを使う 参照)の大部分は対話モードでも有効です. GHCiで有効でないものは見れば判ります.
-
-flocal-ghci-history¶ デフォルトで GHCi グローバル履歴を
~/.ghc/ghci_historyに記録します. Windows では%APPDATA%/<app>/ghci_historyになりますが,これを現在のディレクトリに設定することもできます.$ ghci -flocal-ghci-history
こうすると
.ghci-historyを GHCi を起動した現在のフォルダに作成します.
5.6.1. パッケージ¶
ほとんどのパッケージ(Using Packages 参照)は追加でフラグを指定しなくても利用できます. 最初に必要になったときに自動的ロードされます.
一方で,隠されたパッケージについては -package ⟨pkg⟩ フラグを使ってロードを要求する必要があります.
$ ghci -package readline
GHCi, version 8.y.z: http://www.haskell.org/ghc/ :? for help
Loading package base ... linking ... done.
Loading package readline-1.0 ... linking ... done.
Prelude>
以下のコマンドを使えば起動中の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 ⟨dir⟩コマンドラインオプションで指定したパス.- システムの標準ライブラリ検索パス.これはシステムによっては環境変数
LD_LIBRARY_PATHを設定することで変更可能です.
.dll-形式の共有ライブラリを使うシステムでは,実際にロードされるライブラリは lib.dll です.
GHCi指定のライブラリが見つからなければ,ここでもエラーになります.
GHCi は単なるオブジェクトファイル(プラットフォームによって .o か .obj のどちらか)をコマンドラインで指定してロードできます.オブジェクトファイル名をコマンドラインに追加するだけです.
-l オプションの順序は重要です.ライブラリ指定は,そのライブラリが依存しているライブラリよりも 前に 書いておく必要があります(Options affecting linking 参照).
5.7. GHCi のコマンド群¶
GHCi のコマンドはすべて「 : 」ではじまり,1つのコマンド名と0個以上のパラメータからなります.
コマンド名は短縮可能です.
短縮の結果曖昧になった場合は,よりよく使われるコマンドを優先します.
-
:abandon¶ 現在の評価を破棄します(これはブレイクポイントで停止しているときのみ有効).
-
:add[*] ⟨module⟩¶ ⟨module⟩ を現在のターゲット集合に追加しリロードを実行します. 通常,可能ならそのモジュールのコンパイル済みコードをロードし,そうでなければ,そのモジュールはバイトコードにコンパイルします.
*接頭辞を使えば,強制的にバイトコードとしてロードできます.⟨module⟩ はファイルパスにすることもできます. “
~” 記号を ⟨module⟩ の前に置くとそれが環境変数HOMEに置き換わります.
-
: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> let date _ = Data.Time.getZonedTime >>= print >> return "" Prelude> :def date date Prelude> :date 2018-01-03 09:49:04.34868 JST
次は,引数を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は現在のプロンプトの行番号(コンパイラメッセージで参照されるもの)に,%dは”曜日 月 日” 形式の日付(たとえば “Tue May 26”)に,%tは現在の時刻を24時間の HH:MM:SS形式で表したものに,%Tは現在の時刻を12時間の HH:MM:SS形式で表したものに,%@は現在の時刻を12時間の am/pm 形式で表したものに,%Aは現在の時刻を24時間の HH:MM形式で表したものに,%uは現在のユーザのユーザ名に,%wは現在の作業ディレクトリ名に,%oはOS名に,%aはマシンアーキテクチャ名に,%Nはコンパイラ名に,%Vはコンパイラのバージョンに,%call(cmd [args])はcmd argsを呼んだ結果文字列に,%%は%に,
にそれぞれ置き換えられます.
⟨prompt⟩ が
"で始まる場合,HaskellのStringとしてパースします. そうでない場合はそのまま文字列として扱います.
-
:set prompt-function⟨prompt-function⟩¶ GHCiで表示されるプロンプトに使用される関数を設定します. 関数の型は
[String] - > Int - > IO String型でなければなりません. この関数は,プロンプトが作成されるたびに呼び出されます. 最初の引数は現在スコープ内にあるモジュールの名前を表します(「最上位」モジュールの名前は*で始まります. 詳細については プロンプトのスコープにあるもの を参照してください). 2番目の引数は,現在のプロンプトの(コンパイラメッセージで参照されている)行番号です.
-
:set prompt-cont-function⟨prompt-function⟩¶ 継続プロンプトに使用される関数を設定します(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⟩ の型を推論し表示します. 報告された型は,式に割り当てられた変数に対して推論されますが,単相性の制約は適用されません.
*X> :type length length :: Foldable t => t a -> Int
-
:type +v⟨expression⟩¶ 型変数やクラスの制約をごまかすことなく,⟨expression⟩の型を推論して表示します. これは
-XTypeApplicationsを使用しているときに,指定された型変数(型のアプリケーションで使用可能) と推論された型変数(使用できない)との区別に注意するときに便利です. このモードでは,簡単に解決できる制約(Show Intなど)が表示されることがありますが, これらの制約を解決すると型変数に影響する可能性があるため,GHCはこれを抑制します.*X> :set -fprint-explicit-foralls *X> :type +v length length :: forall (t :: * -> *). Foldable t => forall a. t a -> Int
-
:type +d⟨expression⟩¶ 可能であればデフォルトの型変数を推論して⟨expression⟩の型を表示します. このモードでは,推論された型が任意のインタラクティブクラス(Num,Show,Eq,Ord,Foldable,Traversable) によって制約されている場合,制約された型変数は
-XExtendedDefaultRulesで説明されている規則に従って デフォルト設定されます.このモードは,推論された型がかなり一般的な場合(foldrなど),より具体的なインスタンスを 知るのに便利です.*X> :type +d length length :: [a] -> Int
-
: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種類のオプションを設定できます.
「 +」で始まるオプションと「 - 」で始まる「コマンドライン」オプションです.
注釈
現在のところ :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のコマンドラインオプションのうち,動的なオプション(フラグの一覧表 にある一覧表を参照してください)
として設計されているものは :set を使って有効にすることができます.
オプションを無効にするには,逆の効果を持つオプションを有効すればできます.
Prelude> :set -Wno-incomplete-patterns -XNoMultiParamTypeClasses
フラグの一覧表 には,可能なオプション全てについて逆の効果を持つオプションが一覧してあります.
いくつかの静的なオプション(特に -package ⟨pkg⟩ , -I⟨dir⟩ ,
-i⟨dir⟩[:⟨dir⟩]* , -l ⟨lib⟩)も使えるが,次にリロードするまで効果を発揮しないものもある.
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 フラグが指定されていなければ,以下の各ファイルを順に探し,
存在すれば,そのファイルに書かれているコマンドを読み込み実行します.
./.ghciappdata/ghc/ghci.confただし ⟨appdata⟩ はシステムに依存します. 通常はC:Documents and SettingsuserApplication Dataのようなところです.- Unixでは
$HOME/.ghc/ghci.conf $HOME/.ghci
ghci.conf ファイルは,お気に入りのオプション(たとえば :set +s)を有効にしたり便利なマクロを定義するのに向いています.
注釈
このファイルで言語オプションを設定する場合は,通常は :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 オプション付きで再起動するよりずっと速いからです.
これはインターフェイスファイルがすべてメモリにキャッシュされているからです.
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 ⟨cmd⟩(Replacing the program for one or more phases) および-opti ⟨option⟩(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] | もともと,当該式の自由変数だけではなく,スコープ内にあるすべての変数の束縛を提供していました. しかし,実行性能に大いに影響することが判明したので,それ以来,自由変数のみに制限しています. |