Joe Fiorini
Joe Fiorini

Reputation: 641

Mixing IO with State computation

As an exercise I'm writing a command-line RPN calculator in Haskell. The idea is it will prompt for input (a number or operator) and print out the new stack. My plan is to store the list of numbers in a state monad, and perform calculations against that list. For example:

> 4
[4]
> 3
[3,2]
> 5
[5,3,2]
> +
[8, 2]

and so on.

I'm starting with just trying to build up the list in the State monad with input & output on each entry. I'm already stuck due to the combination of IO & State in the same function. My problem is, I also need to recurse on the input, to keep the prompt going after the first number is input.

Here is my code so far:

module Main where

import Control.Monad.State

addEntry :: Int -> State [Int] Int
addEntry entry = do
  entries <- get
  put (entry : entries)
  return entry

mainLoop :: [Int] -> IO ()
mainLoop entries = do
  entry <- readLn
  newEntries <- execState (addEntry entry) entries
  print newEntries
  mainLoop newEntries

main :: IO ()
main = do
    print $ mainLoop []

and here is the compiler error I'm currently getting:

src/Main.hs@14:28-14:42 Couldn't match type [Int] with ‘IO [Int]’
Expected type: State (IO [Int]) Int
  Actual type: State [Int] Int …
src/Main.hs@14:44-14:51 Couldn't match expected type ‘IO [Int]’ with actual type [Int] …

Any tips on how to structure these functions such that I'm not combining IO & State?

Upvotes: 4

Views: 515

Answers (1)

Squidly
Squidly

Reputation: 2717

I'm not sure if you're using state because you want to try it out, but you can achieve the state itself without the 'bother' of the state monad.

module Main where

addEntry :: Int -> [Int] -> [Int]
addEntry = (:)

mainLoop :: [Int] -> IO ()
mainLoop entries = do
  entry <- readLn
  let newEntries = addEntry entry entries
  print newEntries
  mainLoop newEntries

main :: IO ()
main = mainLoop []

Upvotes: 4

Related Questions