
Hello, The key word here is "if I give asSting an input of IO "myString". Any function that returns and IO "myString" (this is really a misnomer, an (IO String) is a black box that stores the string obtained from an external source, but in an implementation-dependent, not-visible-to-an-user way) can do an arbitrary interaction with the real world to obtain the value. It can look at the keyborad input, read a file, connect to some networked resource, etc. That's why, this is not a pure function because every time you call it, it may return a different string. Or simply put, there's no legal way to write a function with that signature (okay, you can make it happen by using the unsafePerformIO function, but beware, it's called "unsafe" for a reason. It can cause a whole lot of seemingly "magic", unexpected behavior. In general, don't use it unless you really know it won't bite you) I don't know how to explain it more thoroughly. If you want to get deeper into Haskell, it could be said that ANY Haskell function or value is "pure" - for example an (IO String) value is always the same, well, (IO String) value, but the String "inside" may differ (just like a (Maybe String) or [String] is a wholly different beast than a mere String) If you want to connect pure functions to impure function do it the other way around - promote pure functions to IO (or any other monad). For example, you can use any pure function when using do notation easily. You can also compose any function of type (a -> b) with return to create a function (a -> m b). IO, being a monad, also implements many type classes from here: https://wiki.haskell.org/Typeclassopedia so you can turn a (a -> b) function into a (IO a -> IO b) function with just a fmap or <$>, for example. Best regards, Marcin Mrotek