プロンプトに式を入力すると、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]
上で言及した:{ ... :}
構文による複数行入力の他に、GHCには: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>
閉じ中括弧の後では、現在の文が終わったことをGHCiが知っているので、空行が必要ないことに注意。
複数行モードはモナドな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>
[バージョン7.4.1の新機能] 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>:2:3: Couldn't match expected type `main::Interactive.T' with actual type `T' In the first argument of `f', namely `A' In the expression: f A In an equation for `it': it = f A Prelude>
古い、隠された方のT
はGHCiによってmain::Interactive.T
と表示されている。これは、新しいT
(単にT
と表示される)と区別しようとしてこうなっている。
プロンプトに式を入力するとき、どの識別子や型がスコープにあるのだろうか。GHCiでは、式を評価する際の環境をどうやって構築するか、正確に指定することができる。単純な場合から始めよう。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はこれらのモジュールのスコープを全て組み合わせ、プロンプトのスコープとする。
注意: 技術的な理由から、GHCiは解釈実行されるモジュールについてしか*
形式をサポートしない。そのため、コンパイル済みモジュールとパッケージのモジュールは、それがエクスポートしたものでしかプロンプトのスコープに寄与できない。GHCiがモジュールの解釈実行版を使っていることを確実にするには、モジュールをロードする時に*
を加えれば良い。例えば:load *M
のように。
モジュールをスコープに加えるには、通常のHaskellのimport
構文を使う。
Prelude> import System.IO Prelude System.IO> hPutStrLn stdout "hello\n" hello Prelude System.IO>
hiding
節とas
節を含めて、完全なHaskellのimport構文に対応している。プロンプトは現在インポートされているモジュールを示すが、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と標示されているのに注意。通常のHaskellモジュール内と同様に、明示的にPrelude
をインポートすればそちらが優先される。
スコープを操作するもう一つの方法は:module
コマンドである。これは、通常のimport
宣言では実現できないことをする方法を提供する。
:module
は、モジュールに付く*
修飾子に対応する。これは、単にモジュールがエクスポートしているものだけでなく、そのモジュールの完全な最上位スコープを開く。
:module -M
構文を使うと、文脈からモジュールを削除することができる。import
構文は(Haskellモジュール内と同じく)累積的なので、これがスコープから一部を取り除く唯一の方法である。
:module
コマンドの完全な構文は以下である。
:module [+|-] [*]mod1
... [*]modn
+
を使えばモジュールが現在のスコープに加えられ、-
では削除される。+
も-
もない場合は、指定されたモジュール群がそのままで現在のスコープになる。この形式を使って、しかもPrelude
を含めなかった場合、GHCiは暗黙のPrelude
インポートを自動的に追加する。
: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
で削除することもできる。
複数のモジュールがスコープにあるとき、特に複数の*
形式のモジュールがあるときは、名前の衝突が起こりやすい。Haskellでは、名前の衝突は曖昧な識別子が使われたときのみ報告されると定められており、GHCiもプロンプトで入力される式についてこれに倣っている。
ヒント: GHCiはスコープにある名前についてタブ補完を行う。例えば、GHCiを起動してJ<tab>
と打つと、GHCiはそれを“Just
”に展開する。
:module
と:load
:module
と:load
は似たようなことをするように思えるかもしれない。どちらも、モジュールをスコープに入れるのに使うことができる。しかし、この二つには明確な違いがある。GHCiは、以下の二つのモジュール集合を意識する。
現在ロードされているモジュールの集合。この集合は、:load
、:add
、:reload
によって変更され、:show modules
で見ることができる。
現在スコープにあるモジュールの集合。この集合はimport
と: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を実行する時にエラーを発生させることになる。
[バージョン7.6.1の新機能]
デフォルトでは、GHCiはプロンプトに入力された式の結果を関数System.IO.print
を使って表示する。これの型シグネチャはShow a => a -> IO ()
であり、値をshow
でString
に変換することで動作する。
ある種の状況では、これは理想的でない。たとえば出力が長い場合や、非ascii文字のある文字列を含んでいる場合である。
フラグ-interactive-print
を使うと、何らかの制約C
についてC a => a -> IO ()
という型を持つ任意の関数を、評価された式を表示するための関数として指定することができるようになる。その関数はロードされているモジュールのいずれか、あるいは登録済みモジュールのいずれかにあれば良い。
例として、以下の特別な表示モジュールがあるとしよう。
module SpecPrinter where import System.IO sprint a = putStrLn $ show a ++ "!"
関数sprint
は表示された値の最後に感嘆符を付ける。次のコマンドでGHCiを走らせると、
ghci -interactive-print=SpecPrinter.sprinter SpecPrinter
対話セッションが開始され、そこでは値がsprint
で表示される。
*SpecPrinter> [1,2,3] [1,2,3]! *SpecPrinter> 42 42!
デフォルト以外の表示関数は、例えば、木構造や入れ子の構造をより読み易く表示するために使うことができる。
-interactive-print
フラグは、GHCを-eモード
で走らせるときにも使える。
% ghc -e "[1,2,3]" -interactive-print=SpecPrinter.sprint SpecPrinter [1,2,3]!