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