print

さて、まだ基本的なソースを引きずっているわけだ。
せっかくだから、少しいじってみる。

module Test where

f = x + y
h x y = x + y
y = 2
x = 1
g x = x + 3

順番を変えたらどうだろう?

Loading package base-1.0 ... linking ... done.
Prelude> Compiling Test             ( ./mm.hs, interpreted )
Ok, modules loaded: Test.
*Test> f
3

問題ないね。逐次実行ではないので、定義する順番には影響されないんだ。

さて、この前見たエラー文の最後をもう一度よく見てみよう。

   In a 'do' expression: print it

このprint itというのは、どうも関数のようだね。調べてみよう。

*Test> :t print it
print it :: IO ()
*Test> :t print
print :: (Show a) => a -> IO ()
*Test> :t it
it :: Integer

print it はIO型のデータらしい。()は良く分からない。調べると

*Test> :t ()
() :: ()
*Test> :i ()
data () = () 	-- <wired into compiler>
instance Eq () 	-- Imported from GHC.Base
instance Ord () 	-- Imported from GHC.Base
instance Show () 	-- Imported from GHC.Show

こんな感じ。()は特別な意味を持つのかな。
分からないものはとりあえず無視。後で分かるかも知れない。
printはShowクラスのaからIO型を作るようだね。
itは数値になっているようだ。

*Test> it
3

さっきなんとなく試したfと同じだね。

*Test> it == f
True

うん、同じだ。==は同値か調べるのに使う演算子だよ。

*Test> it == f

<interactive>:1:6:
    Couldn't match `Bool' against `Integer'
      Expected type: Bool
      Inferred type: Integer
    In the second argument of `(==)', namely `f'
    In the definition of `it': it = it == f
*Test> it
True

と思ったら、今度は変わってしまった。どうも、前回の計算結果が入るようだ。
計算するたびに値が変わる変数なんだね。
うーん、副作用の例が出てしまった。これは次に扱おうかな。

printも試してみる。

*Test> print 10
10
*Test> print "kome"
"kome"

結局のところ、そのまま表示する関数みたいだね。
ちなみに、何気なく文字列を扱ってるけど、型を見ておこう。

*Test> :t "kome"
"kome" :: [Char]
*Test> :t "kome"::String
"kome"::String :: String
*Test> :i String
type String = [Char] 	-- Imported from GHC.Base

文字列は[Char]型で、つまりString型なんだ。
[]というのはリストを表しているけど、これも後で扱おう。

で、print itがどこから出てきたか。

それは3.4. プロンプトで対話的に評価するに書いてあるみたいね。
今まで試してきたことは、ここに書いてあることの確認だったようだ。

式(正確には束縛文でない文)がプロンプトに入力されると、GHCiはその値を暗黙のうちに変数itに束縛する。
(中略)
GHCiは入力された式を型検査し、もしIO型でなければ、それを次のように変形するのである。式eは

let it = e;
print it

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

文が評価されて、結果が表示されるというのはこうやってされていたんだね。
letというのは定型文で、値を束縛(バインド)するのに使う。束縛の説明は次の機会にね。

*Test> :i let
<interactive>:1:0: parse error on input `let'

*Test> :t let
not an expression: `let'

letはマクロか何かで定義されているのか、これについての情報はないみたい。
まあ、試してみると、束縛文では文を変形しない、つまり、結果が表示されないのが分かる。

*Test> let xy = x*2 + y*4
*Test> xy
10

一応、これは変数の代入に当たる。
でも、恐らく普通のプログラミング言語の代入のようには使えないんだよね。
これは副作用のところで検証しよう。

で、次のようにも書いてある。

式の型がなんらかのaについてIO aである場合は、itはIO動作の結果(これの型はaである)に束縛される。

IO型の返り値を持つ関数は、たった今みたprintを知ってるね。

*Test> print "kome"
"kome"
*Test> :t it
it :: ()

どうも、printの返り値は()で固定ってことみたいだ。引数が表示されるのは副作用なんだ。
そして、これと同じことはで出来るみたいだね。

it <- e

これを試すとこういう関係になるみたい。

*Test> let d = (print 10)
*Test> d
10
*Test> e <- print 10
10
*Test> e
()

じゃあ、今回はここまで。
次はようやくmain関数の説明に入れるよ。
今回出てきた副作用束縛についても調べてみよう。

タグ:

+ タグ編集
  • タグ:
最終更新:2007年09月26日 17:55