tags:

views:

453

answers:

4
+2  A: 

To do IO, you have to be in the IO monad:

printInfo :: Body -> IO Body
printInfo b = do
  putStrLn b
  return b

And to call this function from within your runSim function, it to must be inside the IO monad:

runSim :: [Body] -> Integer -> Double -> IO [Body]

(Though there may be better ways of organizing your function.)

This monad business is non-trivial. It is Haskell's greatest strength, but it is difficult to wrap your head around when you first encounter it. I suggest working through a tutorial, such as this one:

http://learnyouahaskell.com/

Specifically, this will get you started:

http://learnyouahaskell.com/input-and-output

There are many tutorials on monads out there that go into much more detail (writing one is the first thing everyone does after they come to grips with them). The links from haskell.org are your friends.

David Crawshaw
thanks, I'll go and do some more reading. Was just getting tired of theory and wanted to get my feet wet :-)
JS
+3  A: 

Regarding

printInfo :: Body -> Body
printInfo b = do
    putStrLn b
    b

Unless "type Body = String", you can't do putStrLn to a Body.

ghci> :t putStrLn
putStrLn :: String -> IO ()

putStrLn requires a String. You can use putStrLn . show, or

$ hoogle "Show a => a -> IO ()"
Prelude print :: Show a => a -> IO ()

use print.

Now, making reasonable assumptions about the type Body, printInfo's type is wrong. As it calls putStrLn it should end with "-> IO Something".

Here:

printBody :: Body -> IO Body
printBody b = do
  print b
  b

Now the last line here is wrong. b is of type Body but stuff there needs to be of IO Body. How can we make this transformation? Using return :: Monad m => a -> m a.

So here's a working version:

printBody :: Body -> IO Body
printBody b = do
  print b
  return b
yairchu
Thanks, very helpful, I think I need to go and study monads and IO in more depth and then come back to this.
JS
+2  A: 

yairchu has a good answer for your problem with printBody. Your central question, how to structure your program so that you can print out each step, is a little harder. Presumably you want to keep runSim, or something like it, pure, since it's just running the simulation, and I/O isn't really its job.

There are two ways I would approach this: either make runSim return an infinite list of simulation steps, or make the I/O wrapper run only one step at a time. I prefer the first option, so I'll start with that.

Change runSim to return a list of steps:

runSim :: [Body] -> Double -> [[Body]]
-- now returns a list of bodys, but no terminating condition
runSim bodys numSteps dtparam = nextBodys : runSim nextBodys dtparam
    where nextBodys = map (integratePos dtparam . integrateVel dtparam) 
                          (calculateForce bodys)

Now main can take as many steps of the simulation as it wants and print them out:

main = mapM_ (mapM_ print) (take 100 $ runSim [earth, sun] 0.05)

Again, I'll assume that, following yairchu's advice, you have Body deriving Show so that print will work. mapM_ is like map, except that it takes a monadic (here, side-effecting) function to map (ends with M) and doesn't return the list (ends with _). So really it's more like for-each in Scheme or something.

The alternative is to keep your runSim and write a print loop that only runs one step at a time:

printLoop :: Integer -> [Body] -> IO [Body]
printLoop 0 bodies = return bodies
printLoop n bodies = do
    let planets = runSim bodies 1 0.05
    mapM_ print planets -- still need to have Body deriving Show
    printLoop (n-1) planets

main = do
    printLoop 100 [earth, sun]
    return ()
Nathan Sanders
thanks, that's exactly what I needed! Yes the infinite list approach seems to be the cleanest.
JS
A: 
JS