const io_actions = ["hello", "world"].map(s => console.log(s));
io_actions[1];
vs. io_actions = map putStrLn ["hello", "world"]
main = io_actions !! 1
JS will print out "hello" and "world" and the value of io_actions[0] is undefined. In Haskell, the value of io_actions !! 0 is IO "world" which is just a value you can pass around in your program. Only when the Haskell runtime evaluates your code is when it will do the effect, which is to print "world". "hello" is never printed.Naturally you can also pattern match on IO ():
f :: IO ()
f = case putStrLn "hello" of
_ -> putStrLn "world"
also only prints "world".The IO type in Haskell is akin to IO (State World -> (State World, a)), ie. the state monad where the state threaded through the computation is the "real world". This can be demonstrated with the following:
f :: IO ()
f = IO $ \world -> case putStrLn "foo" of
IO _ -> case putStrLn "bar" of
IO g -> g world
prints "bar".