
IMHO there is nothing very surprising here: You have 2 threads with no synchronization between them whatsoever, so you get what you deserve: Undefined behavior. :-)
Well, yes. It feels as if the scheduler is mighty unfair here (delaying the printing indefinitely) but apparently it is allowed to do so - mainly since there is no specification that would require otherwise. But then (seconding your question) what guarantees *do* we have? For a single-threaded program, it would certainly not be OK to execute "main = print ()" as "block immediately"? But when we forkIO this, then it can happen? Possibly related: discussion about (state of formal specification of) GHC RTS memory model at https://mail.haskell.org/pipermail/ghc-devs/2018-November/016583.html - J.W.