Tracking characters and a timestamp ?

============= ======================================================= {- This program should read characters from stdin, stop on 's' and print the character along with a timestamp You need ghc-6.6 because of Date.Time. When not using mapM addTimeCode I'll get the lines printed on stdout instantly -} import Data.Time.Clock import Data.Time import Control.Monad handleChar :: Show a => (Char, a) -> IO () handleChar ('s', _) = exitWith (ExitFailure 1) handleChar tuple = print tuple addTimeCode a = liftM ( (,) a) getCurrentTime main = do hSetBuffering stdin NoBuffering hGetContents stdin >>= mapM addTimeCode >>= mapM_ handleChar ============= ====================================================== When running and typing a b c it should print ('a', <timestamp>) ('b', <timestamp>) ('c', <timestamp>) It seems to wait till the end of the infinite list. Why? When using a take 10 I'll get those 10 events after having typed the 10th character. Do I need continuation passing style? Would unsafeInterleaveIO help? The final goal is writing a timetracker which sums up the time you've spent on different wmii tags (= you can think of them beeing different screens ) Marc Weber

Marc Weber:
main = do hSetBuffering stdin NoBuffering hGetContents stdin >>= mapM addTimeCode >>= mapM_ handleChar
It seems to wait till the end of the infinite list. Why?
The sequencing imposed by the IO monad means that the first mapM must complete before the second can start. To see this, you can try the following:
main = do hSetBuffering stdin NoBuffering hGetContents stdin >>= mapM_ (\c -> addTimeCode c >>= handleChar)

Hello Matthew, Thursday, April 5, 2007, 2:00:03 AM, you wrote:
main = do hSetBuffering stdin NoBuffering hGetContents stdin >>= mapM addTimeCode >>= mapM_ handleChar
It seems to wait till the end of the infinite list. Why?
The sequencing imposed by the IO monad means that the first mapM must complete before the second can start. To see this, you can try the following:
or, alternatively, replace first mapM_ with unsafeInterleavedMapM: unsafeInterleavedMapM f (x:xs) = do a <- f x as <- unsafeInterleaveIO (unsafeInterleavedMapM f xs) return (a:as) (not tested) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Jump to question 1 and question 2 most simple streaming example: ============= ======================================================= module Main where import System.IO import Control.Monad main = do lines <- liftM lines getContents mapM_ print lines -- * =========== ======================================================= Matthiew:
The sequencing imposed by the IO monad means that the first mapM must complete before the second can start. This does not apply to the example above, does it? This example behaves like a pipe output is written before all input has been read. But we have 2 IO actions, don't we?: lines <- liftM lines getContents and mapM_ print lines
question 1: So this example should "hang", as well, shouldn't it? Bulat: Its getting interesting: This works as expected. ============= ======================================================= module Main where import Data.Time.Clock import Data.Time import Control.Monad import System.Exit import System.IO import System.IO.Unsafe handleChar :: Show a => (Char, a) -> IO () handleChar ('s', _) = exitWith (ExitFailure 1) handleChar tuple = print tuple addTimeCode a = liftM ( (,) a) getCurrentTime main = do hSetBuffering stdin NoBuffering liftM (take 4) (hGetContents stdin) >>= unsafeInterleavedMapM addTimeCode >>= mapM print unsafeInterleavedMapM f (x:xs) = do a <- f x as <- unsafeInterleaveIO (unsafeInterleavedMapM f xs) return (a:as) ============= ======================================================= When also using unsafeInterleavedMapM for the second mapM the program will stop after processing the first list item. question 2 I can't see why this is the case. Continuation does work as well: ============= continuation example =================================== module Main where import Control.Monad.Cont import System.Exit import System.IO import Data.Time takeChar [] = exitWith ExitSuccess takeChar (c:cs) = do print c print =<< getCurrentTime when (c =='s') $ exitWith $ ExitFailure 1 takeChar cs main = do hSetBuffering stdin NoBuffering getContents >>= takeChar ============= ======================================================= Marc

Marc Weber:
main = do lines <- liftM lines getContents mapM_ print lines -- *
So this example should "hang", as well, shouldn't it?
It would, except for the magic of unsafeInterleaveIO. It doesn't "hang" because getContents uses unsafeInterleaveIO internally to return the file contents lazily. If you took the unsafeInterleaveIO out of getContents, your program would wait for EOF on input before printing anything to output, as you had been expecting. Bulat's post showed how to add the same kind of magic into your use of mapM.

Hello Marc, Thursday, April 5, 2007, 8:40:04 AM, you wrote:
Bulat: When also using unsafeInterleavedMapM for the second mapM the program will stop after processing the first list item. question 2 I can't see why this is the case.
because there is no need to calculate entire answer before return. unsafeInterleavedMapM is written in the manner that only first list item will be obligatory calculated, all other only on demand :))) you definitely should read http://haskell.org/haskellwiki/IO_inside
Continuation does work as well:
this code is 100% equivalent of (mapM_ (c-> ...)) proposed earlier -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On 4/5/07, Bulat Ziganshin
you definitely should read http://haskell.org/haskellwiki/IO_inside
Thanks for mentioning this link -- I wasn't aware of it. I wish it existed when I first started learning Haskell... -- Rich AIM : rnezzy ICQ : 174908475 Jabber: rich@neswold.homeunix.net
participants (4)
-
Bulat Ziganshin
-
Marc Weber
-
Matthew Brecknell
-
Rich Neswold