あるリストから最初に'x'が現れるまでのリストを取得するには、
h :: [Char] h = takeWhile (/='x') ['a'..]
しますが、これをこのままIOにして、
h' :: IO [Char] h' = takeWhile (/='x') `liftM` mapM (\_ -> getChar) (cycle [1])
とすると、当然ですが、終わらなくなります。
f :: IO [Char]
f = reverse `liftM` f' []
where
f' p = do c <- getChar
if c == 'x' then return p else f' $ c:p
のように自前で再帰するか、再帰部分をunfoldMとして外に出して、
g :: IO [Char]
g = unfoldM g' 0
where
g' _ = do c <- getChar
return $ if c == 'x' then Nothing else Just (c, 0)
unfoldM :: Monad m => (b -> m (Maybe (a, b))) -> b -> m [a]
unfoldM f x = do v <- f x
case v of
Just (a, b) -> unfoldM f b >>= return . (a:)
Nothing -> return []
のようにすれば良いのですが、もう少しシンプルにできる方法はないものでしょうか。