Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

haskell-problem: io string -> [int]

Hello great programmers out there,

I'm doing my first steps in haskell and have a function that confuses me:

import Data.List.Split
getncheck_guesslist = do
    line <- getLine
    let tmp = splitOneOf ",;" line
    map read tmp::[Int]

splitOneOf is in Data.List.Split (i installed it with cabal install split) splitOneOf :: (Eq a)=> [a]->[a]->[[a]]

From the error I get it that there is some type incorrectness - but don't know how to solve this conflicts as IO is still a mystery to me

I want to read an input of integers separated by commas or semicolons and get a list of integers so:

  • how can I check if user input is of type Int
  • how can I "translate" the input which is of type "IO String" to [Int]

thank you in advance for thoughts and hints - yours ε/2

like image 667
epsilonhalbe Avatar asked Feb 23 '11 12:02

epsilonhalbe


2 Answers

When you're writing a function that uses the IO monad, any value you want to return from the function must also be in the IO monad.

This means that, instead of returning a value with type [Int], you have to return something with type IO [Int]. To do this, you use the return function, which "wraps up" the value into IO (it actually works for any monad).

Just change the last line to wrap your value with return, like this:

getncheck_guesslist = do
    line <- getLine
    let tmp = splitOneOf ",;" line
    return (map read tmp :: [Int])
like image 80
porges Avatar answered Sep 23 '22 11:09

porges


The "right way" to do it in Haskell is to separate IO from, well, everything else. The direct translation of your code would be this:

getncheck_guesslist :: IO [Int]
getncheck_guesslist = do line <- getLine               -- get
                         return (check_guesslist line) -- check

check_guesslist :: String -> [Int]
check_guesslist line = let tmp = splitOneOf ",;" line
                       in map read tmp

Notice that getncheck_guesslist is simply an IO action. The function doesn't have input parameters, even though it does require (IO) input from getLine.

Also notice that getncheck_guesslist is a simple modification of the getLine IO action. Isn't there a combinator that would let me push a function to act on the value inside a monad? Stop. Hoogle time!

I have a function (a -> b). I have a value of the input type, but it's stuck in a monad m a. I want to perform the function inside the monad, so the result will inevitably be stuck in the monad too m b. Putting that all together, we hoogle (a -> b) -> m a -> m b. Lo and behold, fmap is just what we were looking for.

get_guesslist = check_guesslist `fmap` getLine
-- or, taking it a step further
get_guesslist = (map read . splitOneOf ",;") `fmap` getLine :: IO [Int]

As a final note, whenever you code a method with the name like somethingAndSomethingElse, it's usually better coding style to write and invoke something and somethingElse as two separate methods. For the final versions, I just renamed it get_guesslist, since conceptually that's what it does. It gets the guesses as a list of Ints.

As a final final note, I have left off at the point where barsoap started. ;) fmap is the same as <$>.

like image 29
Dan Burton Avatar answered Sep 21 '22 11:09

Dan Burton