This may be due to ready / raw / rare modes; ^C does not always send a signal. It seems likely that readline frees up the terminal, and therefore any signals caused by keyboard input should be triggered by the logic in readline itself; it seems plausible that it can only initiate SIGINT on two consecutive ^C (especially since for many programs using readline, such as shells and REPL, a program exiting on one ^C will be very annoying!).
You may be able to change this behavior using the readline API to re-bind ^C to some of your own code that runs SIGINT. I have not used readline from Haskell, only with C, so I'm not sure exactly how you will do this, but the binding seems rich enough to reach it.
ehird source share