haskell-problem: io string -> [int]

前端 未结 4 1696
天涯浪人
天涯浪人 2021-01-18 00:18

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_guessl         


        
相关标签:
4条回答
  • 2021-01-18 00:58

    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])
    
    0 讨论(0)
  • 2021-01-18 00:58
    import Data.List.Split
    import Control.Applicative
    
    getncheck_guesslist :: IO [Int]
    getncheck_guesslist = map read . splitOneOf ",;" <$> getLine
    

    Every time code bogs down to foo >>= return . bar, which yours does by desugaring the do-block (and correcting the type error), you are not using the monadic features of your monad, but only its functor part, that is, bluntly said, you are not messing with the IO part of the type, but with the a of IO a:

    (<$>) :: (Functor f) => (a -> b) -> f a -> f b
    

    (fmap would be the non-infix name for <$>. Both are closely related to map.)

    That code above is pretty much idiomatic for ad-hoc code, but clean code would look like

    import Data.Maybe
    
    maybeRead :: Read a => String -> Maybe a
    maybeRead = fmap fst . listToMaybe . reads 
                                   -- That fmap is using "instance Functor Maybe"
    
    parseGuessList :: String -> [Int]
    parseGuessList =  catMaybes . map maybeRead . splitOneOf ",;"
    
    getncheck_guesslist = parseGuessList <$> getLine
    

    , or, alternatively, if you don't want to ignore non-int input but error out,

    parseGuessList xs = if success then Just . catMaybes $ ys else Nothing 
      where ys :: String -> [Mabye Int]
            ys = map maybeRead . splitOneOf ",;" $ xs
            success = all isJust ys
    

    (Beware, though, I have only proven that code correct, not actually tried it.)

    If it gets any more complex than that you'd want to use a proper parsing library, I think.

    0 讨论(0)
  • 2021-01-18 01:01

    If something is within the IO monad, you can't take it to the pure outside world for further processing. Instead, you pass your pure functions to work inside the functions within IO.

    In the end, your program reads some input and writes some output, so your main function is going to work with IO, else you'll just not be able to output anything. Instead of reading IO String and creating an [Int], pass the function that consumes that [Int] into your main function and use it inside do.

    0 讨论(0)
  • 2021-01-18 01:12

    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 <$>.

    0 讨论(0)
提交回复
热议问题