There is a reason for this, and it's not that Haskell is jumping through hoops unnecessarily. All those languages are strictly evaluated, which means that side effects are clearly localized in time and space, we know when and where they happen if we run the program. Haskell is lazy, and laziness doesn't go well together with ad-hoc side effects. Haskell evaluates expressions when the result is needed, and an expression that only side effects and doesn't return a value is never needed.
You could finagle your way around that, but the result would be something like unsafePerformIO in current Haskell, where it's hard work to ensure that it's used correctly.
You could finagle your way around that, but the result would be something like unsafePerformIO in current Haskell, where it's hard work to ensure that it's used correctly.