IO in Haskell (or GHC, more specifically), is deeply magical. A function returning IO a can perform any effect, and a caller does not know what is going on: Just printing something to the terminal, calling a web service or launching the missiles. But it doesn't have to be that way: Using free monads, we can construct tiny subsets of IO functionality, combine them together and even pick the concrete interpreter depending on the environment.
Based on the "Functional Mocking" talk from Lambda Days 2015, with more focus on free structures.
Demo code: https://gist.github.com/larsrh/5cd5652c25ec84b8852c