this is the bouncing ball code. I'm trying to make 'appendFile' run on the update function, so when the ball bounces off the wall, then 'appendFile' will write the px and px values to the file "log.txt"
import Graphics.Gloss
import Graphics.Gloss.Data.ViewPort (ViewPort)
main :: IO ()
main =
simulate
(InWindow "Bouncing ball" (width, height) (50, 50))
white
30
initial
view
update
but I'm having trouble because 'appendFile' only runs on signiture IO. And I don't know how to apply it in a situation like this
update :: ViewPort -> Float -> World -> World
update _ _ World {position = (px, py), velocity = (vx, vy)} =
let
appendFile "Log.txt" ("" ++ show px ++ " " + show py ++ "")
newPx = px + vx
newPy = py + vy
newVx = if newPx >= fromIntegral width || newPx <= 0 then - vx else vx
newVy = if newPy >= fromIntegral height || newPy <= 0 then - vy else vy
in World {position = (newPx, newPy), velocity = (newVx, newVy)}
Haskell is really strict about side effects. Writing to a file is a side effect, and a pure function (like your
update) is not allowed to have side effects.If you merely want to record the data for debugging then you can use the infamous accursed
unsafePerformIO, which provides a back door into the IO monad for pure computations. The reason for the "unsafe" bit of the name is that this makes no promises about how often the IO action gets run, or even if it gets run at all.BUT the code you have above won't actually call
appendFile. In fact that is a syntax error; aletintroduces values which might be used in the code, but you have no assignment for the result ofappendFile.You would need something more like:
seqis a magic function which is "strict" in its first argument, so theunsafePerformIOgets evaluated before the newWorld, even though nothing ever uses the result of that first argument.However this is a kluge. You should not use
unsafePerformIOfor production code. It is a lie to the compiler. If you make a habit of it then the compiler will get its revenge, and it will not be pretty.If this is for production code then you should instead use
simulateIO. This takes an update function which returns anIOvalue, so then you can writeupdateto return anIO Worldand everyone will be happy.