1

I need some wrapped on IO type, but I can not 'force' haskell to run IO action in wrapper. I tried using bang pattern for it, but it also does not work.

I recreated problem on much simpler type. Code snippet below

{-# LANGUAGE BangPatterns #-}

newtype IOWrapper a = IOWrapper (IO a) 

mkWrapper :: IO a -> IOWrapper a
mkWrapper !a = IOWrapper a

foo :: IO ()
foo = putStrLn "foo" --Shows "foo" on console, as expected

bar :: IOWrapper ()
bar = IOWrapper $ putStrLn "bar" --Shows nothing

baz :: IOWrapper ()
baz = mkWrapper $ putStrLn "baz" --Shows nothing
t4ccer
  • 83
  • 1
  • 6
  • 1
    Side note: the use of `BangPatterns` doesn’t do anything here for two reasons: first, as others have mentioned in answers, there’s a difference between *evaluating* an expression and *executing* an `IO` action, and `BangPatterns` only controls evaluation; second, `newtype` wrappers don’t exist at runtime, so `mkWrapper !a = IOWrapper a` is essentially equivalent to ``mkWrapper a = a `seq` a``, which means “evaluate `a` before evaluating `a`”. This has no effect because they’re the same `a` (the evaluation will happen only once). – Jon Purdy Sep 03 '20 at 00:38
  • I think this is an XY problem. Wrapping `IO` in this way doesn't really accomplish anything at all. You should explain _why_ you thought you need this; the solution is probably going to be something quite different. – leftaroundabout Sep 04 '20 at 09:36
  • Related: https://stackoverflow.com/questions/24775528/why-cant-haskell-be-tricked-into-performing-io-operations-by-using-strict-evalu . – atravers Nov 17 '20 at 05:32

2 Answers2

6

You cannot do this (without cheating). Evaluating IO does not cause it to perform its effects. The only thing that causes IO to perform effects is to include it in the special action main :: IO a, usually by combining a number of smaller actions into one large IO action.

Instead of asking how to do this thing, back up and think about why you want to do it. Ask instead how to accomplish your original goal.

amalloy
  • 89,153
  • 8
  • 140
  • 205
6

Merely calling putStrLn "foo" does not actually print "foo". Instead, it creates an "action", which then has to be separately "executed", and only then will it print "foo". Moreover, you can actually "execute" it multiple times, and the effect will be produced multiple times as well.

And the only way to "execute" an IO action is to make it the entry point - i.e. the main function - or part of the entry point, or part of another function, which is itself part of the entry point, and so on. The entry point is the root of all effects, there is no effect execution outside of it.

Your foo function probably appears to "work" because you're running it in GHCi, right? If so, then it's executed as part of GHCi's own entry point. GHCi recognizes things of IO type and executes them right away.

But GHCi does not recognize things of the IOWrapper type, so it doesn't unwrap and execute them.

So the only way for you to go is to unwrap and execute yourself:

runIOWrapper :: IOWrapper a -> IO a
runIOWrapper (IOWrapper a) = a

Then you can run your wrapped actions in GHCi like this:

> runIOWrapper bar
bar

> runIOWrapper baz
baz
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • 1
    This can also be written with record syntax: `newtype IOWrapper a = IOWrapper { runIOWrapper :: IO a }`, which is a fairly common pattern with `newtype`s over monads/applicatives – Jon Purdy Sep 03 '20 at 04:49