Does there exist or how much fuss would it be to implement a setTimeout function in Haskell?
The main idea is
setTimeout someFunction 1000 --someFunction will be called in 1000ms = 1s
I am new to Haskell and probably will not use this; I am learning Haskell for the kick of it.
A game's event loop usually requires you to
Managing time is a great deal (hence setTimeout) here to ensure constant FPS.
How would a pattern like this look on Haskell?
Does there exist or how much fuss would it be to implement a setTimeout function in Haskell?
JavaScript's setTimeout
and other similar methods like Qt's QTimer usually work within a single event loop in a single thread (not counting web workers or QThread
). Modelling that exact behaviour is a little bit hard, although not impossible, with Haskell, as GHC already provides an (internal) implementation .
If you don't care for the actual single-threaded behaviour (which also means that two functions with almost the same timeout can possibly execute at the same time), then you can simply fork a new thread and delay its action:
doLater :: Int -> IO () -> IO ThreadId
doLater ms io = forkIO $ threadDelay (ms * 1000) >> io
For any further thoughts, we would actually need to know more about your specific event loop.
How would a pattern like this look on Haskell?
Very, very generalised, you would need something like this:
mkGame :: World
-> (Input -> World -> World)
-> (TimeDiff -> World -> World)
-> (World -> GraphicalRepresentation)
-> IO ()
mkGame initialWorld handleInput handleProgression drawWorld = undefined
Note that you can probably throw other arguments in there, such as the maximum number of world updates per second.
How could we now model setTimeout? Assume that World
is something like:
data World = World {
getWorldData :: WorldData,
getCurrentTime :: TimePoint, -- would get updated by your loop
getTimeouts :: Queue TimePoint (World -> World)
}
where Queue
is any working priority queue ( pick one, there are many, or build your own). Now you can simply set a timeout by using
setTimeout world delay action = world { getTimeouts = timeouts' }
where timeouts' = insert (getTimeouts world) t action
t = delay + getCurrentTime world
-- insert :: Queue p v -> p -> v -> Queue p v
If you want to be able to cancel timeouts, you also need a key on Queue
, have a look at GHC.Event.PSQ for inspiration.
Aside from that you can now check whether the time has passed and act accordingly in your game loop by simply going through the queue, and applying all your functions.
This is basically a very simple and crude foundation you can use to inspire your own work on. Depending on what you actually want to do, you might want to have a look at gloss
, which already implements a very similar concept, although without timeouts, but in this case you can still add the timeouts and the total time difference in to your world and simply use a fitting update function (the TimeDiff -> World -> World
part).
The question doesn't make sense for pure functions since Haskell does lazy evaluation. A function will be called when its results are needed, not any earlier, and not later.
However, if you have some IO operation you want delayed, you can in GHC use the threadDelay
function from Control.Concurrent
. Your setTimeout
would then be implemented as
setTimeout :: IO () -> Int -> IO ThreadId
setTimeout ioOperation ms =
forkIO $ do
threadDelay (ms*1000)
ioOperation
This is an old question, but I'm not satisfied with the answers (and none have been marked as correct), as there's too much focus on the reimplementing setTimeout
function, which I think is a misinterpretation of what setTimeout is used for in making games with javascript.
Can I assume you're wanting a replacement for this javascript pattern?
function update() {
undefined;
setInterval(update, 1000)
}
Game loops outside of Javascript are not usually written using setTimeout
, setTimeout is a work around from the asynchronous nature of JS in the browser. as an aside, it's not recommended to use setTimeout
for time critical visual code, requestAnimationFrame
is the optimal function for this.
The idiomatic method for programming a game loop is a simple loop, pseudo haskell code would look like this:
draw :: World -> IO ()
draw w = do
undefined
glSwapBuffers
updateWorld :: World -> World
updateWorld w = undefined
loop World -> Time -> IO ()
loop world time = do
inputs <- getInputs
time' <- getTime
let delta = time' - time
let world' = updateWorld delta world inputs
draw world'
loop world' time'
setup :: IO World
setup = do
undefined -- window setup etc
world <- undefined
return world
main :: IO ()
main = do
w <- setup
t <- getTime
loop w t
Your draw
function should call glSwapBuffers
(or equivalent), which on most devices will block until vsync, that'll maintain a smooth fps, But do not assume that all computers have vsync enabled, and do not assume that all monitors are 60hz, 144hz and variable frame rate technology is now prevalent in the gamer community.
A loop like this will also safely degrade if it cannot make the 16.67ms window (unlike anything using forkIO to build a loop), though haskell's microthreads, make it not insane to program using them, but make sure use of them is thread safe, for example uses something like STM .
the clock or time packages will provide the getTime function.
Zeta's mkGame
is more functional style (as it takes functions as arguments), and can be easily adapted to my code.
Also note, libraries like sdl, provide their own loop and input, and timekeeping methods, which may simplify (or complicate) your code.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.