Haskell DISCUSSION

How do I get a plain String out of IO String in Haskell so a pure function can use it?

Started by alaasharaf Haskell IO monaddo notationpure functionsgetLinebind operator
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

How do I get a plain String out of IO String in Haskell so a pure function can use it?

alaasharaf Haskell Forum
#1

I am writing my first Haskell program that reads input. getLine has type IO String, and my function wordCount :: String -> Int wants a String, so wordCount getLine is rejected by the type checker. I have looked for a function of type IO String -> String and cannot find one.

How do I use the result of an IO action in ordinary code, and why is it designed this way?

Community replies 5

Re: How do I get a plain String out of IO String in Haskell so a pure function can use it?

#2

A value of type IO String is not a string that is hidden somewhere; it is a description of an action that will produce a string when it is run. There is deliberately no safe function IO a -> a. Instead of taking the value out, you bring your pure function to it inside another IO action:

main = do { line <- getLine; print (wordCount line) }

The arrow <- runs the action and binds its result, so line is an ordinary String for the rest of the block. With the input to be or not and wordCount = length . words, this prints 4.

Re: How do I get a plain String out of IO String in Haskell so a pure function can use it?

#3

Be clear about <- versus let. line <- getLine executes the action and names the result. let action = getLine only gives the action another name; nothing is read, and action still has type IO String. Inside a do block use let for pure values: let n = wordCount line.

Also, return is not a control-flow statement. return x is an action that does nothing and yields x; it does not leave the function, and statements after it in the block still run.

Re: How do I get a plain String out of IO String in Haskell so a pure function can use it?

#4

The do block is shorthand for the bind operator. The example above is getLine >>= \line -> print (wordCount line), where >>= has type IO a -> (a -> IO b) -> IO b for IO: run the first action and pass its result to a function that produces the next action.

When you only want to transform the result, use fmap: fmap wordCount getLine, also written wordCount <$> getLine, has type IO Int. The type keeps the IO marker, which is the point: anything computed from input is itself dependent on input.

Re: How do I get a plain String out of IO String in Haskell so a pure function can use it?

#5

The reason for the design is that a Haskell function must return the same result for the same arguments; that is what makes lazy evaluation and equational reasoning sound. Reading a line cannot satisfy that, so such operations are kept in the IO type, and the type signatures show exactly which parts of a program can touch the outside world.

The practical structure is a thin IO layer around a pure core. Put the logic in functions like process :: String -> String that can be tested in GHCi with no files or devices, and keep main short: main = do { text <- readFile "log.txt"; putStr (process text) }, or simply main = interact process for a filter that reads standard input.

Re: How do I get a plain String out of IO String in Haskell so a pure function can use it?

#6

For numeric input, remember that parsing is a separate, pure step. n <- readLn :: IO Int reads a line and parses it, but throws an exception on bad input. For anything a user types, prefer readMaybe from Text.Read: readMaybe line :: Maybe Int gives Just 42 for "42" and Nothing for "4x", and you handle both cases with a case expression.

You may come across unsafePerformIO, which does have the type you were looking for. It exists for low-level library work and breaks the guarantees described above; it is not the answer to this problem.

TEP COMMUNITY