6 ms·
Haskell I/O is pure
- Zr40 14y agoPermalink: http://chris-taylor.github.com/blog/2013/02/09/io-is-not-a-side-effect/ http://chris-taylor.github.com/blog/2013/02/09/io-is-not-a-s...
- crntaylor 14y agoThanks! Stupid of me to miss that.
- ambrop7 14y agoC is also pure. After all, C code is just data until it gets compiled and executed. When and how often this happens is irrelevant. You could write a C interpreter in Haskell, and then your C code would be as pure as anything else in Haskell!
- loup-vaillant 14y agoThat doesn't do monadic I/O justice. An IO value in Haskell is a first class value. Your typical C instruction is not.
- kenko 14y agohttp://conal.net/blog/posts/the-c-language-is-purely-functional http://conal.net/blog/posts/the-c-language-is-purely-functio...
- Chattered 14y agoUnless you are conal, himself, I demand an acknowledgement! http://conal.net/blog/posts/the-c-language-is-purely-functional http://conal.net/blog/posts/the-c-language-is-purely-functio... 1 minute behind kenko! :P
- jeffdavis 14y ago"When and how often this happens is irrelevant." Why do you say that?
- ambrop7 14y agoBecause it's an implementation thing, it has no relevance from a theoretic view on the language. It's like the "compiled/interpreted" label.
- jeffdavis 14y agoI'm not an expert, but my understanding is that IO is not necessarily run even if an expression is evaluated that produces a value of type "IO a". That is a theoretical difference, not just an implementation detail.
- ambrop7 14y agoI just made that argument up, didn't know about Conal's post, thanks for the link!
- dons 14y agoLooks like a reimplementation of Wouter Swiestra's work on a functional model of IO, http://www.staff.science.uu.nl/~swier004/Publications/BeautyInTheBeast.pdf http://www.staff.science.uu.nl/~swier004/Publications/Beauty... Author = {Wouter Swierstra and Thorsten Altenkirch}, Booktitle = {Haskell '07: Proceedings of the ACM SIGPLAN Workshop on Haskell}, Title = {Beauty in the Beast: A Functional Semantics of the Awkward Squad}, Pages = {25--36}, Location = {Freiburg, Germany}, Year = {2007}}
- ibotty 14y agohi dons, that is a nice link that i will check out. (i just read their data types a la carte because of a recommendation by you on stack overflow afair just last week.) i was wondering whether it is possible to restructure the io monad into smaller monads that only do more restricted things. is it possible to do something like that w/o breaking backwards compatibility? and if so, is it possible to really do so in the haskell standards process? i'd really like to reason about programs by looking at a type signature like cat :: FilePath → Term (Teletype :+: FileSystem) () instead of cat :: FilePath → IO ()
- Evbn 14y agoI seem to recall ken shan writing about this. But I can't remember much now. I feel like it is related to "regions" modeling.
- evincarofautumn 14y agoThis kind of thing is usually done with typeclasses. You have the “real” monad, but you only use it via the restricted interface allowed by the typeclass constraints. Off the top of my head: class (MonadIO m) => MonadTT m where ttGetLine :: m String ttGetLine = liftIO getLine ttPutStrLn :: String -> m () ttPutStrLn = liftIO . putStrLn class (MonadIO m) => MonadFS m where fsReadFile :: FilePath -> m String fsReadFile = liftIO . readFile cat :: (MonadTT m, MonadFS m) => FilePath -> m () In reality I don’t think I would structure it quite this way, but you get the general idea. Code to interfaces, not to implementations.
- ibotty 14y agosee https://news.ycombinator.com/item?id=5193368 https://news.ycombinator.com/item?id=5193368 for another submission.
- dschiptsov 14y agoWhat is the point of having this "mini-language"? What would happen to purity if I hit ^D?)
- IsTom 14y agoIt's useful with Safe Haskell, which limits a few things so you can 'safely' run untrusted code. With this interface you could limit IO actions the user is allowed to run.
- millstone 14y agoIf Haskell I/O is pure, why can't it be intermingled with other pure functions?
- jerf 14y agoIt can be. But what gets intermingled with the other functions is indeed the pure language describing what IO actions to take, not their result. For instance, I'd had some good experiences returning IO actions out of STM transactions.
- tmhedberg 14y agoIt 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.
- jeremyjh 14y agoI had an "aha" moment here - very nice little article.
- A1kmm 14y agoIf you want to do this in practice rather than as a specialised example, I would suggest using something like Conduit, rather than reinventing the wheel. Conduit is used widely in the Yesod web framework (for example, to implement the HTTP server), and could easily be hooked up to stdin and stdout for an interactive pure program. Conduit internally looks like this: http://hackage.haskell.org/packages/archive/conduit/0.5.6/doc/html/Data-Conduit-Internal.html http://hackage.haskell.org/packages/archive/conduit/0.5.6/do... - note that you get to choose an underlying monad m and run things in it using PipeM, but you could just make this the Identity monad.
- evincarofautumn 14y agoSo basically, “main” is not an impure function. It’s a pure value representing an impure function. A monad is essentially a data type that represents computations in some domain-specific language, using data (return) and code (>>=) from the host language. The only thing special about the IO datatype is that the impure Haskell runtime can evaluate it.