Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[Haskell-cafe] Functions with side-effects?

6 views
Skip to first unread message

Daniel Carrera

unread,
Dec 21, 2005, 6:44:12 AM12/21/05
to Haskell-Cafe Cafe
Hi all,

I'm a Haskell newbie and I don't really understand how Haskell deals
with functions that really must have side-effects. Like a rand()
function or getLine().

I know this has something to do with monads, but I don't really
understand monads yet. Is there someone who might explain this in newbie
terms? I don't need to understand the whole thing, I don't need a rand()
function right this minute. I just want to understand how Haskell
separates purely functional code from non-functional code (I understand
that a rand() function is inevitably not functional code, right?)

Cheers,
Daniel.
--
/\/`) http://oooauthors.org
/\/_/ http://opendocumentfellowship.org
/\/_/
\/_/ I am not over-weight, I am under-tall.
/
_______________________________________________
Haskell-Cafe mailing list
Haskel...@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Creighton Hogg

unread,
Dec 21, 2005, 7:15:42 AM12/21/05
to Daniel Carrera, Haskell-Cafe Cafe
On Wed, 21 Dec 2005, Daniel Carrera wrote:

> Hi all,
>
> I'm a Haskell newbie and I don't really understand how Haskell deals
> with functions that really must have side-effects. Like a rand()
> function or getLine().
>
> I know this has something to do with monads, but I don't really
> understand monads yet. Is there someone who might explain this in newbie
> terms? I don't need to understand the whole thing, I don't need a rand()
> function right this minute. I just want to understand how Haskell
> separates purely functional code from non-functional code (I understand
> that a rand() function is inevitably not functional code, right?)

Well, it's *all* functional fundamentally but when people
say "non-functional" in relation to Haskell code what they
mean is in a monad. Monads, I believe, can be just thought
of as containers for state.
So like I said in an e-mail on the tutorial thread the only
distinction you need to make is between "=" which is truly a
statement of definition and not subject to change, and "<-"
which says that the left hand side is the result of the
action of the right hand side. In terms of types, "<-"
strips away the monadic type.

Note for example that if you want your main program to take
in a string and then print it to the screen, it is *NOT*
main = putStrLn . getLine (which was I first thought)
or
main = putStrLn (getLine) (which you might guess from the
type sig)
but rather

main = do
x <- getLine
putStrLn x

Because you want putStrLn to act on the string that
*resulted* from getLine, and not on getLine itself.

I'd check out this link from HaWiki on monads,
http://haskell.org/hawiki/MonadsAsContainers

Cale Gibbard

unread,
Dec 21, 2005, 7:50:22 AM12/21/05
to Daniel Carrera, Haskell-Cafe Cafe
On 21/12/05, Daniel Carrera <daniel....@zmsl.com> wrote:
> Hi all,
>
> I'm a Haskell newbie and I don't really understand how Haskell deals
> with functions that really must have side-effects. Like a rand()
> function or getLine().
>
> I know this has something to do with monads, but I don't really
> understand monads yet. Is there someone who might explain this in newbie
> terms? I don't need to understand the whole thing, I don't need a rand()
> function right this minute. I just want to understand how Haskell
> separates purely functional code from non-functional code (I understand
> that a rand() function is inevitably not functional code, right?)
>
> Cheers,
> Daniel.

Haskell separates pure functions from computations where side effects
must be considered by encoding those side effects as values of a
particular type. Specifically, a value of type (IO a) is an action,
which if executed would produce a value of type 'a'.

Some examples:
getLine :: IO String
putStrLn :: String -> IO () -- note that the result value is an empty tuple
.
randomRIO :: (Random a) => (a,a) -> IO a

Ordinary Haskell evaluation doesn't cause this execution to occur. A
value of type (IO a) is almost completely inert. In fact, the only IO
action which can really be said to run in a compiled Haskell program
is main.

Now, so far, all this is great, but without a way to chain actions
together end-to-end, we can't do a whole lot. So Haskell provides us
with a few primitives for composing and chaining together IO actions.
A simple one of these is:
(>>) :: IO a -> IO b -> IO b
where if x and y are IO actions, then (x >> y) is the action that
performs x, dropping the result, then performs y and returns its
result.
Great, we can now write programs which do multiple things:
main = putStrLn "Hello" >> putStrLn "World"
will print "Hello" and "World" on separate lines.

However, we don't yet have a way to chain actions in which we are
allowed to use the result of the first in order to affect what the
second action will do. This is accomplished by the following
operation, called 'bind':
(>>=) :: IO a -> (a -> IO b) -> IO b
Now, if x is an IO action with a result of type 'a', and f is a
function from values of type 'a' to actions with a result of type 'b',
then x >>= f is the action that performs x, and captures its result,
passing it to f, which then computes a second action to be performed.
That action is then carried out, and its result is the result of the
overall computation.

That's a mouthful, but once you see it in use, perhaps the idea will
become clearer:
main = putStrLn "Hello, what is your name?"
>> getLine
>>= \name -> putStrLn ("Hello, " ++ name ++ "!")

This is most of what we need. In fact, this bind function is really
successful, and we can define (>>) in terms of it:
x >> y = x >>= const y

In practice, it turns out to also be quite important to turn a value
into an IO action which does nothing, and simply returns that value.
This is quite handy near the end of a chain of actions, where we want
to decide what to return ourselves, rather than leaving it up to the
last action in the chain. So there's one more primitive,
return :: a -> IO a
which does just that.

Note that there is no function:
unsafe :: IO a -> a
as this would defeat the referential transparency of Haskell --
applying 'unsafe' to the same IO action might return different things
every time, and Haskell functions aren't allowed to behave that way.

Now, I haven't really told you anything about monads in general yet.
Most monads are actually rather unlike IO, but they do share the
similar concepts of bind and return. For monads in general, see my
tutorial MonadsAsContainers on the wiki :)

Hope this helps
- Cale

Cale Gibbard

unread,
Dec 21, 2005, 7:54:08 AM12/21/05
to Daniel Carrera, Haskell-Cafe Cafe

Oh, just sent that, but there was one more point I wanted to make. You
might see do-notation all over the place in real Haskell programs. In
do-notation, our example program might look like:

main = do putStrLn "Hello, what is your name?"
name <- getLine


putStrLn ("Hello, " ++ name ++ "!")

This is in fact entirely equivalent to the above form, and is
translated into it by the Haskell compiler. So whenever you see a do
block, you can just imagine a chain of applications of (>>) and (>>=),
and some lambdas when appropriate to capture the results of actions.

Daniel Carrera

unread,
Dec 21, 2005, 8:36:11 AM12/21/05
to Haskell-Cafe Cafe
Thanks for all the help. I think things are much clearer now. And this bit:

> main = do putStrLn "Hello, what is your name?"
> name <- getLine
> putStrLn ("Hello, " ++ name ++ "!")

Looks quite straight forward.

I just wrote my very first IO program with Haskell:
--//--


main = do
x <- getLine

putStrLn( show(length x) )
--//--

That's not so scary. The next thing I need to figure out is how to act
on the input data itself (not just its length). For example, if I wanted
a program to output the nth element of the fibonacci sequence:
--//--
$ ./fib 12
144
--//--

My first inclination would be to write it as:
--//--


main = do
x <- getLine

putStrLn( show(fib x) )
--//--

Of course that won't work because x is an IO String and 'fib' wants an
Int. To it looks like I need to do a conversion "IO a -> b" but from
what Cale said, that's not possible because it would defeat the
referential transparency of Haskell.

Hmm... there must be a way to solve this problem though...

In any event, thanks for the help. I'm learning :)

Cheers,
Daniel.
--
/\/`) http://oooauthors.org
/\/_/ http://opendocumentfellowship.org
/\/_/
\/_/ I am not over-weight, I am under-tall.
/

Cale Gibbard

unread,
Dec 21, 2005, 8:49:43 AM12/21/05
to Daniel Carrera, Haskell-Cafe Cafe
On 21/12/05, Daniel Carrera <daniel....@zmsl.com> wrote:

Actually, you're closer than you think here. x is actually of type String!
The problem is just that fib wants a number, so you need to read the string
:


main = do x <- getLine

putStrLn (show (fib (read x)))

There's a handy name for the composite (putStrLn . show) called


'print', so you can write:

main = do x <- getLine

print (fib (read x)))

Cheers!
- Cale

Creighton Hogg

unread,
Dec 21, 2005, 8:50:25 AM12/21/05
to Daniel Carrera, Haskell-Cafe Cafe
On Wed, 21 Dec 2005, Daniel Carrera wrote:

x is a String, getLine has type IO String. That's what I
was getting at in one of my last e-mails.
So you just need something that can read in a string and
convert it to an int.
Something like
let y = (read x)
putStrLn $ show $ fib y
should work, yes?

David Roundy

unread,
Dec 21, 2005, 10:03:41 AM12/21/05
to haskel...@haskell.org
On Wed, Dec 21, 2005 at 11:43:42AM +0000, Daniel Carrera wrote:
> Hi all,
>
> I'm a Haskell newbie and I don't really understand how Haskell deals
> with functions that really must have side-effects. Like a rand()
> function or getLine().
>
> I know this has something to do with monads, but I don't really
> understand monads yet. Is there someone who might explain this in newbie
> terms? I don't need to understand the whole thing, I don't need a rand()
> function right this minute. I just want to understand how Haskell
> separates purely functional code from non-functional code (I understand
> that a rand() function is inevitably not functional code, right?)

What everyone said about monads is great, but I'd just like to add that for
rand() you don't strictly need a monad. With a bit of care, you could
write rand() to be of type

rand :: Int -> [Int]

where it accepts a seed value, and returns an infinite list of pseudorandom
numbers. In one sense we haven't gone non-monadic here, since there is
actually an instance of monad for [a], but you needn't actually write
monadic code.

The catch with this rand is that when you go to actually use the output,
you need to thread this list of random numbers through your code, and need
to be sure never to use the same random number twice. Which is why you'd
really prefer to use a monad that could force you to obey the constraints
of only using each random number once.

For some uses, though, the above rand will be great, as long as you're
using it for list manipulations, such as xoring a list of numbers with
random numbers as a primitive form of encryption (or a strong form of
encryption, if you use a strong random number generator).
--
David Roundy
http://www.darcs.net

Wolfgang Jeltsch

unread,
Dec 21, 2005, 10:21:12 AM12/21/05
to haskel...@haskell.org
Am Mittwoch, 21. Dezember 2005 13:15 schrieb Creighton Hogg:
> [...]

> Monads, I believe, can be just thought of as containers for state.

I would say that you are talking especially about the I/O monad here. A monad
as such is a rather general concept like a group is in algebra.

The important point of the integration of imperative programming in Haskell is
not that it's done using monads. The point is that you have a specific type
(IO) whose values are descriptions of I/O actions, and some primitive
operations on IO values. The IO type together with two of these primitive
operations forms a monad but this is secondary in my opinion.

> [...]

Best wishes,
Wolfgang

Daniel Carrera

unread,
Dec 21, 2005, 10:25:39 AM12/21/05
to Haskell-Cafe Cafe
Creighton Hogg wrote:
> x is a String, getLine has type IO String. That's what I
> was getting at in one of my last e-mails.

Hmm... let's see if I understand:

* getLine() has type IO String.
* The <- will "convert" an IO String to a plain String
* So if I do x <- getLine() then x has the type String.

So, the <- effectively ammounts to an IO a -> a conversion.

In another email John Hughes said that one could think of "IO a" as a
set of instructins for obtaining a. I guess that means that IO is a sort
of imperative layer that helps the purely functional code interact with
the outside world.

So I can have an IO bit (e.g. a do-block) that calls functions (which
are purely functional code) but I can't have a function that executes
any IO.

For example, it is not possible to write a function "my_read_file" that
could work like this:

my_data = my_read_file("my_file.txt")

Correct? Otherwise this would be a function that is not referentially
transparent.

Cheers,
Daniel.
--
/\/`) http://oooauthors.org
/\/_/ http://opendocumentfellowship.org
/\/_/
\/_/ I am not over-weight, I am under-tall.
/

David Barton

unread,
Dec 21, 2005, 10:29:12 AM12/21/05
to haskel...@haskell.org
Wolfgang Jeltsch writes:
----- Original Message -----

> Am Mittwoch, 21. Dezember 2005 13:15 schrieb Creighton Hogg:
>> [...]
>
>> Monads, I believe, can be just thought of as containers for state.
>
> I would say that you are talking especially about the I/O monad here. A
> monad
> as such is a rather general concept like a group is in algebra.

While this is correct, I'm afraid that for most of us it is a flavorless
answer. I wish I had the mathematical mind that made the word "group" in
this context instantly intuitively recognizable, but I don't.

I think Phil Wadler said it best when he said that a monad is a
*computation*. If a function is a mapping between input and output values,
a computation is both an invocation of a function and the provision of
values --- which can include state, ordering, and many other things. Of
course, I'm a Phil Wadler fan anyway.

> The important point of the integration of imperative programming in
> Haskell is
> not that it's done using monads. The point is that you have a specific
> type
> (IO) whose values are descriptions of I/O actions, and some primitive
> operations on IO values. The IO type together with two of these primitive
> operations forms a monad but this is secondary in my opinion.

Yes and no. It is important for me, at least, to continue to grasp that IO
is just not a functional thing --- it is not captured intuitively in a
function. Rather, it is a computation --- IO doesn't make sense until it
executes in an environment which it can effect. This is why we capture IO
(as well as other computational concepts) in monads, and why (again IMHO)
mondadic IO is so much more effective and intuitive than continuation style
IO or stream based IO ever was.

Dave Barton

Creighton Hogg

unread,
Dec 21, 2005, 10:35:52 AM12/21/05
to haskel...@haskell.org
On Wed, 21 Dec 2005, Daniel Carrera wrote:

> Creighton Hogg wrote:
> > x is a String, getLine has type IO String. That's what I
> > was getting at in one of my last e-mails.
>
> Hmm... let's see if I understand:
>
> * getLine() has type IO String.
> * The <- will "convert" an IO String to a plain String
> * So if I do x <- getLine() then x has the type String.
>
> So, the <- effectively ammounts to an IO a -> a conversion.

Yeah, pretty much. You're saying that "x is the result of
action getLine", and in terms of types it means you're
getting the a out of m a.



> In another email John Hughes said that one could think of "IO a" as a
> set of instructins for obtaining a. I guess that means that IO is a sort
> of imperative layer that helps the purely functional code interact with
> the outside world.
>
> So I can have an IO bit (e.g. a do-block) that calls functions (which
> are purely functional code) but I can't have a function that executes
> any IO.
>
> For example, it is not possible to write a function "my_read_file" that
> could work like this:
>
> my_data = my_read_file("my_file.txt")
>
> Correct? Otherwise this would be a function that is not referentially
> transparent.

Assuming I understand correctly, then you are right. "="
implies definition. my_data is the symbol assigned to the
result of a computation, not a defined constant or function.
That's at least how I think of referential transparency.

Wolfgang Jeltsch

unread,
Dec 21, 2005, 12:31:20 PM12/21/05
to haskel...@haskell.org
Am Mittwoch, 21. Dezember 2005 16:28 schrieb David Barton:
> Wolfgang Jeltsch writes:
> ----- Original Message -----
>
> > Am Mittwoch, 21. Dezember 2005 13:15 schrieb Creighton Hogg:
> >> [...]
> >>
> >> Monads, I believe, can be just thought of as containers for state.
> >
> > I would say that you are talking especially about the I/O monad here. A
> > monad as such is a rather general concept like a group is in algebra.
>
> While this is correct, I'm afraid that for most of us it is a flavorless
> answer. I wish I had the mathematical mind that made the word "group" in
> this context instantly intuitively recognizable, but I don't.

The point is that the concept of a monad is a rather abstract one. Maybe we
should take vector spaces instead of groups. A vector space is just a set
together with two operations, namely vector addition and scalar
multiplication whereby the operations have to fulfill a couple of laws. One
law, for example, would be that vector addition is commutative.

The point is that this vector space concept is very general. There are a lot
of concrete vector spaces which all fit that general idea. For example, R^2
together with the commonly defined vector addition and scalar multiplication
is a vector space. But there are many others, lots of which seem to have
nothing to do with R^2 and its operations from the first sight. So we have a
general concept "vector space" and a lot of concrete vector spaces.

Now, "monad" is a general concept like "vector space". In Haskell terms a
monad is a type of kind * -> * (i.e., a type which needs to be applied to one
type parameter in order to be able to be used as a type of expressions)
together with operations (>>=) and return whereby (>>=) and return have to
have specific types and have to fulfill certain laws.

IO together with its (>>=) and return is an example of a concrete monad. []
together with concatMap and \x -> [x] is another one. This may seem very
surprising at first ("What do lists have to do with I/O computations?") but
it's really so.

> I think Phil Wadler said it best when he said that a monad is a
> *computation*.

No, a value of type IO <something> is a computation. The type IO plus (>>=)
plus return is a monad. So is [] plus concatMap plus \x -> [x].

> [...]

> Dave Barton

Best wishes,
Wolfgang

Greg Woodhouse

unread,
Dec 21, 2005, 1:03:05 PM12/21/05
to Wolfgang Jeltsch, haskel...@haskell.org
--- Wolfgang Jeltsch <wolf...@jeltsch.net> wrote:

>
> > I think Phil Wadler said it best when he said that a monad is a
> > *computation*.

To be honest, I'm still struggling with the monad concept myself. Oh
sure, I can read the definition and it makes sense. But I'm still
missing that "aha!" moment when it all comes together.

>
> No, a value of type IO <something> is a computation. The type IO
> plus (>>=)
> plus return is a monad. So is [] plus concatMap plus \x -> [x].


This may be totally off-base, but when I read this, it occured to me
that all I/O is basically a computation made to appear as if it is
something your program "does". You (or, rather the processor) don't
execute instructions to write "Hello" in same way as, say, adding 2 and
2. Rather, you add writing this string to a "to do" list and wait for a
driver to respond to an interrupt, pick up the request(s), and carry it
(them) out when control passes back the kernel.
>


===
Gregory Woodhouse <gregory....@sbcglobal.net>
"Interaction is the mind-body problem of computing."
--Philip L. Wadler

Wolfgang Jeltsch

unread,
Dec 21, 2005, 2:44:49 PM12/21/05
to haskel...@haskell.org
Am Mittwoch, 21. Dezember 2005 19:02 schrieben Sie:
> [...]

> You (or, rather the processor) don't execute instructions to write "Hello"
> in same way as, say, adding 2 and 2.

Exactly!

> Rather, you add writing this string to a "to do" list and wait for a driver
> to respond to an interrupt, pick up the request(s), and carry it (them) out
> when control passes back the kernel.

I don't completely understand what you mean but I think it goes into the right
direction. We can illustrate the process of executing a Haskell program as
follows.

We have a "evaluation machine" and a "execution machine". The former
evaluates expressions, the latter executes I/O actions. When the program is
started, the execution machine wants to execute the actions from the "main to
do list". In order to be able to do so, it asks the evaluation machine to
start evaluating main. For each entry on the to do list, main is evaluated
as far as it's necessary to provide the execution machine with the entry. Of
course, evaluating main may result in evaluating other expressions.

The point is that the evaluation machine does never execute any I/O actions
while the execution machine is unable to do any evaluation but needs to be
feeded by the evaluation machine.

Best wishes,
Wolfgang

Wolfgang Jeltsch

unread,
Dec 22, 2005, 9:25:31 AM12/22/05
to haskel...@haskell.org
Am Donnerstag, 22. Dezember 2005 03:32 schrieb Gregory Woodhouse:
> [...]

> If this analogy is at all valid, then I wonder if the IO monad, at
> least, doesn't represent something of a blurring of the traditional
> distinction between applications and operating system functionality.

An application can modify local state, e.g., a C variable. Purely functional
code cannot do this.

> Incidentally, devices do not necessarily correspond to hardware and
> you can write drivers to do all sorts of interesting things if you
> are so inclined -- just as monads are not limited in their use to I/O.

The IO monad is not just about I/O but also about modifiable variables. Most
other monads only deal with purely functional stuff. For example, a list has
nothing to do with modifiable variables and I/O at all.

> > We have a "evaluation machine" and a "execution machine". The former
> > evaluates expressions, the latter executes I/O actions.
>

> This is where I'm a bit fuzzy. "Actions" make think of something like
> a finite automaton, where the action would be (possibly) consuming an
> input token and changing state.

"Actions" makes me think of things like I/O and modifying variables in
contrast to calculations which don't really "act". Of course, an I/O action,
for example, modifies a state, namely the state of the outside world.

> > When the program is started, the execution machine wants to execute the
> > actions from the "main to do list". In order to be able to do so, it asks
> > the evaluation machine to start evaluating main. For each entry on the to
> > do list, main is evaluated as far as it's necessary to provide the
> > execution machine with the entry. Of course, evaluating main may result
> > in evaluating other expressions.
>

> I imagine this is somewhat analogous to a C statement like
>
> c = getchar();
>
> Evaluation is "blocked" (and the process becomes not runnable) until
> such a time as getchar() returns a value. If there is no buffered
> input, a system call will be required, and control will pass to the
> driver for the appropriate device. When control returns to the
> calling process it can, of course, go on until it again
> (transparently) blocks, waiting for input or other actions.

I meant it the other way round. The execution machine does something like the
following:

while (actionsRemaining()) {
nextAction = getNextAction();
executeAction(nextAction);
}

getNextAction() will force the evaluation machine to evaluate as much of main
as is needed to provide the execution machine with the next action.

> > The point is that the evaluation machine does never execute any I/O
> > actions while the execution machine is unable to do any evaluation but
> > needs to be feeded by the evaluation machine.
>

> Is this a reasonable analogy?

I think so. :-)

> [...]

Bulat Ziganshin

unread,
Dec 23, 2005, 4:28:40 AM12/23/05
to Daniel Carrera, Haskell-Cafe Cafe
Hello Daniel,

Wednesday, December 21, 2005, 6:24:29 PM, you wrote:

DC> So I can have an IO bit (e.g. a do-block) that calls functions (which
DC> are purely functional code) but I can't have a function that executes
DC> any IO.

it's true

DC> For example, it is not possible to write a function "my_read_file" that
DC> could work like this:

DC> my_data = my_read_file("my_file.txt")

DC> Correct? Otherwise this would be a function that is not referentially
DC> transparent.

you are right. type "IO a", after all, stands for "RealWorld ->
(a,RealWorld)", i.e. it gets RealWorld as parameter and returns,
besides value of type "a", a new RealWorld state. the function which
type don't ended with "IO a", just can't receive or return value of
type RealWorld, so there is just no way to check something outside or
return new state of the outer world


--
Best regards,
Bulat mailto:bul...@HotPOP.com

Glynn Clements

unread,
Dec 23, 2005, 6:12:27 AM12/23/05
to Daniel Carrera, Haskell-Cafe Cafe

Daniel Carrera wrote:

> I'm a Haskell newbie and I don't really understand how Haskell deals
> with functions that really must have side-effects. Like a rand()
> function or getLine().

Those aren't functions.

A function is a single-valued relation, i.e. a (possibly infinite) set
of ordered pairs <x,y> such that the set doesn't contains two pairs
<a,b> and <c,d> where a == c and b =/= d. IOW, a static mapping from
argument to result.

Haskell uses the term "function" to mean a function in the strict
mathematical sense, and not (like most other languages) to mean a
procedure which returns a value as well as reading and writing some
implicit state.

> I know this has something to do with monads, but I don't really
> understand monads yet. Is there someone who might explain this in newbie
> terms? I don't need to understand the whole thing, I don't need a rand()
> function right this minute. I just want to understand how Haskell
> separates purely functional code from non-functional code (I understand
> that a rand() function is inevitably not functional code, right?)

All Haskell code is functional (discounting certain low-level details
such as unsafePerformIO).

Side effects are implemented by making the prior state an argument and
the new state a component of the result, i.e. a C procedure of type:

res_t foo(arg_t);

becomes a Haskell function with type:

ArgType -> State -> (State, ResType)

To simplify coding (particularly, making sure that you use the correct
iteration of the state at any given point), all of this is usually
wrapped up in an instance of the Monad class. But there isn't anything
special about Monad instances. The class itself and many of its
instances are written in standard Haskell.

To provide a concrete example, here's a monadic random number
generator:

type Seed = Int

data Rand a = R { app :: Seed -> (Seed, a) }

myRand :: Rand Int
myRand = R $ \seed -> let
result = (seed' `div` 65536) `mod` 32768
seed' = seed * 1103515245 + 12345
in (seed', result)

instance Monad Rand where
f >>= g = R $ \seed -> let (seed', x) = app f seed
in app (g x) seed'
return x = R $ \seed -> (seed, x)

runR :: Seed -> Rand a -> a
runR seed f = snd $ app f seed

Example usage:

randomPair :: Rand (Int, Int)
randomPair = do
myRand >>= \x ->
myRand >>= \y ->
return (x, y)

or, using "do" notation (which is simply syntactic sugar):

randomPair :: Rand (Int, Int)
randomPair = do
x <- myRand
y <- myRand
return (x, y)

main = print $ runR 99 randomPair

The main difference between the built-in IO monad and the Rand monad
above is that where the Rand monad has a Seed for its state, the IO
monad has the (conceptual) World type.

As the World type has to represent the entire observable state of the
universe, you can't actually obtain instances of it within a Haskell
program, and thus there is no equivalent to runR.

Instead, you provide an IO instance (main) to the runtime, which
(conceptually) applies it to the World value representing the state of
the universe at program start, and updates the universe to match the
World value returned from main at program end.

--
Glynn Clements <gl...@gclements.plus.com>

Dylan Thurston

unread,
Dec 26, 2005, 2:25:45 PM12/26/05
to haskel...@haskell.org
On Wed, Dec 21, 2005 at 11:43:42AM +0000, Daniel Carrera wrote:
> Hi all,
>
> I'm a Haskell newbie and I don't really understand how Haskell deals
> with functions that really must have side-effects. Like a rand()
> function or getLine().
>
> I know this has something to do with monads, but I don't really
> understand monads yet. Is there someone who might explain this in newbie
> terms? I don't need to understand the whole thing, I don't need a rand()
> function right this minute. I just want to understand how Haskell
> separates purely functional code from non-functional code (I understand
> that a rand() function is inevitably not functional code, right?)

You said you were a mathematician, so let me give you the mathematical
version in case you like category theory:

A monad is an monoid object in the category of endofunctors.

That is, a monad is an endofunctor 'F' (i.e., a data type depending on
a single parameter, which is furthermore an instance of Functor), with
a natural transformations from 'F (F a)' to 'F a' and from 'a' to
'F a', satisfying the usual rules for identity and associativity.

Exercise: List is a monad. So is Maybe.

You can think about a monad 'F a' as a computation of an object of
type 'a' with side effects; for instance, the List monad allows the
side-effect of multiple answers (like non-determinism), and the Maybe
monad allows the side-effect of failure. One particularly useful and
complicated monad is the IO monad, which allows interactions with the
outside world.

Peace,
Dylan

signature.asc
0 new messages