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

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

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

2.4.1. I/O動作とプロンプト

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

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

これは、式の型をIO a具体化することができる限り、その型がもっと一般的であっても動作する。例えば

Prelude> return True
True

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

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

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

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

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

2.4.2. プロンプトで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]

2.4.3. 複数行入力

上で言及した:{ ... :}構文による複数行入力の他に、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>

2.4.4. 型、クラスおよびその他の宣言

GHCiプロンプトではまた、Haskellの任意の最上位宣言を入力することができる。これには以下の各宣言が含まれる。datatypenewtypeclassinstancederivingforeign。例を挙げる。

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と表示される)と区別しようとしてこうなっている。

クラスや型族のインスタンス宣言は、単に「利用可能なインスタンスの一覧」に追加される。ただし例外が一つある。型族インスタンスには重複があってはならないが、再定義したいこともあるので、左辺が同一であるインスタンスが過去にあった場合にはそれを置き換える。(7.7. 型の族を見よ。)

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

プロンプトに式を入力するとき、どの識別子や型がスコープにあるのだろうか。GHCiでは、式を評価する際の環境をどうやって構築するか、正確に指定することができる。以下のように。

:show importsは、どのモジュールの内容がスコープにあるかについて要約を表示する。

ヒント: GHCiはスコープにある名前をtab補完する。たとえば、GHCiを起動してJ<tab>を打ち込むと、GHCiはそれを「Just 」に展開する。

2.4.5.1. スコープ内容に対する:loadの影響

:load:add:reloadの各コマンドがスコープに影響する。(2.2. ソースファイルをロードするおよび2.3. コンパイル済みコードをロードする)。単純な場合から始めよう。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が指定されておらず、しかも*形式のモジュールが一つもないとき、自動的にそれを付け加える)。これらの自動追加されたインポートは: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で削除することもできる。

2.4.5.2. importを使ったスコープ制御

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

モジュールをスコープに加えるには、通常のHaskellのimport構文を使う。

Prelude> import System.IO
Prelude System.IO> hPutStrLn stdout "hello\n"
hello
Prelude System.IO>

hiding節とas節を含めて、完全なHaskellのimport構文に対応している。プロンプトは現在インポートされているモジュールを示すが、hidingasなどの詳細は省略される。詳しい事情を知りたければ、: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をインポートすればそちらが優先される。

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

2.4.5.3. :moduleコマンドを使ったスコープ制御

スコープを操作するもう一つの方法は:moduleコマンドである。構文は以下である。

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

+を使えばモジュールが現在のスコープに加えられ、-では削除される。+-もない場合は、指定されたモジュール群がそのままで現在のスコープになる。この形式を使って、しかもPreludeを含めなかった場合、GHCiは暗黙のPreludeインポートを自動的に追加する。

:moduleコマンドは、通常のimport宣言では実現できないことをする方法を提供する。

  • :moduleは、モジュールに付く*修飾子に対応する。これは、単にモジュールがエクスポートしているものだけでなく、そのモジュールの完全な最上位スコープを開く。

  • :module -M構文を使うと、文脈からモジュールを削除することができる。import構文は(Haskellモジュール内と同じく)累積的なので、これがスコープから一部を取り除く唯一の方法である。

2.4.5.4. 修飾名

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

2.4.5.5. :module:load

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

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

  • 現在スコープにあるモジュールの集合。この集合はimport:moduleによって変更される。また、上述のように:load:add:reloadの後に自動的に変更が加えられる。スコープにあるモジュールの集合は:show importsで見ることができる。

(:moduleimportで)スコープに入れることができるのは、(a)ロードされているモジュールか、(b)GHCiが知っているパッケージに入っているモジュールだけである。:moduleを使ってロードされていないモジュールをスコープに入れようとすると「module M is not loaded」というメッセージが表示されることがある。

2.4.6. :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"]

2.4.7. 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の古い値は失われることに注意。

2.4.8. 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を実行する時にエラーを発生させることになる。

計算を扱う式のモナドが可能ならIOにデフォルト化される事については2.4.1. I/O動作とプロンプトを見よ。

2.4.9. デフォルト以外の対話表示関数を使う

[バージョン7.6.1の新機能] デフォルトでは、GHCiはプロンプトに入力された式の結果を関数System.IO.printを使って表示する。これの型シグネチャはShow a => a -> IO ()であり、値をshowStringに変換することで動作する。

ある種の状況では、これは理想的でない。たとえば出力が長い場合や、非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]!