I've wanted to get some clarity on the difference between effects and side effects for some time. I think side effects are effects that aren't explicit in the program. So if you have a function with signature `Int -> Int` which also prints something, that is a side effecting program. But if you have a function `Int -> IO Int`, that is not a side effecting function. But it is a function with effects.
To complicate things, funprogrammers often like to talk about effects in a broader sense, such as errors and failure. But these effects are often perfectly pure; the effect is some kind of semantic that is layered over some other, existing function. Like an addition that either returns the result, or `Nothing` if the input is malformed in some way. This effect has nothing to do with IO. So what is a non-side effectful function, that does IO? Is it called an "IO effect"?
To make things even more complicated, apparently expressions with `IO` in them are pure. That is because it is left to the runtime to actually execute the IO action, or something. At this point, you might feel that they are yanking your chain and using the old "just solve this problem by adding another layer of indirection" (this layer being conceptual). I need to read more on this topic.
> Most programs raison d'être is to do IO
You can strengthen that; All programs that are to be evaluated need to do IO in order to do anything useful (heating the CPU is not useful). At least I can't think of any counter examples. Surely people like Haskell programmers know this, and the point of IO was never to ban IO in the first place. Just to make it explicit. (Hint: what's the point of having non-strict semantics if you have to assume that any expression may be side-effecting?)
I've only done a handful of Haskell tutorial, I mostly do Elixir (and sometimes Erlang) when it comes to FP. Elixir/Erlang is most definitely not pure or side-effect free.
I understand how IO can be made explicit, but I don't understand how it can be made pure. An addition might return `Nothing` if the input is malformed in some way as you say, but 1 + 1 will always return 2. On the other hand IO will return different values all the time. You can certainly make all of that explicit, but if given the same input you get different outputs, isn't that the opposite of "pure"?
> I understand how IO can be made explicit, but I don't understand how it can be made pure.
Expressions with IO in them do not "do" IO, they declare a composable specification for how IO is to be done.
When one of those expressions is "main", the runtime will do that specified IO (which, in the case of programs with nontrivial IO, will itself be composed of several other IO actions) when the program is run.
All the expressions containing IO are pure, because what they return every time they are called is the same specification of an IO action comprised of lower-level IO actions.
Values in the IO monad aren't the results of performing IO, they are procedures, in the imperative sense, that do IO. The "main" function of a Haskell program -- because it returns a value in that monad -- returns such a procedure, which the runtime executes.
To complicate things, funprogrammers often like to talk about effects in a broader sense, such as errors and failure. But these effects are often perfectly pure; the effect is some kind of semantic that is layered over some other, existing function. Like an addition that either returns the result, or `Nothing` if the input is malformed in some way. This effect has nothing to do with IO. So what is a non-side effectful function, that does IO? Is it called an "IO effect"?
To make things even more complicated, apparently expressions with `IO` in them are pure. That is because it is left to the runtime to actually execute the IO action, or something. At this point, you might feel that they are yanking your chain and using the old "just solve this problem by adding another layer of indirection" (this layer being conceptual). I need to read more on this topic.
> Most programs raison d'être is to do IO
You can strengthen that; All programs that are to be evaluated need to do IO in order to do anything useful (heating the CPU is not useful). At least I can't think of any counter examples. Surely people like Haskell programmers know this, and the point of IO was never to ban IO in the first place. Just to make it explicit. (Hint: what's the point of having non-strict semantics if you have to assume that any expression may be side-effecting?)