putStrLn =<< show . sum . take 3000000 . randomRs (0, 100::Int) `fmap` newStdGen
::Int is necessary to nail down exactly which numeric type we're dealing with, as nothing else in the expression nails down an exact type.
Personally I prefer loading Control.Applicative and using <$> where the `fmap` is, but I thought I'd keep the dependencies low. This demonstrates both how adding IO to an expression can be a bit of a pain, and that Haskell has come up with tools to deal with this without turning it into the naive "do" block with three or four lines a newbie would turn out.
I wouldn't expect miracles on the random number generation speed. Also in this context you may not be getting the best optimizations that you would get if you really cared. It so happens that Real World Haskell uses a virtually identical example in its optimization chapter, where by the end they get down to what is essentially the optimal non-SIMD assembler, if you're really interested in the dirty details: http://book.realworldhaskell.org/read/profiling-and-optimiza...
What if random generation played a role, how would that look and what times do you get, just curious?