The issues with effect systems

However, you could instead use an abstract data type based on a newtype declaration:

module Follow (
  Follow, follow,
  openLog, addLog, saveLog, ...
) where
{-- assorted imports -}

newtype Follow a = Follow (IO a)
follow :: Follow a -> IO ()
openLog :: Filepath -> Follow LogFile
addLog :: LogFile -> Message -> Follow ()
saveLog :: LogFile -> Follow () 
                    ⋮

As always, it will depend on what’s required - sometimes an ADT will suffice; other situations may require the granularity provided by the tracking of individual effects. Just remember that there can be more that one way to achieve a result.