プロンプトに式を入力すると、GHCiは即座に評価して結果を印字する。
Prelude> reverse "hello" "olleh" Prelude> 5+5 10
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 42
をIO
モナドの中で実行し、結果を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
である場合は、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
の古い値は失われることに注意。
次の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)
を考え、次の条件が満たされたとき、この型変数をデフォルト化する。
型変数a
が他のどの制約にも現れない。
Ci
が全て標準のクラスである。
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を実行する時にエラーを発生させることになる。