Optimizing Haskell for performance can be tricky. When I wrote a functional image library (updated at [0]) I tried to get the frame draw rate down by using strictness, unpacked records, and so on, but one of the changes that made the biggest difference came from changing round to truncate[1], I went from 120 ms/frame to 65 ms/frame! The reason why is because round has quite a bit of logic associated with it[2] in Haskell's standard library, versus a simple one-liner for truncate.
It sort of shows that in even simple functions such as this, underlying implementation details can have an impact on performance.
Also, for some reason filling a C array in Haskell was faster than doing it in C, probably due to the switching between RTS.
[0] https://github.com/siraben/functional-images
[1] https://github.com/sbond75/OpenGLTesting/commit/116384edd6a3...
[2] https://hackage.haskell.org/package/base-4.14.0.0/docs/src/G...