逆ポーランド計算機

を作ってみた。
dcライクに"p"で表示〜とか考えてたのだが、どうも対話的に動かない。

とか思ってたらreadのせいっぽいね。
こんな感じのソース。

import Char(isSpace)

dropSpace :: String -> String
dropSpace = dropWhile isSpace

data RPNData a	=	RPNNum a
		|	RPNAdd
		|	RPNSub
		|	RPNMul
		|	RPNDiv
		|	RPNPrint

instance Read a => Read (RPNData a) where
	readsPrec _ r =	[(RPNNum x, dropSpace rs)	| (x, rs) <- reads $ dropSpace r] ++
			[(RPNAdd, dropSpace rs)		| ("+", rs) <- lex $ dropSpace r] ++
			[(RPNSub, dropSpace rs)		| ("-", rs) <- lex $ dropSpace r] ++
			[(RPNMul, dropSpace rs)		| ("*", rs) <- lex $ dropSpace r] ++
			[(RPNDiv, dropSpace rs)		| ("/", rs) <- lex $ dropSpace r] ++
			[(RPNPrint, dropSpace rs)	| ("p", rs) <- lex $ dropSpace r]
	readList [] =	[([], [])]
	readList r =	[(x:xs, dropSpace rs)		| (x, r1) <- reads $ dropSpace r
							, (xs, rs) <- reads r1]

eval :: (Fractional a) => [RPNData a] -> IO [a]
eval =	eval' []
	where	eval' acc [] =			return acc
		eval' acc (RPNNum x:xs) =	eval' (x:acc) xs
		eval' (a:b:acc) (RPNAdd:xs) =	eval' (b+a:acc) xs
		eval' (a:b:acc) (RPNSub:xs) =	eval' (b-a:acc) xs
		eval' (a:b:acc) (RPNMul:xs) =	eval' (b*a:acc) xs
		eval' (a:b:acc) (RPNDiv:xs) =	eval' (b/a:acc) xs
		eval' acc@(a:_) (RPNPrint:xs) =	print a >> eval' acc xs
		eval' _ _ =			error "Stack Empty"

main :: IO [Double]
main =	getContents >>= (eval . read)

readまわりがこれでいいのか分からんが動いてるからよしとしよう。


で、対話的に動かしたいのだがreadがあかん。
readListとかreadsPrecだけ見れば遅延評価がちゃんと働きそうなのだが、readが入力の遅延評価殺してて、結局'p'を入力した直後に"print a"が評価されてくれない...。
まぁ、あたりまえと言えば当たり前か...。

read "[1,2,3,4,5]" :: [Int]

とかやったとき、ほんとに[Int]として読めるかは']'まで(というか最後まで)読まないと分からない。
なので

head (read "[1,2,3,4,5]" :: [Int])

とかしても最後まで文字列は読まれてしまう...。


対話的にやりたいならgetLineでぐるぐる回せばいいのかな?
多分そんなことをしないといけんのじゃないかと思いつつ、とりあえずメモ書き。