4 ms·
It can be. A value of type `IO foo` is an ordinary, pure value just like a value of any other data type. It can be passed as an argument to a pure function, ret
by tmhedberg 14y ago
It can be. A value of type `IO foo` is an ordinary, pure value just like a value of any other data type. It can be passed as an argument to a pure function, returned from a pure function, manipulated using any functions that can apply to `IO` values, stored inside an arbitrary data structure, composed with other `IO` values to make new ones, etc.
What isn't pure is the process of extracting the `foo` from the `IO foo`, i.e. getting the result of the I/O action. For instance,
getLine :: IO String
is not a function in Haskell, which you can "call" in order to get a string from stdin. It is instead a value which represents an I/O operation yet to be performed. There is no way to force it to yield its result within your entirely pure program (ignoring escape hatches like `unsafePerformIO` and friends), but you can nonetheless compose it with other I/O operations which depend on its result, such as putStrLn:
echo :: IO ()
echo = getLine >>= putStrLn
The above defines a pure value representing an I/O operation which, when performed, will echo a line from stdin to stdout. The `>>=` combines the two pure values to yield a new pure value. Again, there is no way to directly cause this I/O action to be executed, which would necessarily introduce impurity. You can only ensure that it will happen later by threading it into the special I/O operation `Main.main :: IO ()`, which every program must define, and which will be implicitly performed when your program is run.
By reifying "statements with side effects" into simple, pure, first-class values of type `IO`, Haskell lets us remain 100% pure even while our program will eventually have effects on the real world outside of the machine. Haskell code is just a set of pure declarations; the side effects are deferred until after our code has already run (which conceptually happens instantaneously when the program is launched). Since our code runs "before" any side effects occur, it cannot possibly depend on their results.
A Haskell program merely sets up the dominoes in an elaborate arrangement, then looks the other way while the runtime system knocks over the first one.
- Evbn 14y agoThat's not entirely fair. Better to say that Haskell separates the pure from impure, but it has both. The straight line dependencies of the IO sequencing are still part of the Haskell program. The domino analogy could apply to C just as well, except that there are only a few lines of code before the first domino falls.
- tmhedberg 14y agoSorry, but I disagree. A Haskell program emphatically does not have any concept of impurity or statefulness whatsoever, except in the form of `unsafePerformIO`, which circumvents the type system, only comes into play under "extenuating circumstances", and which an ordinary program has no use or need for. The language itself does not have any notion of sequencing or order of evaluation, nor does it need one. It inhabits a pure, mathematical universe where such things have no meaning. Given an arithmetic expression such as (1 + 2) * (3 - 4) it naturally makes no difference which parenthesized expression is evaluated first, because the evaluation of mathematical expressions cannot have side effects. It would be absurd to consider a universe in which such a choice could influence the result of fully evaluating the expression, because such a universe would be wholly unlike our own, to the extent that our intuitive rules of logic and causality would no longer apply. Likewise, given the Haskell expression putStrLn "Hello" >> putStrLn "world!" it makes no difference which of the two IO expressions is evaluated first, because the evaluation of those values cannot have side effects. The second one may be evaluated before the first; it makes no difference. The execution of the resulting action will have side effects and impose sequencing, but there is no way to express execution or cause it to occur in the Haskell language. Execution can only happen "outside" of the context of the program itself, not in the program's abstract and pure universe, but in our own imperative and stateful one, as a side effect of the RTS. A C program, to extend the metaphor, knocks over each domino one by one as soon as it is placed. In fact, placing the domino and knocking it over are one and the same thing. The programmer has no opportunity to leave the room before the side effects come into play, because imperative languages do not distinguish between evaluation and execution. A purely functional language not only draws that distinction, but eliminates execution from the picture entirely, leaving it to some external entity to actually do the deed that the program constructs from pure components.
- millstone 14y agoFrom what you wrote, it sounds like what Haskell buys you is a layer of indirection - the proper analogy is not between a Haskell program and a C program, but instead between a Haskell program and a C compiler. A C compiler does not care in what order it outputs code, so long as it all gets output by the time it finishes. If I understand this, Haskell "compiles" pure code into a sequence of side effects, and then the RTS executes them. So say I wrote a C compiler, that, given a source code, outputs a subcompiler that compiles the source code and then executes it. My C code does the same thing as it would under a traditional compiler, except more slowly - why should I want this?
- millstone 14y agoI appreciate your detailed answer, but I don't understand the distinction - it feels almost like a form of solipsism to me. Say I'm a traveler exploring the programming language universe, and I land on a programming language planet. I wish to determine whether said planet has functions that "you can call in order to get a string from stdin", or whether it "sets up the dominoes in an elaborate arrangement" and "lets the runtime system knock them down". What experiment could I perform? What program could I write that would illustrate the difference?