[Haskell-beginners] Convert String to List/Array of Numbers

Brent Yorgey byorgey at seas.upenn.edu
Wed Sep 8 14:17:56 EDT 2010

On Wed, Sep 08, 2010 at 07:24:12PM +0200, Lorenzo Isella wrote:
> Hi Daniel,
> Thanks for your help.
> I have a couple of questions left
> (1) The first one is quite down to earth.
> The snippet below
> ---------------------------------------------------
> main :: IO ()
> main = do
>   txt <- readFile "mydata.dat"
>   let dat = convert txt
>   print dat -- this prints out my chunk of data
>   return ()
> convert x = lines x
> -----------------------------------------------

Looks good.  Note that the return () is not necessary since 'print
dat' already results in ().

> pretty much does what it is supposed to do, but if I use this
> definition of convert x
> convert x = map (map read . words) . lines x

That ought to be

  convert = map (map read . words) . lines

or alternatively

  convert x = map (map read . words) (lines x)

The dot (.) is function composition, which lets you make "pipelines"
of functions.  So the first one says "convert is the function obtained
by first running 'lines' on the input, and then running 'map (map read
. words)' on the output of 'lines'.  You can also say explicitly what
to do with the input x, as in the second definition.  These two
definitions are exactly equivalent.

> (2) This is a bit more about I/O in general. I start an action with
> "do" to read some files and I define outside the action some
> functions which are supposed to operate (within the do action) on the
> read data.
> Is this the way it always has to be? I read something about monads
> but did not get very far (and hope that they are not badly needed for
> simple I/O).

When you do I/O you are using monads whether you know it or not!  But
no, you don't need a deep understanding of monads to do simple I/O.

In any event, this has nothing to do with monads in general, but is
particular to IO.  And yes, this is the way it always has to be with
I/O: there is no way to "escape", that is, there is no function* with
the type

  escapeIO :: IO a -> a

The problem is that because of Haskell's laziness, if there were such
a function you would have no idea when all the effects (like reading a
file, writing to disk, displaying something on the screen) would
happen -- or they might happen twice, or not at all!  Because of
Haskell's purity, the compiler is free to reorder and schedule
computations however it likes, and throwing side effects into the mix
would simply wreak havoc.

> Am I on the right track here? And what is the benefit of this?

The benefit is precise control of side effects, and what is known as
"referential transparency": if you have a function of type

  Int -> Int

then you know for certain that it only computes a numerical function.
Calling it will never result in things getting written to disk or the
screen or anything like that, and calling it with the same input will
always give you the same result.  This is a very strong guarantee that
gives you powerful ways to reason about programs.


* Actually, there is, but it is only for use in very special low-level
  sorts of situations by those who really know what they are doing.

More information about the Beginners mailing list