AKAIK neither one is necessarily safe. You need something like deepseq to fully evaluate the lazy structure.

If you are sure that evaluation to WHNF is sufficient for performing all lazy IO, then I think either evaluate or ($!) will work.

evaluate is probably to be preferred if IO is available, similar to throw vs throwIO.  However I think idioms with seq and ($!) are so common that they need to be supported too. After all, you may have just a 'Monad m' context.

John