What other ways can state be handled in a pure functional language besides with Monads? - haskell

So I started to wrap my head around Monads (used in Haskell). I'm curious what other ways IO or state can be handled in a pure functional language (both in theory or reality). For example, there is a logical language called "mercury" that uses "effect-typing". In a program such as haskell, how would effect-typing work? How does other systems work?

There are several different questions involved here.
First, IO and State are very different things. State is easy to do
yourself: Just pass an extra argument to every function, and return an extra
result, and you have a "stateful function"; for example, turn a -> b into
a -> s -> (b,s).
There's no magic involved here: Control.Monad.State provides a wrapper that
makes working with "state actions" of the form s -> (a,s) convenient, as well
as a bunch of helper functions, but that's it.
I/O, by its nature, has to have some magic in its implementation. But there are
a lot of ways of expressing I/O in Haskell that don't involve the word "monad".
If we had an IO-free subset of Haskell as-is, and we wanted to invent IO from
scratch, without knowing anything about monads, there are many things we might
do.
For example, if all we want to do is print to stdout, we might say:
type PrintOnlyIO = String
main :: PrintOnlyIO
main = "Hello world!"
And then have an RTS (runtime system) which evaluates the string and prints it.
This lets us write any Haskell program whose I/O consists entirely of printing
to stdout.
This isn't very useful, however, because we want interactivity! So let's invent
a new type of IO which allows for it. The simplest thing that comes to mind is
type InteractIO = String -> String
main :: InteractIO
main = map toUpper
This approach to IO lets us write any code which reads from stdin and writes to
stdout (the Prelude comes with a function interact :: InteractIO -> IO ()
which does this, by the way).
This is much better, since it lets us write interactive programs. But it's
still very limited compared to all the IO we want to do, and also quite
error-prone (if we accidentally try to read too far into stdin, the program
will just block until the user types more in).
We want to be able to do more than read stdin and write stdout. Here's how
early versions of Haskell did I/O, approximately:
data Request = PutStrLn String | GetLine | Exit | ...
data Response = Success | Str String | ...
type DialogueIO = [Response] -> [Request]
main :: DialogueIO
main resps1 =
PutStrLn "what's your name?"
: GetLine
: case resps1 of
Success : Str name : resps2 ->
PutStrLn ("hi " ++ name ++ "!")
: Exit
When we write main, we get a lazy list argument and return a lazy list as a
result. The lazy list we return has values like PutStrLn s and GetLine;
after we yield a (request) value, we can examine the next element of the
(response) list, and the RTS will arrange for it to be the response to our
request.
There are ways to make working with this mechanism nicer, but as you can
imagine, the approach gets pretty awkward pretty quickly. Also, it's
error-prone in the same way as the previous one.
Here's another approach which is much less error-prone, and conceptually very
close to how Haskell IO actually behaves:
data ContIO = Exit | PutStrLn String ContIO | GetLine (String -> ContIO) | ...
main :: ContIO
main =
PutStrLn "what's your name?" $
GetLine $ \name ->
PutStrLn ("hi " ++ name ++ "!") $
Exit
The key is that instead of taking a "lazy list" of responses as one big
argument at he beginning of main, we make individual requests that accept one
argument at a time.
Our program is now just a regular data type -- a lot like a linked list, except
you can't just traverse it normally: When the RTS interprets main, sometimes
it encounters a value like GetLine which holds a function; then it has to get
a string from stdin using RTS magic, and pass that string to the function,
before it can continue. Exercise: Write interpret :: ContIO -> IO ().
Note that none of these implementations involve "world-passing".
"world-passing" isn't really how I/O works in Haskell. The actual
implementation of the IO type in GHC involves an internal type called
RealWorld, but that's only an implementation detail.
Actual Haskell IO adds a type parameter so we can write actions that
"produce" arbitrary values -- so it looks more like data IO a = Done a |
PutStr String (IO a) | GetLine (String -> IO a) | .... That gives us more
flexibility, because we can create "IO actions" that produce arbitrary
values.
(As Russell O'Connor points out,
this type is just a free monad. We can write a Monad instance for it easily.)
Where do monads come into it, then? It turns out that we don't need Monad for
I/O, and we don't need Monad for state, so why do we need it at all? The
answer is that we don't. There's nothing magical about the type class Monad.
However, when we work with IO and State (and lists and functions and
Maybe and parsers and continuation-passing style and ...) for long enough, we
eventually figure out that they behave pretty similarly in some ways. We might
write a function that prints every string in a list, and a function that runs
every stateful computation in a list and threads the state through, and they'll
look very similar to each other.
Since we don't like writing a lot of similar-looking code, we want a way to
abstract it; Monad turns out to be a great abstraction, because it lets us
abstract many types that seem very different, but still provide a lot of useful
functionality (including everything in Control.Monad).
Given bindIO :: IO a -> (a -> IO b) -> IO b and returnIO :: a -> IO a, we
could write any IO program in Haskell without ever thinking about monads. But
we'd probably end up replicating a lot of the functions in Control.Monad,
like mapM and forever and when and (>=>).
By implementing the common Monad API, we get to use the exact same code for
working with IO actions as we do with parsers and lists. That's really the only
reason we have the Monad class -- to capture the similarities between
different types.

Another major approach is uniqueness typing, as in Clean. The short story is that handles to state (including the real world) can only be used once, and functions that access mutable state return a new handle. This means that an output of the first call is an input of a second, forcing the sequential evaluation.
Effect typing is used in the Disciple Compiler for Haskell, but to the best of my knowledge it would take considerable compiler work to enable it in, say, GHC. I shall leave discussion of the details to those better-informed than myself.

Well, first what is state? It can manifest as a mutable variable, which you don't have in Haskell. You only have memory references (IORef, MVar, Ptr, etc.) and IO/ST actions to act on them.
However, state itself can be pure as well. To acknowledge that review the 'Stream' type:
data Stream a = Stream a (Stream a)
This is a stream of values. However an alternative way to interpret this type is a changing value:
stepStream :: Stream a -> (a, Stream a)
stepStream (Stream x xs) = (x, xs)
This gets interesting when you allow two streams to communicate. You then get the automaton category Auto:
newtype Auto a b = Auto (a -> (b, Auto a b))
This is really like Stream, except that now at every instant the stream gets some input value of type a. This forms a category, so one instant of a stream can get its value from the same instant of another stream.
Again a different interpretation of this: You have two computations that change over time and you allow them to communicate. So every computation has local state. Here is a type that is isomorphic to Auto:
data LS a b =
forall s.
LS s ((a, s) -> (b, s))

Take a look at A History of Haskell: Being Lazy With Class. It describes two different approaches to doing I/O in Haskell, before monads were invented: continuations and streams.

There is an approach called Functional Reactive Programming that represents time-varying values and/or event streams as a first-class abstraction. A recent example that comes to my mind is Elm (it is written in Haskell and has a syntax similar to Haskell).

I'm curious - what other ways I/O or state can be handled in a pure functional language (both in theory or reality)?
I'll just add to what's already been mentioned here (note: some of these approaches don't seem to have one, so there are a few "improvised names").
Approaches with freely-available descriptions or implementations:
"Orthogonal directives" - see An alternative approach to I/O by Maarten Fokkinga and Jan Kuper.
Pseudodata - see Nondeterminism with Referential Transparency in Functional Programming Languages by F. Warren Burton. The approach is used by Dave Harrison to implement clocks in his thesis
Functional Real-Time Programming: The Language Ruth And Its Semantics, and name supplies in the functional pearl On generating unique names by Lennart Augustsson, Mikael Rittri and Dan Synek; there are also a few library implementations in Hackage.
Witnesses - see Witnessing Side Effects by Tachio Terauchi and Alex Aiken.
Observers - see Assignments for Applicative Languages by Vipin Swarup, Uday S. Reddy and Evan Ireland.
Other approaches - references only:
System tokens:
L. Augustsson. Functional I/O Using System Tokens. PMG Memo 72, Dept Computer Science, Chalmers University of Technology, S-412 96 Göteborg, 1989.
"Effect trees":
Rebelsky S.A. (1992) I/O trees and interactive lazy functional programming. In: Bruynooghe M., Wirsing M. (eds) Programming Language Implementation and Logic Programming. PLILP 1992. Lecture Notes in Computer Science, vol 631. Springer, Berlin, Heidelberg.

Related

Is the monadic IO construct in Haskell just a convention?

Regarding Haskell's monadic IO construct:
Is it just a convention or is there is a implementation reason for it?
Could you not just FFI into libc.so instead to do your I/O, and skip the whole IO-monad thing?
Would it work anyway, or is the outcome nondeterministic because of:
(a) Haskell's lazy evaluation?
(b) another reason, like that the GHC is pattern-matching for the IO monad and then handling it in a special way (or something else)?
What is the real reason - in the end you end up using a side effect, so why not do it the simple way?
Yes, monadic I/O is a consequence of Haskell being lazy. Specifically, though, monadic I/O is a consequence of Haskell being pure, which is effectively necessary for a lazy language to be predictable.†
This is easy to illustrate with an example. Imagine for a moment that Haskell were not pure, but it was still lazy. Instead of putStrLn having the type String -> IO (), it would simply have the type String -> (), and it would print a string to stdout as a side-effect. The trouble with this is that this would only happen when putStrLn is actually called, and in a lazy language, functions are only called when their results are needed.
Here’s the trouble: putStrLn produces (). Looking at a value of type () is useless, because () means “boring”. That means that this program would do what you expect:
main :: ()
main =
case putStr "Hello, " of
() -> putStrLn " world!"
-- prints “Hello, world!\n”
But I think you can agree that programming style is pretty odd. The case ... of is necessary, however, because it forces the evaluation of the call to putStr by matching against (). If you tweak the program slightly:
main :: ()
main =
case putStr "Hello, " of
_ -> putStrLn " world!"
…now it only prints world!\n, and the first call isn’t evaluated at all.
This actually gets even worse, though, because it becomes even harder to predict as soon as you start trying to do any actual programming. Consider this program:
printAndAdd :: String -> Integer -> Integer -> Integer
printAndAdd msg x y = putStrLn msg `seq` (x + y)
main :: ()
main =
let x = printAndAdd "first" 1 2
y = printAndAdd "second" 3 4
in (y + x) `seq` ()
Does this program print out first\nsecond\n or second\nfirst\n? Without knowing the order in which (+) evaluates its arguments, we don’t know. And in Haskell, evaluation order isn’t even always well-defined, so it’s entirely possible that the order in which the two effects are executed is actually completely impossible to determine!
This problem doesn’t arise in strict languages with a well-defined evaluation order, but in a lazy language like Haskell, we need some additional structure to ensure side-effects are (a) actually evaluated and (b) executed in the correct order. Monads happen to be an interface that elegantly provide the necessary structure to enforce that order.
Why is that? And how is that even possible? Well, the monadic interface provides a notion of data dependency in the signature for >>=, which enforces a well-defined evaluation order. Haskell’s implementation of IO is “magic”, in the sense that it’s implemented in the runtime, but the choice of the monadic interface is far from arbitrary. It seems to be a fairly good way to encode the notion of sequential actions in a pure language, and it makes it possible for Haskell to be lazy and referentially transparent without sacrificing predictable sequencing of effects.
It’s worth noting that monads are not the only way to encode side-effects in a pure way—in fact, historically, they’re not even the only way Haskell handled side-effects. Don’t be misled into thinking that monads are only for I/O (they’re not), only useful in a lazy language (they’re plenty useful to maintain purity even in a strict language), only useful in a pure language (many things are useful monads that aren’t just for enforcing purity), or that you needs monads to do I/O (you don’t). They do seem to have worked out pretty well in Haskell for those purposes, though.
† Regarding this, Simon Peyton Jones once noted that “Laziness keeps you honest” with respect to purity.
Could you just FFI into libc.so instead to do IO and skip the IO Monad thing?
Taking from https://en.wikibooks.org/wiki/Haskell/FFI#Impure_C_Functions, if you declare an FFI function as pure (so, with no reference to IO), then
GHC sees no point in calculating twice the result of a pure function
which means the the result of the function call is effectively cached. For example, a program where a foreign impure pseudo-random number generator is declared to return a CUInt
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign
import Foreign.C.Types
foreign import ccall unsafe "stdlib.h rand"
c_rand :: CUInt
main = putStrLn (show c_rand) >> putStrLn (show c_rand)
returns the same thing every call, at least on my compiler/system:
16807
16807
If we change the declaration to return a IO CUInt
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign
import Foreign.C.Types
foreign import ccall unsafe "stdlib.h rand"
c_rand :: IO CUInt
main = c_rand >>= putStrLn . show >> c_rand >>= putStrLn . show
then this results in (probably) a different number returned each call, since the compiler knows it's impure:
16807
282475249
So you're back to having to use IO for the calls to the standard libraries anyway.
Let's say using FFI we defined a function
c_write :: String -> ()
which lies about its purity, in that whenever its result is forced it prints the string. So that we don't run into the caching problems in Michal's answer, we can define these functions to take an extra () argument.
c_write :: String -> () -> ()
c_rand :: () -> CUInt
On an implementation level this will work as long as CSE is not too aggressive (which it is not in GHC because that can lead to unexpected memory leaks, it turns out). Now that we have things defined this way, there are many awkward usage questions that Alexis points out—but we can solve them using a monad:
newtype IO a = IO { runIO :: () -> a }
instance Monad IO where
return = IO . const
m >>= f = IO $ \() -> let x = runIO m () in x `seq` f x
rand :: IO CUInt
rand = IO c_rand
Basically, we just stuff all of Alexis's awkward usage questions into a monad, and as long as we use the monadic interface, everything stays predictable. In this sense IO is just a convention—because we can implement it in Haskell there is nothing fundamental about it.
That's from the operational vantage point.
On the other hand, Haskell's semantics in the report are specified using denotational semantics alone. And, in my opinion, the fact that Haskell has a precise denotational semantics is one of the most beautiful and useful qualities of the language, allowing me a precise framework to think about abstractions and thus manage complexity with precision. And while the usual abstract IO monad has no accepted denotational semantics (to the lament of some of us), it is at least conceivable that we could create a denotational model for it, thus preserving some of the benefits of Haskell's denotational model. However, the form of I/O we have just given is completely incompatible with Haskell's denotational semantics.
Simply put, there are only supposed to be two distinguishable values (modulo fatal error messages) of type (): () and ⊥. If we treat FFI as the fundamentals of I/O and use the IO monad only "as a convention", then we effectively add a jillion values to every type—to continue having a denotational semantics, every value must be adjoined with the possibility of performing I/O prior to its evaluation, and with the extra complexity this introduces, we essentially lose all our ability to consider any two distinct programs equivalent except in the most trivial cases—that is, we lose our ability to refactor.
Of course, because of unsafePerformIO this is already technically the case, and advanced Haskell programmers do need to think about the operational semantics as well. But most of the time, including when working with I/O, we can forget about all that and refactor with confidence, precisely because we have learned that when we use unsafePerformIO, we must be very careful to ensure it plays nicely, that it still affords us as much denotational reasoning as possible. If a function has unsafePerformIO, I automatically give it 5 or 10 times more attention than regular functions, because I need to understand the valid patterns of use (usually the type signature tells me everything I need to know), I need to think about caching and race conditions, I need to think about how deep I need to force its results, etc. It's awful[1]. The same care would be necessary of FFI I/O.
In conclusion: yes it's a convention, but if you don't follow it then we can't have nice things.
[1] Well actually I think it's pretty fun, but it's surely not practical to think about all those complexities all the time.
That depends on what the meaning of "is" is—or at least what the meaning of "convention" is.
If a "convention" means "the way things are usually done" or "an agreement among parties covering a particular matter" then it is easy to give a boring answer: yes, the IO monad is a convention. It is the way the designers of the language agreed to handle IO operations and the way that users of the language usually perform IO operations.
If we are allowed to choose a more interesting definition of "convention" then we can get a more interesting answer. If a "convention" is a discipline imposed on a language by its users in order to achieve a particular goal without assistance from the language itself, then the answer is no: the IO monad is the opposite of a convention. It is a discipline enforced by the language that assists its users in constructing and reasoning about programs.
The purpose of the IO type is to create a clear distinction between the types of "pure" values and the types of values which require execution by the runtime system to generate a meaningful result. The Haskell type system enforces this strict separation, preventing a user from (say) creating a value of type Int which launches the proverbial missiles. This is not a convention in the second sense: its entire goal is to move the discipline required to perform side effects in a safe and consistent way from the user and onto the language and its compiler.
Could you just FFI into libc.so instead to do IO and skip the IO Monad thing?
It is, of course, possible to do IO without an IO monad: see almost every other extant programming language.
Would it work anyway or is the outcome undeterministic because of Haskell evaluating lazy or something else, like that the GHC is pattern matching for IO Monad and then handling it in a special way or something else.
There is no such thing as a free lunch. If Haskell allowed any value to require execution involving IO then it would have to lose other things that we value. The most important of these is probably referential transparency: if myInt could sometimes be 1 and sometimes be 5 depending on external factors then we would lose most of our ability to reason about our programs in a rigorous way (known as equational reasoning).
Laziness was mentioned in other answers, but the issue with laziness would specifically be that sharing would no longer be safe. If x in let x = someExpensiveComputationOf y in x * x was not referentially transparent, GHC would not be able to share the work and would have to compute it twice.
What is the real reason?
Without the strict separation of effectful values from non-effectful values provided by IO and enforced by the compiler, Haskell would effectively cease to be Haskell. There are plenty of languages that don't enforce this discipline. It would be nice to have at least one around that does.
In the end you end you endup in a sideeffect. So why not do it the simple way?
Yes, in the end your program is represented by a value called main with an IO type. But the question isn't where you end up, it's where you start: If you start by being able to differentiate between effectful and non-effectful values in a rigorous way then you gain a lot of advantages when constructing that program.
What is the real reason - in the end you end up using a side effect, so why not do it the simple way?
...you mean like Standard ML? Well, there's a price to pay - instead of being able to write:
any :: (a -> Bool) -> [a] -> Bool
any p = or . map p
you would have to type out this:
any :: (a -> Bool) -> [a] -> Bool
any p [] = False
any p (y:ys) = y || any p ys
Could you not just FFI into libc.so instead to do your I/O, and skip the whole IO-monad thing?
Let's rephrase the question:
Could you not just do I/O like Standard ML, and skip the whole IO-monad thing?
...because that's effectively what you would be trying to do. Why "trying"?
SML is strict, and relies on sytactic ordering to specify the order of evaluation everywhere;
Haskell is non-strict, and relies on data dependencies to specify the order of evaluation for certain expressions e.g. I/O actions.
So:
Would it work anyway, or is the outcome nondeterministic because of:
(a) Haskell's lazy evaluation?
(a) - the combination of non-strict semantics and visible effects is generally useless. For an amusing exhibition of just how useless this combination can be, watch this presentation by Erik Meiyer (the slides can be found here).

Monads in Haskell and Purity

My question is whether monads in Haskell actually maintain Haskell's purity, and if so how. Frequently I have read about how side effects are impure but that side effects are needed for useful programs (e.g. I/O). In the next sentence it is stated that Haskell's solution to this is monads. Then monads are explained to some degree or another, but not really how they solve the side-effect problem.
I have seen this and this, and my interpretation of the answers is actually one that came to me in my own readings -- the "actions" of the IO monad are not the I/O themselves but objects that, when executed, perform I/O. But it occurs to me that one could make the same argument for any code or perhaps any compiled executable. Couldn't you say that a C++ program only produces side effects when the compiled code is executed? That all of C++ is inside the IO monad and so C++ is pure? I doubt this is true, but I honestly don't know in what way it is not. In fact, didn't Moggi (sp?) initially use monads to model the denotational semantics of imperative programs?
Some background: I am a fan of Haskell and functional programming and I hope to learn more about both as my studies continue. I understand the benefits of referential transparency, for example. The motivation for this question is that I am a grad student and I will be giving 2 1-hour presentations to a programming languages class, one covering Haskell in particular and the other covering functional programming in general. I suspect that the majority of the class is not familiar with functional programming, maybe having seen a bit of scheme. I hope to be able to (reasonably) clearly explain how monads solve the purity problem without going into category theory and the theoretical underpinnings of monads, which I wouldn't have time to cover and anyway I don't fully understand myself -- certainly not well enough to present.
I wonder if "purity" in this context is not really well-defined?
It's hard to argue conclusively in either direction because "pure" is not particularly well-defined. Certainly, something makes Haskell fundamentally different from other languages, and it's deeply related to managing side-effects and the IO type¹, but it's not clear exactly what that something is. Given a concrete definition to refer to we could just check if it applies, but this isn't easy: such definitions will tend to either not match everyone's expectations or be too broad to be useful.
So what makes Haskell special, then? In my view, it's the separation between evaluation and execution.
The base language—closely related to the λ-caluclus—is all about the former. You work with expressions that evaluate to other expressions, 1 + 1 to 2. No side-effects here, not because they were suppressed or removed but simply because they don't make sense in the first place. They're not part of the model² any more than, say, backtracking search is part of the model of Java (as opposed to Prolog).
If we just stuck to this base language with no added facilities for IO, I think it would be fairly uncontroversial to call it "pure". It would still be useful as, perhaps, a replacement for Mathematica. You would write your program as an expression and then get the result of evaluating the expression at the REPL. Nothing more than a fancy calculator, and nobody accuses the expression language you use in a calculator of being impure³!
But, of course, this is too limiting. We want to use our language to read files and serve web pages and draw pictures and control robots and interact with the user. So the question, then, is how to preserve everything we like about evaluating expressions while extending our language to do everything we want.
The answer we've come up with? IO. A special type of expression that our calculator-like language can evaluate which corresponds to doing some effectful actions. Crucially, evaluation still works just as before, even for things in IO. The effects get executed in the order specified by the resulting IO value, not based on how it was evaluated. IO is what we use to introduce and manage effects into our otherwise-pure expression language.
I think that's enough to make describing Haskell as "pure" meaningful.
footnotes
¹ Note how I said IO and not monads in general: the concept of a monad is immensely useful for dozens of things unrelated to input and output, and the IO types has to be more than just a monad to be useful. I feel the two are linked too closely in common discourse.
² This is why unsafePerformIO is so, well, unsafe: it breaks the core abstraction of the language. This is the same as, say, putzing with specific registers in C: it can both cause weird behavior and stop your code from being portable because it goes below C's level of abstraction.
³ Well, mostly, as long as we ignore things like generating random numbers.
A function with type, for example, a -> IO b always returns an identical IO action when given the same input; it is pure in that it cannot possibly inspect the environment, and obeys all the usual rules for pure functions. This means that, among other things, the compiler can apply all of its usual optimization rules to functions with an IO in their type, because it knows they are still pure functions.
Now, the IO action returned may, when run, look at the environment, read files, modify global state, whatever, all bets are off once you run an action. But you don't necessarily have to run an action; you can put five of them into a list and then run them in reverse of the order in which you created them, or never run some of them at all, if you want; you couldn't do this if IO actions implicitly ran themselves when you created them.
Consider this silly program:
main :: IO ()
main = do
inputs <- take 5 . lines <$> getContents
let [line1,line2,line3,line4,line5] = map print inputs
line3
line1
line2
line5
If you run this, and then enter 5 lines, you will see them printed back to you but in a different order, and with one omitted, even though our haskell program runs map print over them in the order they were received. You couldn't do this with C's printf, because it immediately performs its IO when called; haskell's version just returns an IO action, which you can still manipulate as a first-class value and do whatever you want with.
I see two main differences here:
1) In haskell, you can do things that are not in the IO monad. Why is this good? Because if you have a function definitelyDoesntLaunchNukes :: Int -> IO Int you don't know that the resulting IO action doesn't launch nukes, it might for all you know. cantLaunchNukes :: Int -> Int will definitely not launch any nukes (barring any ugly hacks that you should avoid in nearly all circumstances).
2) In haskell, it's not just a cute analogy: IO actions are first class values. You can put them in lists, and leave them there for as long as you want, they won't do anything unless they somehow become part of the main action. The closest that C has to that are function pointers, which are quite a bit more cumbersome to use. In C++ (and most modern imperative languages really) you have closures which technically could be used for this purpose, but rarely are - mainly because Haskell is pure and they aren't.
Why does that distinction matter here? Well, where are you going to get your other IO actions/closures from? Probably, functions/methods of some description. Which, in an impure language, can themselves have side effects, rendering the attempt of isolating them in these languages pointless.
fiction-mode: Active
It was quite a challenge, and I think a wormhole could be forming in the neighbour's backyard, but I managed to grab part of a Haskell I/O implementation from an alternate reality:
class Kleisli k where
infixr 1 >=>
simple :: (a -> b) -> (a -> k b)
(>=>) :: (a -> k b) -> (b -> k c) -> a -> k c
instance Kleisli IO where
simple = primSimpleIO
(>=>) = primPipeIO
primitive primSimpleIO :: (a -> b) -> (a -> IO b)
primitive primPipeIO :: (a -> IO b) -> (b -> IO c) -> a -> IO c
Back in our slightly-mutilated reality (sorry!), I have used this other form of Haskell I/O to define our form of Haskell I/O:
instance Monad IO where
return x = simple (const x) ()
m >>= k = (const m >=> k) ()
and it works!
fiction-mode: Offline
My question is whether monads in Haskell actually maintain Haskell's purity, and if so how.
The monadic interface, by itself, doesn't maintain restrain the effects - it is only an interface, albeit a jolly-versatile one. As my little work of fiction shows, there are other possible interfaces for the job - it's just a matter of how convenient they are to use in practice.
For an implementation of Haskell I/O, what keeps the effects under control is that all the pertinent entities, be they:
IO, simple, (>=>) etc
or:
IO, return, (>>=) etc
are abstract - how the implementation defines those is kept private.
Otherwise, you would be able to devise "novelties" like this:
what_the_heck =
do spare_world <- getWorld -- how easy was that?
launchMissiles -- let's mess everything up,
putWorld spare_world -- and bring it all back :-D
what_the_heck -- that was fun; let's do it again!
(Aren't you glad our reality isn't quite so pliable? ;-)
This observation extends to types like ST (encapsulated state) and STM (concurrency) and their stewards (runST, atomically etc). For types like lists, Maybe and Either, their orthodox definitions in Haskell means no visible effects.
So when you see an interface - monadic, applicative, etc - for certain abstract types, any effects (if they exist) are contained by keeping its implementation private; safe from being used in aberrant ways.

One more time...can I have an example of state monad that does what I want?

I'm trying to understand the actual need for the reader and/or state monads. All the examples I've seen (including many on stackoverflow as I've hunted for suitable examples I can use and in various books and blog articles) are of the form (pseudo code)
f = do
foo <- ask
do something with foo
g = do
foo <- ask
do something else using foo
h = runReader
(
f
g
)
In other words, call two functions and (presumably) maintain some state from one call to the next. However, I don't find this example particularly convincing as (I think) I could just have f return some state and then pass that state on to g.
I'd love to see an example, using a single integer (say) as the state to be preserved where, rather than two sequential calls to f and then g from a central place, rather there's a call to f which then internally calls g and then have changed state (if state monad) available back in the main routine.
Most (well actually all) the examples I have seen spend a tremendous amount of time focusing on the definition of the monad and then show how to set up a single function call. To me, it would be the ability to do nested calls and have the state carried along for the ride to demonstrate why it's useful.
Here's a non-trivial example of a stateful subroutine calling another stateful subroutine.
import Control.Monad.Trans.State
f :: State Int ()
f = do
r <- g
modify (+ r)
g :: State Int Int
g = do
modify (+ 1)
get
main = print (execState f 4)
In this example, the initial state begins at 4 and the stateful computation begins at f. f internally calls g, which increments the state to 5 and then returns the current state (still 5). This restores control to f, which binds the value 5 to r and then increments the current state by r, giving a final state of 10:
>>> main
10
Almost everything you can do with monads you can do without them. (Well, some are special like ST, STM, IO, etc., but that's a different story.) But:
they allow you to encapsulate many common patterns, like in this case stateful computations, and hide details or boiler-plate code that would be otherwise needed; and
there are plethora of functions that work on any (or many) monads, which you can just specialize for a particular monad you're using.
To give an example: Often one needs to have some kind of a generator that supplies unique names, like when generating code etc. This can be easily accomplished using the state monad: Each time newName is called, it outputs a new name and increments the internal state:
import Control.Monad.State
import Data.Tree
import qualified Data.Traversable as T
type NameGen = State Int
newName :: NameGen String
newName = state $ \i -> ("x" ++ show i, i + 1)
Now let's say we have a tree that has some missing values. We'd like to supply them with such generated names. Fortunately, there is a generic function mapM that allows to traverse any traversable structure with any monad (without the monad abstraction, we wouldn't have this function). Now fixing the tree is easy. For each value we check if it's filled (then we just use return to lift it into the monad), and if not, supply a new name:
fillTree :: Tree (Maybe String) -> NameGen (Tree String)
fillTree = T.mapM (maybe newName return)
Just imagine implementing this function without monads, with explicit state - going manually through the tree and carrying the state around. The original idea would be completely lost in boilerplate code. Moreover, the function would be very specific to Tree and NameGen.
But with monads, we can go even further. We could parametrize the name generator and construct even more generic function:
fillTreeM :: (Monad m) => m String -> Tree (Maybe String) -> m (Tree String)
fillTreeM gen = T.mapM (maybe gen return)
Note the first parameter m String. It's not a constant String value, it's a recipe for generating a new String within m, whenever it's needed.
Then the original one can be rewritten just as
fillTree' :: Tree (Maybe String) -> NameGen (Tree String)
fillTree' = fillTreeM newName
But now we can use the same function for many very different purposes. For example, use the Rand monad and supply randomly generated names.
Or, at some point we might consider a tree without filled out nodes invalid. Then we just say that wherever we're asked for a new name, we instead abort the whole computation. This can be implemented just as
checkTree :: Tree (Maybe String) -> Maybe (Tree String)
checkTree = fillTreeM Nothing
where Nothing here is of type Maybe String, which, instead of trying to generate a new name, aborts the computation within the Maybe monad.
This level of abstraction would be hardly possible without having the concept of monads.
I'm trying to understand the actual need for the reader and/or state monads.
There are many ways to understand monads in general, and these monads in particular. In this answer, I focus on one understanding of these monads which I believe might be helpful for the OP.
The reader and state monads provide library support for very simple usage patterns:
The reader monad provides support for passing arguments to functions.
The state monad provides support for getting results out of functions and passing them to other functions.
As the OP correctly figured out, there is no great need for library support for these things, which are already very easy in Haskell. So many Haskell programs could use a reader or state monad, but there's no point in doing so, so they don't.
So why would anyone ever use a reader or state monad? I know three important reasons:
Realistic programs contain many functions that call each other and pass information back and forth. Sometimes, many functions take arguments that are just passed on to other functions. The reader monad is a library for this "accept arguments and pass them on" pattern. The state monad is a library for the similar "accept arguments, pass them on, and pass the results back as my result" pattern.
In this situation, a benefit of using the reader or state monad is that the arguments get passed on automatically, and we can focus on the more interesting jobs of these functions. A cost is that we have to use monadic style (do notation etc.).
Realistic programs can use multiple monads at once. They need arguments that are passed on, arguments that are returned, error handling, nondeterminism, ...
In this situation, a benefit of using the reader or state monad transformer is that we can package all of these monads into a single monad transformer stack. We still need monadic style, but now we pay the cost once (use do notation everywhere) and get the benefit often (multiple monads in the transformer stack).
Some library functions work for arbitrary monads. For example, the sequence :: [m a] -> m [a] takes a list of monadic actions, runs all of them in sequence, and returns the collected result.
A benefit of using the reader or state (or whatever) monad is that we can use these very generic library functions that work for any monad.
Note that points 1 and 2 only show up in realistic, somewhat large programs. So it is hard to give a small example for this benefit of using monads. Point 3 shows up in small library functions, but is harder to understand, because these library functions are often very abstract.

In what sense is the IO Monad pure?

I've had the IO monad described to me as a State monad where the state is "the real world". The proponents of this approach to IO argue that this makes IO operations pure, as in referentially transparent. Why is that? From my perspective it appears that code inside the IO monad have plenty of observable side effects. Also, isn't it possible to describe pretty much any non-pure function like a function of the real world? For example, can't we think of, say, C's malloc as being a function that takes a RealWorld and an Int and returns a pointer and a RealWorld, only just like in the IO monad the RealWorld is implicit?
Note: I know what a monad is and how it's used. Please don't respond with a link to a random monad tutorial unless it specifically adresses my question.
I think the best explanation I've heard was actually fairly recently on SO. IO Foo is a recipe for creating a Foo. Another common, more literal, way of saying this is that it is a "program that produces a Foo". It can be executed (many times) to create a Foo or die trying. The execution of the recipe/program is what we ultimately want (otherwise, why write one?), but the thing that is represented by an IO action in our code is the recipe itself.
That recipe is a pure value, in the same exact sense that a String is a pure value. Recipes can be combined and manipulated in interesting, sometimes astonishing, ways, but the many ways these recipes can be combined (except for the blatantly non-pure unsafePerformIO, unsafeCoerce, etc.) are all completely referentially transparent, deterministic, and all that nice stuff. The resulting recipe depends in absolutely no way whatsoever on the state of anything other than the recipes that it was built up from.
Also, isn't it possible to describe pretty much any non-pure function like a function of the real world? For example, can't we think of, say, C's malloc as being a function that takes a RealWorld and an Int and returns a pointer and a RealWorld, only just like in the IO monad the RealWorld is implicit?
For sure ...
The whole idea of functional programming is to describe programs as a combination of small, independent calculations building up bigger computations.
Having these independent calculations, you'll have lots of benefits, reaching from concise programs to efficient and efficiently parallelizable codes, laziness up to the the rigorous guarantee that control flows as intended - with no chance of interference or corruption of arbitrary data.
Now - in some cases (like IO), we need impure code. Calculations involving such operations cannot be independent - they could mutate arbitrary data of another computation.
The point is - Haskell is always pure, IO doesn't change this.
So, our impure, non-independent codes have to get a common dependency - we have to pass a RealWorld. So whatever stateful computation we want to run, we have to pass this RealWorld thing to apply our changes to - and whatever other stateful computation wants to see or make changes has to know the RealWorld too.
Whether this is done explicitly or implicitly through the IO monad is irrelevant. You build up a Haskell program as a giant computation that transforms data, and one part of this data is the RealWorld.
Once the initial main :: IO () gets called when your program is run with the current real world as a parameter, this real world gets carried through all impure calculations involved, just as data would in a State. That's what monadic >>= (bind) takes care of.
And where the RealWorld doesn't get (as in pure computations or without any >>=-ing to main), there is no chance of doing anything with it. And where it does get, that happened by purely functional passing of an (implicit) parameter. That's why
let foo = putStrLn "AAARGH" in 42
does absolutely nothing - and why the IO monad - like anything else - is pure. What happens inside this code can of course be impure, but it's all caught inside, with no chance of interfering with non-connected computations.
Suppose we have something like:
animatePowBoomWhenHearNoiseInMicrophone :: TimeDiff -> Sample -> IO ()
animatePowBoomWhenHearNoiseInMicrophone
levelWeightedAverageHalfLife levelThreshord = ...
programA :: IO ()
programA = animatePowBoomWhenHearNoiseInMicrophone 3 10000
programB :: IO ()
programB = animatePowBoomWhenHearNoiseInMicrophone 3 10000
Here's a point of view:
animatePowBoomWhenHearNoiseInMicrophone is a pure function in the sense that its results for same input, programA and programB, are exactly the same. You can do main = programA or main = programB and it would be exactly the same.
animatePowBoomWhenHearNoiseInMicrophone is a function receiving two arguments and resulting in a description of a program. The Haskell runtime can execute this description if you set main to it or otherwise include it in main via binding.
What is IO? IO is a DSL for describing imperative programs, encoded in "pure-haskell" data structures and functions.
"complete-haskell" aka GHC is an implementation of both "pure-haskell", and an imperative implementation of an IO decoder/executer.
It quite simply comes down to extensional equality:
If you were to call getLine twice, then both calls would return an IO String which would look exactly the same on the outside each time. If you were to write a function to take 2 IO Strings and return a Bool to signal a detected difference between them both, it would not be possible to detect any difference from any observable properties. It could not ask any other function whether they are equal and any attempt at using >>= must also return something in IO which all are equall externally.
I'll let Martin Odersky answer this
The IO monad does not make a function pure. It just makes it obvious
that it's impure.
Sounds clear enough.
Even though its title is a bit weird (in that it doesn't precisely match the content) the following haskell-cafe thread contains a nice discussion about different IO models for Haskell.
http://www.mail-archive.com/haskell-cafe#haskell.org/msg79613.html
Well, this is what we have been taught at college -
Function is referentially transparent when it always returns the same value for specified input (or the same expression always evaluates to same value in the same context). Therefore, for example getChar would not be referentially transparent if it had type signature just () -> Char or Char, because you can get different results if you call this function multiple times with the same argument.
But, if you introduce IO monad, then getChar can have type IO Char and this type has only one single value - IO Char. So getChar allways reutrns the same value, no matter on which key user really pressed.
But you are still able to "get" the underlying value from this IO Char thing. Well, not really get, but pass to another function using bind operator (>>=), so you can work with the Char that user entered further in your program.
Philip Wadler writes:
In an impure language, an operation like tick would be represented by a function of type () -> (). The spurious argument () is required to delay the effect until the function is applied, and since the output type is () one may guess that the function's purpose lies in a side effect. In contrast, here tick has type M (): no spurious argument is needed, and the appearance of M explicitly indicates what sort of effect may occur.
I fail to understand how M () makes the empty argument list () less spurious but Wadler is pretty clear that monads just indicate a kind of side-effect, they do not eliminate it.
In what sense is the monadic IO type pure?
In the sense that values of the IO type are portions of Standard ML abstract imperative code which ideally can only be processed by the RTS of a Haskell implementation - in How to Declare an Imperative, Philip Wadler provides a hint as to how this is possible:
(* page 26 *)
type 'a io = unit -> 'a
infix >>=
val >>= : 'a io * ('a -> 'b io) -> 'b io
fun m >>= k = fn () => let
val x = m ()
val y = k x ()
in
y
end
val return : 'a -> 'a io
fun return x = fn () => x
(* page 27 *)
val execute : unit io -> unit
fun execute m = m ()
However, not everyone finds this situation acceptable:
[...] a state-less model of computation on top of a machinery whose most
eminent characteristic is state [means] the gap between model and machinery is wide, and therefore costly to bridge. [...]
This has in due time also been recognized by the protagonists of functional
languages. They have introduced state (and variables) in various tricky ways.
The purely functional character has thereby been compromised and sacrificed. [...]
Niklaus Wirth.
...anyone for Miranda(R)?
I've had the IO monad described to me as a State monad where the state is "the real world".
That would be the classic pass-the-planet model of I/O, which Clean uses directly:
import StdFile
import StdMisc
import StdString
Start :: *World -> *World
Start w = putString "Hello, world!\n" w
putString :: String *World -> *World
putString str world
# (out, world1) = stdio world
# out1 = fwrites str out
# (_, world2) = fclose out1 world1
= world2
putChar :: Char *World -> *World
putChar c w = putString {c} w
The proponents of this approach to I/O argue that this makes I/O operations pure, as in referentially transparent. Why is that?
Because it's usually correct.
From the standard Haskell 2010 library module Data.List:
mapAccumL _ s [] = (s, [])
mapAccumL f s (x:xs) = (s'',y:ys)
where (s', y ) = f s x
(s'',ys) = mapAccumL f s' xs
If this idiom is so common that it has specific definitions to support it, then its use as a model of I/O (with a suitable state-type) is really no great surprise - from pages 14-15 of State in Haskell by John Launchbury and Simon Peyton Jones:
How, then, are I/O operations executed at all? The meaning of the whole program
is given by the value of the top-level identifier mainIO:
mainIO :: IO ()
mainIO is an I/O state transformer, which is applied to the external world state by
the operating system. Semantically speaking, it returns a new world state, and the
changes embodied therein are applied to the real world.
(...back when main was called mainIO.)
The most recent Clean Language Report (I/O on the Unique World on page 24 of 148) goes into more detail:
The world which is given to the initial expression is an abstract data structure, an abstract world of type *World which
models the concrete physical world as seen from the program. The abstract world can in principle contain
anything what a functional program needs to interact during execution with the concrete world. The world can be seen as a
state and modifications of the world can be realized via state transition functions defined on the world or a part of the world. By
requiring that these state transition functions work on a unique world the modifications of the abstract world can directly be
realized in the real physical world, without loss of efficiency and without losing referential transparency.
In terms of semantics, the crucial point is this: for an I/O-centric program's changes to take effect, that program must return the final world-state value.
Now consider this small Clean program:
Start :: *World -> *World
Start w = loopX w
loopX :: *World -> *World
loopX w
# w1 = putChar 'x' w
= loopX w1
Obviously the final World value is never returned so 'x' should not be seen at all...
Also, isn't it possible to describe pretty much any non-pure function like a function of the real world?
Yes; that's more-or-less how the FFI works in Haskell 2010.
From my perspective it appears that code inside the monadic IO type have plenty of observable side effects.
If you're using GHC, it isn't an appearance - from
A History of Haskell (page 26 of 55) by Paul Hudak, John Hughes, Simon Peyton Jones, and Philip Wadler:
Of course, GHC does not actually pass the world around; instead, it passes a dummy “token,” to ensure proper sequencing of actions in the presence of lazy evaluation, and performs input and output as actual side effects!
But that's merely an implementation detail:
An IO computation is a function that (logically) takes the state of the world, and returns a modified world as well as the return value.
Logic doesn't apply to the real world.
Marvin Lee Minsky.

Can Haskell's monads be thought of as using and returning a hidden state parameter?

I don't understand the exact algebra and theory behind Haskell's monads. However, when I think about functional programming in general I get the impression that state would be modelled by taking an initial state and generating a copy of it to represent the next state. This is like when one list is appended to another; neither list gets modified, but a third list is created and returned.
Is it therefore valid to think of monadic operations as implicitly taking an initial state object as a parameter and implicitly returning a final state object? These state objects would be hidden so that the programmer doesn't have to worry about them and to control how they gets accessed. So, the programmer would not try to copy the object representing the IO stream as it was ten minutes ago.
In other words, if we have this code:
main = do
putStrLn "Enter your name:"
name <- getLine
putStrLn ( "Hello " ++ name )
...is it OK to think of the IO monad and the "do" syntax as representing this style of code?
putStrLn :: IOState -> String -> IOState
getLine :: IOState -> (IOState, String)
main :: IOState -> IOState
-- main returns an IOState we can call "state3"
main state0 = putStrLn state2 ("Hello " ++ name)
where (state2, name) = getLine state1
state1 = putStrLn state0 "Enter your name:"
No, that's not what monads in general do. However, your analogy is in fact exactly correct with regards to the data type State s a, which happens to be a monad. State is defined like this:
newtype State s a = State { runState :: s -> (a, s) }
...where the type variable s is the state value and a is the "regular" value that you use. So a value in "the State monad" is just a function from an initial state, to a return value and final state. The monadic style, as applied to State, does nothing more than automatically thread a state value through a sequence of functions.
The ST monad is superficially similar, but uses magic to allow computations with real side-effects, but only such that the side effects can't be observed from outside particular uses of ST.
The IO monad is essentially an ST monad set to "more magic", with side effects that touch the outside world and only a single point where IO computations are run, namely the entry point for the entire program. Still, on some conceptual level, you can still think of it as threading a "state" value through functions the way regular State does.
However, other monads don't necessarily have anything whatsoever to do with threading state, or sequencing functions, or whatnot. The operations needed for something to be a monad are incredibly general and abstract. For instance, using Maybe or Either as monads lets you use functions that might return errors, with the monadic style handling escaping from the computation when an error occurs the same way that State threads a state value. Using lists as a monad gives you nondeterminism, letting you simultaneously apply functions to multiple inputs and see all possible outputs, with the monadic style automatically applying the function to each argument and collecting all the outputs.
Is it therefore valid to think of monadic operations as implicitly taking an initial state object as a parameter and implicitly returning a final state object?
This seems to be a common sticking point for learning monads, i.e., trying to figure out how a single magical monad primordial soup is simultaneously useful for representing stateful computations, computations that can fail, nondeterministic computations, exceptions, continuations, sequencing side effects, and so on.
Threading state through a sequence of stateful computations is one single example of an operation that satisfies the monad laws.
You're correct in observing that the State and IO monads are close cousins, but your analogy will fall apart if you try inserting, say, the list monad.
Not monads in general, but for the IO monad, yes — in fact, the type IO a is often defined as the function type RealWorld -> (RealWorld, a). So in this view, the desugared type of putStrLn is String -> RealWorld -> (RealWorld, ()) and getChar is RealWorld -> (RealWorld, Char) — and we only partially apply it, with the monadic bind taking care of fully evaluating it and passing the RealWorld around. (GHC's ST library actually includes a very real RealWorld type, though it's described as "deeply magical" and not for actual use.)
There are many other monads that don't have this property, though. There's no RealWorld being passed around with, for example, the monads [1,2,3,4] or Just "Hello".
Absolutely not. This is not what monads are in general. You can use monads to implicitly pass around data, but that is just one application. If you use this model of monads then you are going to miss out on a lot of the really cool things monads can do.
Instead, think about monads as being pieces of data that represent a computation. For example, there is a sense in which implicitly passing around data isn't pure because pure languages insist that you be explicit about all of your arguments and return types. So if you want to pass around data implicitly you can do this: define a new data type that is a representation of doing something impure, and then write a piece of code to work with that.
An extreme example (just a theoretical example, you're unlikely to want to do this) would be this: C allows impure computations, so you could define a type that represents a piece of C code. You can then write an interpreter that takes one of these C structures and interprets it. All monads are like this, though usually much simpler than a C interpreter. But this view is more powerful because it also explains the monads that aren't about passing around hidden state.
You should probably also try to resist the temptation to see IO as passing around a hidden world state. The internal implementation of IO has nothing to do with how you should think about IO. The IO monad is about building representations of the I/O you want to perform. You return one of these representations to the Haskell run-time system and it's up to that system to implement your representation however it wants.
Any time you want to do something, and you can't see how to directly implement it in a pure way, but you can see how to build a pure data structure to describe want you want, you may have an application for a monad, especially if your structure is naturally a tree of some sort.
I prefer to think about monads as objects that represents delayed actions (runXXX, main) with result which can be combined according to that result.
return "somthing"
actionA >>= \x -> makeActionB x
And those action not necessary to be state-full. I.e. you can think about monad of function construction like this:
instance Monad ((->) a) where
m >>= fm = \x -> fm (m x) x
return = const
sqr = \x -> x*x
cube = \x -> x*x*x
weird = do
a <- sqr
b <- cube
return (a+b)

Resources