Using a C bindings doesn’t always force you to use some imperative style programming.
In sdl2, for example, you don’t need to use callbacks, so if you want to adopt a functional style, you can pass your functional state through all the three main parts of the program. Event processing, update of the state, and display. The complete result could even be seen as purelly functional, and deterministic. The user input can be seen as a discrete input list of events, and the display function can be seen as a pure function, which transforms the input state into a result image. If you decide to display at a fixed fps (frames par seconds), the update function can be made to update the state at a fixed number of milliseconds, and if you use integers for calculations, you can get a deterministic result.
If you see an unexpected behavior while testing, and if you log user inputs in a list, we can reproduce it while providing the same inputs again in a non-interactive way, and the result can be a list of images.
let img_list, final_state =
let initial_state = initialise_state () in
List.fold_left (fun (imgs, state) event ->
let state = process_event state event in
let state = update state in
let img = render_state state in
(img::imgs, state)
) ([], initial_state) events_list
The three values state
, event_list
and img_list
can be functional, as long as the three functions in the loop. The result can be seen as a nice functional “Model-View-Controler” pattern, for functional programming adepts.
If the UI library provides callbacks, references can be used in a very localised way.
And with javascript backends, it even helps a lot to create fixed fps rates.
Writing everything in a functional way is often quite chalenging though.
And it often looks like a mind teaser puzzle.
While writing this shmup I was not able to figure out how to filter the player bullets and the enemies in a functional way. These are 2 lists, and every elements of each list have to be compared in pairs (which is a cartesian-product comparision of the elements, while preserving the initial structure of the input lists with no duplications). I have only been able to do it several days ago, with the help of claude-3-sonnet, which provided me this tower-defense for study, where there is also a list of enemies and a list of towers.
For this problem, 2 nested for loops (or 2 nested List.iter), with mutable fields (or a list ref) is much easier to do. This is where we like this programming language to be impure, so that we can use the imperative style as a rescue solution, before to solve these kind of functional puzzles.