Haskell gloss 库,如何 运行 appendFile on model signature for log to file?

Haskell gloss library, how to run appendFile on model signiture for log to file?

这是弹跳球代码。我试图在更新函数上设置 'appendFile' 运行,所以当球从墙上反弹时,'appendFile' 会将 px 和 px 值写入文件“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

但我遇到了麻烦,因为 'appendFile' 只有 运行 签名 IO。而且我不知道如何在这种情况下应用它

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 对副作用非常严格。写入文件是一种副作用,纯函数(如您的update)不允许有副作用。

如果您只是想记录数据以进行调试,那么您可以使用臭名昭著的 accursed unsafePerformIO,它为纯计算提供了进入 IO monad 的后门。名称中“不安全”位的原因是,这并没有保证 IO 操作获得 运行 的频率,甚至根本没有获得 运行。

但是您上面的代码实际上不会调用 appendFile。事实上这是一个语法错误; let 引入了可能在代码中使用的值,但您没有为 appendFile.

的结果赋值

你需要更多类似的东西:

let
   ... omitted
in seq 
    (unsafePerformIO $ appendFile "Log.txt" (show px ++ " " ++ show py ++ "\n")
    World {position = (newPx, newPy), velocity = (newVx, newVy)}

seq 是一个魔术函数,它的第一个参数是“严格的”,所以 unsafePerformIO 在新的 World 之前被评估,即使没有任何东西使用结果第一个参数。

但是这是一个错误。您不应将 unsafePerformIO 用于生产代码。这是一个lie to the compiler。如果你习惯了,那么编译器 报复,它不会很漂亮。

如果这是用于生产代码,那么您应该改用 simulateIO。这需要一个更新函数,其中 return 是一个 IO 值,因此您可以将 update 写入 return 和 IO World,每个人都会很高兴。