マニピュレータ

最初にお断りしておくのだが、今回の文章は
Cマガ2004年9月号(つまり今書店に行ったら売ってあるやつ)の
επιστημηのオブジェクト工房」の記事をかなり参考にしたので
そちらをお持ちなら先に読んでもらえるとよいかもしれない。
まぁ、話題はiostreamに対するマニピュレータの定義をしやすくする
"マクロ"あるいは"テンプレート"というようなことなのだが、
(こんなこと書いていいんかな…)


で、その一節に
…メカニズム自体は関数オブジェクトにも通じていて
なかなかにプログラマゴコロを刺激するのではないだろうか…云々
ということが記載されていて、
関数オブジェクトもとい、ファーストクラス関数な言語では
そんなことは直截的なんだ…といつものごとく考えてみたり、何とかしたり。


ここではマニピュレータとはCマガと同じように、
streamに何らかの作用を及ぼすもの、という意味で用いている。

cout<

とかのendlとかになるのかな。
そこで問題となるのが自分でマニピュレータを作る場合。
星(アスタリスク)をn個表示するようなマニピュレータ、

cout<

のようなものを作りたいと。


私の第一感ではoperator<<を定義したクラスをつくるのかなあ、
て感じだったのだが、ここは星n個から成る文字列を返す解法が書いてあった。
おそらく、正格性評価を考慮したときの使用スペースの問題を度外視すると
問題をoperator<<(ostream &,string&)に還元する解法のほうが
クラスを定義するよりも遥かに綺麗なやり方だと思う。
Haskellだと遅延評価なのでなおのこと。


そういうことで、文字列をストリームに突っ込むだけのものならば
文字列に還元でいいのではなかろうかと一人納得する中、
話はクラスを定義する方向へ。

ostream &foo(ostream &os,...){
}
OAPP(...) manip(foo);

C++のライブラリにあったiomanipを使えばこのようにostreamに対して
任意の操作を行うことが出来るマニピュレータが作れると。
その実はOAPPマクロがこっそりとクラスを定義しているらしい。


Cマガのほうではその後templateを使ったiomanipの再実装
みたいなことになっていくのだが、ここではHaskellでの実装を
行ってみようかと思う。関数型の考察、あるいはちょっとだけ<<オペレータが
懐かしかったのかもしれない。


まず、<<をどうするか。<<に突っ込めるものをManipuratorクラスとしよう。
ManipulatorクラスがShowクラスから継承されていればデフォルトの
実装が作れるのでそのほうがよいかもしれない。


ということで、定義してみたのが以下のManipulatorクラス。

class Show m => Manipulator m where
  (<<) :: IO Handle -> m -> IO Handle
  a << m = do
    h <- a
    hPutStr h (show m)
    return h

何か色々めんどくさかったのでostreamの変わりは単にHandleで。
IOが付いてるのは都合上。付けないとコマンドが実行できないので。
これでShowクラスのインスタンスは何でも突っ込めるのだが、
明示的にinstanceしておかないといけないので、

instance Manipulator Int
instance Manipulator Integer
instance Manipulator Double

適当にこのあたりを追加。
Charに対してはshowして欲しくないので

instance Manipulator Char where
  a << m = do
    h <- a
    hPutChar h m
    return h

このような特殊化を施す。
さらに、リストに対して

instance (Manipulator a) => Manipulator [a] where
  a << m = foldl (<<) a m

このようにインスタンスを定義。
これで文字列、整数などは突っ込めるようになった。
また、<<が組み立てるのはIO Handleなコマンドなので、

cout = return stdout

このように定義しておく。
これで、

main = cout<<"Hello, World"<

のようなC++チックなことが出来るように。
で、前述のstarsのようなものがどう定義できるのか。

main = cout<

のように書いて星が5こ表示されればよい。
私が第一感で浮かばなかった手である文字列への還元、で考えると

stars = flip replicate '*'

これでよい。
例として上がっているclsやlocateも

cls = "\x1b[2J"
locate x y = "\x1b["++show x++";"++show y++"H"

ありゃ、出来ちゃった…
でもまぁ、やっぱりあれなので、
文字列では収まらないとき、バッファのフラッシュとかをするものとかを
作ろうと思うとHandleを触る必要が出てくるので、
そういうのを考えると任意のHandle操作を行うものをマニピュレータに
すると言うのはあながち無駄では無さそうな気もする。


で、そういうものを考えてみると、
まずひとつ浮かぶのが普通にデータ型+インスタンス
という方法。

data Stars = Stars Int deriving (Show)
instance Manipulator Stars where
  a << (Stars n) = do
    h <- a
    replicateM_ n (hPutChar h '*')
    return h

main = cout<

これはC++でoperator<<を定義したクラスを定義するのに極めて近い。
Haskellならでは?…のカリー化を用いた方法も考えられる。

(<<<) :: IO Handle -> (Handle -> IO a) -> IO Handle
a <<< f = do
  h <- a
  f h
  return h

新たに<<<というオペレータを定義し、
それの第二引数としてコマンドを受け取れるようにする。
すると、

starsf n h = replicateM_ n (hPutChar h '*')

星をhにn個表示する…といった普通の関数を

main = cout<<

このように使用することが出来るようになる。
この方法は個人的には良いんではないかなぁと思うのだが、
いかんせんオペレータを共通化できなかったのが痛い。
制限された関数だけクラスのインスタンスにすることが出来れば
(Handle -> IO a) の関数をManipulatorクラスにすることが出来るのだが、
出来ない…あるいはやり方がわからない。
というか、そもそもHaskellのクラスはそういうもんではないのか。
やはりまぁ、よく分からんというか、結局これだけ書いて文字列解が
一番よさげなのはどうしたものか。