プロンプトで対話的に評価する

プロンプトに式を入力すると、GHCiは即座に評価して結果を印字する。

Prelude> reverse "hello"
"olleh"
Prelude> 5+5
10

I/O動作とプロンプト

GHCiはプロンプトにおいて行うのは単なる式の評価だけではない。なんらかのaについてIO aの型を持つものを入力すると、GHCiはそれをIO動作として実行するのである。

Prelude> "hello"
"hello"
Prelude> putStrLn "hello"
hello

さらに、次の場合には(そしてその場合に限り)、GHCiはIO動作の結果を印字する。

  • 結果の型がShowのインスタンスである。

  • 結果の型が()でない。

例えば、putStrLn :: String -> IO ()に注意すると、次のようになる。

Prelude> putStrLn "hello"
hello
Prelude> do { putStrLn "hello"; return "yes" }
hello
"yes"

プロンプトでdo記法を使う

GHCiは実際には単なる式ではなくを受け付ける。そのため、値や関数を名前に束縛して、後で式や文の中で使うことができる。

GHCiプロンプトが受け付ける文の構文は、Haskellのdo式における文の構文と全く同じである。ただし、こちらにはモナドの多重定義はない。プロンプトに入力される文はIOモナドの中になければならない。

Prelude> x <- return 42
Prelude> print x
42
Prelude>

x <- return 42という文は、「return 42IOモナドの中で実行し、結果をxに束縛する」という意味である。以降、xを文の中で使う(例えば、上でしたように、値を印字する)ことができるようになる。

-fprint-bind-resultが設定されているなら、GHCiは次の場合に(そしてその場合に限り)、文の結果を印字する。

  • 文が束縛ではないか、ただ一つの変数を束縛するモナド束縛(p <- e)である。

  • 変数の型が多相的でなく、()でなく、Showのインスタンスである。

もちろん、非IOの式をlet文を使って束縛することもできる。

Prelude> let x = 42
Prelude> x
42
Prelude>

この二種類の束縛のもう一つの違いは、モナド束縛(p <- e)が正格(eが評価される)のに対して、let形式では式がすぐには評価されないことである。

Prelude> let x = error "help!"
Prelude> print x
*** Exception: help!
Prelude>

モナド束縛と違って、let束縛では束縛された値が自動的に印字されることがないことに注意。

ヒント: let文を使って、プロンプトで関数を定義することもできる。

Prelude> let add a b = a + b
Prelude> add 1 2
3
Prelude>

しかし、複数の節からなる関数を定義したり、相互再帰的な関数を定義したりしようとすると、このやりかたはすぐ面倒になる。レイアウト規則が使えず、定義全体を、明示的な中括弧とセミコロンを使って一行で与えないといけないからだ。

Prelude> let { f op n [] = n ; f op n (h:t) = h `op` f op n t }
Prelude> f (+) 0 [1..3]
6
Prelude>

この問題を軽減するために、GHCiのコマンドを:{:}で囲む(それぞれ一行使って)と、複数行に渡らせることができるようになっている。

Prelude> :{
Prelude| let { g op n [] = n
Prelude|     ; g op n (h:t) = h `op` g op n t
Prelude|     }
Prelude| :}
Prelude> g (*) 1 [1..3]
6

このような複数行コマンドは全てのGHCiコマンドについて用いることができ、:{:}の間の行は単純に一行に繋げられて解釈される。よって、この行の集りは繋げられたときに単一の正しいコマンドになっていなければならず、レイアウト規則を使うことはできない。複数行コマンドの主な目的は、モジュールのロードの代替にすることではなく、.ghciファイル(2.9. .ghciファイルを見よ)での定義を見やすく、保守しやすくすることである。

文を評価または実行している間に発生した例外は、GHCiのコマンド行インタフェースによって捕捉され、印字される(例外について詳しくは、ライブラリ説明書のControl.Exceptionを見よ)。

束縛は、同じ名前の既存の束縛を覆い隠す。現在のモジュールの文脈でスコープにある実体も同様に覆い隠される。

警告: プロンプトで導入された束縛は一時的なもので、次に:load:reloadコマンドが実行されるまでしか存在しない。これらのコマンドが実行されると、一時的な束縛は全て失われる。ただし、:moduleでの文脈の変更では失われない。一時的な束縛が新しい場所に移動するだけである。

ヒント: :show bindingコマンドを使うと、その時点でスコープにある束縛の一覧を得ることができる。

Prelude> :show bindings
x :: Int
Prelude>

ヒント: +tオプションを有効にすると、GHCiは文によって束縛された全ての変数の型を表示するようになる。例えば、次のようにである。

Prelude> :set +t
Prelude> let (x:xs) = [1..]
x :: Integer
xs :: [Integer]

プロンプトで実際にスコープにあるのは何か

プロンプトに式を入力するとき、どの識別子や型がスコープにあるのだろうか。GHCiでは、式を評価する際の環境をどうやって構築するか、正確に指定することができる。単純な場合から始めよう。GHCiを開始すると、プロンプトは次のようになっている。

Prelude>

これは、Preludeモジュールの全てのものが現在スコープにあるということを示している。ここでファイルをGHCiにロードすると、プロンプトは次のように変わる。

Prelude> :load Main.hs
Compiling Main             ( Main.hs, interpreted )
*Main>

新しいプロンプトは*Mainであり、これは、入力される式が、Mainモジュールのトップレベルの文脈で評価されるということである。Mainモジュールのトップレベルでスコープにあるものは、全てプロンプトでもスコープにある。(Mainが明示的に隠していない限り、これにはおそらくPreludeも含まれる)

*moduleという構文は、プロンプトから入力される式に対して、moduleの完全なトップレベルスコープが使われるということを示している。*がない場合、そのモジュールがエクスポートしているものだけが可視である。

扱えるのは単一のモジュールだけではない。GHCiは複数のモジュールのスコープを組み合わせることができ、このとき*が付く形式と*なしの形式を混合して用いることができる。GHCiはこれらのモジュールのスコープを全て組み合わせ、プロンプトのスコープとする。

注意: 技術的な理由から、GHCiは解釈実行されるモジュールについてしか*形式をサポートしない。そのため、コンパイル済みモジュールとパッケージのモジュールは、それがエクスポートしたものでしかプロンプトのスコープに寄与できない。GHCiがモジュールの解釈実行版を使っていることを確実にするには、モジュールをロードする時に*を加えれば良い。例えば:load *Mのように。

スコープは:moduleコマンドで操作できる。例えば、現在のスコープがPreludeなら、次のようにして、IOモジュールでエクスポートされたものをスコープに導入することができる。

Prelude> :module +IO
Prelude IO> hPutStrLn stdout "hello\n"
hello
Prelude IO>

(注意: 通常のHaskellのimport構文を使うこともできるが、この場合*形式には対応しない)。また、:module:mに短縮できる。:moduleコマンドの完全な構文は次の通り。

:module [+|-] [*]mod1 ... [*]modn

+を使えばモジュールが現在のスコープに加えられ、-では削除される。+-もない場合は、指定されたモジュール群がそのままで現在のスコープになる。この形式を使って、しかもPreludeを含めなかった場合、GHCiはPreludeが実は必要とされていると推定し、それを追加することに注意せよ。(Preludeが必要ない場合は、:m -Preludeで削除すれば良い)。

:loadコマンドが実行されると、直近にロードされた「ターゲット」モジュールがスコープとして自動的に設定される。このとき、可能なら*形式が使われる。例えば、:load foo.hs bar.hsと入力したとき、bar.hsにはBarというモジュールがあるとすると、スコープは、Barが解釈実行されているなら*Barになり、Barがコンパイル済みならPrelude Barになる。(GHCiはPreludeが指定されておらず、しかも*形式のモジュールが一つもないとき、自動的にPreludeを付け加える)

複数のモジュールがスコープにあるとき、特に複数の*形式のモジュールがあるときは、名前の衝突が起こりやすい。Haskellでは、名前の衝突は曖昧な識別子が使われたときのみ報告されると定められており、GHCiもプロンプトで入力される式についてこれに倣っている。

ヒント: GHCiはスコープにある名前についてタブ補完を行う。例えば、GHCiを起動してJ<tab>と打つと、GHCiはそれを“Just ”に展開する。

:module:load

:module:loadは似たようなことをするように思えるかもしれない。どちらも、モジュールをスコープに入れるのに使うことができる。しかし、この二つには明確な違いがある。GHCiは、以下の二つのモジュール集合を意識する。

  • 現在ロードされているモジュールの集合。この集合は、:load:add:reloadによって変更される。

  • 現在スコープにあるモジュールの集合。この集合は:moduleによって変更される。また、:load:add:reloadの後に自動的に設定される

ロードされていないモジュールをスコープに入れることはできない。新しいモジュールをロードするために:moduleを使おうとすると「module M is not loaded」というメッセージが表示されるのはこのためである。

修飾名

手間をすこし省くことができるように、GHCiのプロンプトは、全てのパッケージの全てのモジュールと、現在GHCiにロードされている全てのモジュールについて、暗黙のimport qualified宣言があるかのように振る舞う。これは-fno-implicit-import-qualifiedというフラグで無効にできる。

:mainコマンドと:runコマンド

プログラムがコンパイルされ実行されるとき、コマンド行引数にアクセスするためにgetArgs関数を使うことができる。しかし、ghciでテストをしているときは、コマンド行引数を単純にmain関数の引数として渡すことはできない。main関数は直接には引数をとらないからである。

その代わり、:mainコマンドを使うことができる。これは、とにかくスコープにあるmainを、引数がコマンド行から渡されたのと同じようにして実行する。例えば、次のようにである。

Prelude> let 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> let foo = putStrLn "foo" >> System.Environment.getArgs >>= print
Prelude> let 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"]

itという変数

式(正確には束縛文でない文)がプロンプトに入力されると、GHCiはその値を暗黙のうちに変数itに束縛する。例えば、次のようにである。

Prelude> 1+2
3
Prelude> it * 2
6

実際に何が起こっているかというと、GHCiは入力された式を型検査し、もしIO型でなければ、それを次のように変形するのである。式e

let it = e;
print it

になり、これがIO動作として実行される。

そのため、元の式の型は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である場合は、itIO動作の結果(これの型は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の古い値は失われることに注意。

GHCiにおける型のデフォルト化

次のGHCiセッションを考えてみよう。

  ghci> reverse []

GHCiは何をするべきか。厳密に言うと、このプログラムは曖昧である。show (reverse [])(ここでGHCiが計算するのはこれである)の型はShow a => Stringであり、これをどのように表示するかはaの型に依存する。例えば、

  ghci> reverse ([] :: String)
  ""
  ghci> reverse ([] :: [Int])
  []

のようになる。しかし、利用者が型を指定しなければならないというのは面倒なので、GHCiはHaskellの型デフォルト化規則(Haskell 2010レポートの4.3.4節)を以下のように拡張している。標準の規則では、個々の型変数aについて制約の集まり(C1 a, C2 a, ..., Cn a)を考え、次の条件が満たされたとき、この型変数をデフォルト化する。

  1. 型変数aが他のどの制約にも現れない。

  2. Ciが全て標準のクラスである。

  3. Ciのうち、少なくとも一つが数値クラスである。

GHCiのプロンプトでは、あるいはGHCで-XExtendedDefaultRulesフラグが与えられている場合、以下の変更が適用される。

  • 上の規則2が次のように緩和される: Ci全て単引数の型クラスである。

  • 上の規則3が次のように緩和される: Ciのうち、少なくとも一つが数値クラスであるか、Showであるか、Eqであるか、Ordである

  • 単位型()が、型のデフォルト化の際に通常試す型のリストの先頭に追加される。

最後の点は、例えば、このプログラムに影響する。

main :: IO ()
main = print def

instance Num ()

def :: (Num a, Enum a) => a
def = toEnum 0

これが、0ではなく()を表示する。型がIntegerでなく()にデフォルト化されるためである。

この変更の理由は、これによってIO aの動作がIO ()にデフォルト化されるので、ghciがこれらを走らせたときに結果を表示する必要がなくなることである。これはprintfの場合に特に重要である。printfにはIO aを返すインスタンスがあるが、これが返せるのはundefinedだけである。(インスタンスがこの型なのは、printfがクラスシステムの拡張を必要としないようにである)。このため、もし型がIntegerにデフォルト化されるなら、ghciはprintfを実行する時にエラーを発生させることになる。