3 ms·
Not exactly. When using the I/O monad in a pure language like Haskell, your code doesn't directly have side effects, instead you're building a container (a mona
by tinco 5y ago
Not exactly. When using the I/O monad in a pure language like Haskell, your code doesn't directly have side effects, instead you're building a container (a monad) of instructions of what side effect having operations to do, and what should be done with the results. The Haskell runtime then takes this monad and unwraps it layer by layer, each layer executing the side effect having instruction and using the results of the side effects as input for unwrapping the next layer.
You could argue that because of this system the language itself is pure and without side effects and without mutation.
Note that the effect of this system is exactly the same as a language that does have side effects. They are functionally identical. The nicety of Haskell's purity comes from how the monads allow you to organize your code.
By the way, not sure if this is controversial but JavaScript's I/O system is a lot like the I/O monad. I/O doesn't really get executed immediately, and you can't directly operate on the results of I/O. The runtime executes the I/O after all JavaScript's been executed, and you use callbacks to tell the runtime to do whenever the I/O data returns.
Of course JavaScript does allow for mutation, so it's not pure in that sense.
- lmm 5y ago> instead you're building a container (a monad) of instructions of what side effect having operations to do, and what should be done with the results. The Haskell runtime then takes this monad and unwraps it layer by layer, each layer executing the side effect having instruction and using the results of the side effects as input for unwrapping the next layer. Note that this is the implementation of a specific monad - not all monads are like that. Many monads are not containers, and some can't be "unwrapped" at all. > Note that the effect of this system is exactly the same as a language that does have side effects. They are functionally identical. The nicety of Haskell's purity comes from how the monads allow you to organize your code. You might enjoy "the C language is purely functional": http://conal.net/blog/posts/the-c-language-is-purely-functional http://conal.net/blog/posts/the-c-language-is-purely-functio... . The useful part of purity is that the pure form of Haskell has some interesting algebraic structure (in particular there are a lot of ways to refactor code while preserving its semantics), whereas the pure form of C or javascript does not have many equivalence laws. > By the way, not sure if this is controversial but JavaScript's I/O system is a lot like the I/O monad. I/O doesn't really get executed immediately, and you can't directly operate on the results of I/O. The runtime executes the I/O after all JavaScript's been executed, and you use callbacks to tell the runtime to do whenever the I/O data returns. I think you're conflating interface and implementation here. Javascript I/O is implemented similarly to Haskell I/O in some ways, yes. But the interesting part of Haskell's I/O isn't the implementation, it's the (pure) API that it uses, and Javascript has nothing like that.
- tinco 5y ago> But the interesting part of Haskell's I/O isn't the implementation, it's the (pure) API that it uses, and Javascript has nothing like that. No, I insist JavaScript really has a pure approach to I/O, quite like Haskell. There is literally no side effects except for application state mutation from the perspective of a JavaScript function (if you disregard the few deprecated synchronous API's). And this pure API has proven very useful and distinctive for JavaScript (i.e. through node.js)
- lmm 5y ago> There is literally no side effects except for application state mutation from the perspective of a JavaScript function (if you disregard the few deprecated synchronous API's). That's a pretty big "except" - there are no side effects except for all the side effects (and there's nothing unique about JavaScript there, that much is the same as almost any other language). JavaScript's approach to I/O, like most languages, uses things that look like functions but aren't functions, in that they give different outputs for the same inputs. The whole point of Haskell's I/O system, the thing that separates it from other languages, is that it doesn't do that; in Haskell your functions are actually functions and I/O operations are very clearly not functions.
- tinco 5y ago> That's a pretty big "except" - there are no side effects except for all the side effects (and there's nothing unique about JavaScript there, that much is the same as almost any other language) It's not like Haskell doesn't have those either though, I don't think it's a very big except. Discussing synchronous I/O in JavaScript is not interesting, were not littering our Haskell with unsafePerformIO either. > JavaScript's approach to I/O, like most languages, uses things that look like functions but aren't functions, in that they give different outputs for the same inputs. No, this is just not true, are we using the same JavaScript? JavaScript's functions change only based on modified application state, not on any other side effect. > The whole point of Haskell's I/O system, the thing that separates it from other languages, is that it doesn't do that; in Haskell your functions are actually functions and I/O operations are very clearly not functions. Sure, in the current Haskell that's true. In previous iterations of Haskell this wasn't always the case though, monadic I/O wasn't the first pure system, we also had stream based and continuation/callback based I/O, the latter being basically what JavaScript does.