Elif Ak
Elif Ak

Reputation: 59

Read IO String and return custom data type list in Haskell

I am beginner in Haskell and I struggle with IO operations. I should get IO String from user until user send "." (point) character. I will use these inputs to transform Custom Data Type, naming Card, by parsing the String. And then I will add the Card to the list. When user types the ".", I will send whole Card List. Haskell code is below:

readCards :: IO [Card]
readCards = return (returnCardList [])
    where 
        returnCardList :: [Card] -> [Card]
        returnCardList acc =  do line <- getLine
                                 if line == "."
                                 then acc
                                 else returnCardList ((convertCard (line !! 0) (line !! 1)):acc)

convertCard is a function which takes two char and return Card. Also acc stand for accumulator for tail recursion function (It is not necessary to implement with tail recursion, I just chose it).

For example returnCardList 'h' 'q' gives Card {suit=Hearts, rank=Queen}

However above code partition gives an error:

 Couldn't match type `IO' with `[]'
 Expected type: [String]
 Actual type: IO String

However below code (with dummy Card List) run properly:

readCards :: IO [Card]
readCards = return [Card {suit=Clubs, rank=King}, Card {suit=Clubs, rank=Ace}, Card {suit=Clubs, rank=Jack}]

I read a lot of things but I couldn't solve it. I am really wonder what I miss.

Upvotes: 0

Views: 884

Answers (1)

chi
chi

Reputation: 116174

This code

readCards :: IO [Card]
readCards = return (returnCardList [])

is stating that readCards is an IO computation that does not actually do IO. Indeed return something implies that no IO is being performed, and that something is a pure value of type [Card].

This is not what you want. You need something like

readCards :: IO [Card]
readCards = returnCardList []

Consequently, the IO must be done by returnCardList, which must now have the type

returnCardList :: [Card] -> IO [Card]
                         -- ^^ we do perform IO here!

Your own implementation should work, roughly

    returnCardList acc = do 
       line <- getLine
       if line == "."
       then return acc
         -- ^^^^^^ turn the plain value into an IO computation
       else returnCardList ((convertCard (line !! 0) (line !! 1)):acc)

This can be rewritten as follows:

    returnCardList acc = do 
       line <- getLine
       case line of
          "." -> return acc
          (c1:c2:_) -> returnCardList (convertCard c1 c2 : acc)
          _ -> ... -- you should handle here the other cases (length < 2)

Note that you can also do without an acc parameter:

    returnCardList = do 
       line <- getLine
       case line of
          "." -> return []
          (c1:c2:_) -> (convertCard c1 c2 :) <$> returnCardList
          _ -> ... -- you should handle here the other cases (length < 2)

Upvotes: 1

Related Questions