Unwrapping a from IO (a)

一个人想着一个人 提交于 2021-01-28 07:31:50

问题


I've been learning Haksell the last 2 weeks and decided to try challenges at places such as Hackerrank. This has required learning IO. I have read many answers on stackExchange and the general gist is you don't unwrap IO a.. you just manipulate that data inside the IO function. That being the case what is the point of all the pure functions, if I'm not allowed to send data from main out to them? Here is some code that reads how many test cases, then for each test case reads N ordered pairs.

main = do
     test <- getLine
     replicateM (read test) doTest

doTest = do
    query<-getLine
    rs<-replicateM (read query) readPair
    return rs  -- just here to make the file compile

readPair :: IO (Int, Int)
readPair = do
   input <- getLine
   let a = words input in return (read (a!!0) :: Int, read (a!!1) ::Int)

At this point I have a IO [(Int,Int)] inside of rs. I would like to send that data to this function:

validFunction :: [(Int,Int)]->Bool
validFuntion [] = True
validFunction (x:[]) = True
validFunction (x:xs) = (not $ elem (snd x) (fmap snd xs))  && validFunction xs

But I can't seem to figure out how to do that. Any help or suggestions about how to call this function with the data I've read from the user would be appreciated. Or if I'm going about it from the wrong angle, and pointers on what I should be doing would also work.

Edit: From reading lots of other questions on here I've gotten the general Idea that once your in IO your stuck there. But what I can't seem to find is the syntax to call a pure function with IO data and get back IO data. I've tried some of the following :

fmap validFunction [rs] :: IO Bool  -- tried it with just rs without [] as well 
mapM validFunction [rs] :: IO Bool
validFunction rs :: IO Bool

I was able to get this to work:

 putStrLn . f . validFunction $ rs

Though I'm still not clear on why this lets you pass the IO [(Int,Int)] to validFunction.


回答1:


First of all, if you use x <- act in do, you essentially have a value. Unless you did something very suspicious, x isn't a IO something, but a something: So it's perfectly fine to use

foo :: Int -> Char
foo = …

bar :: IO Int
bar = …

fooDo :: IO Char
fooDo = do
   number <- bar
   return (foo number) -- apply foo directly on number

However, IO is an instance of Functor, so we can use fmap to lift foo:

liftedFoo :: IO Int -> IO Char
liftedFoo = fmap foo

So we could have written fooDo like this:

fooDo = fmap foo readLn

Although it's name is now misleading, it still does the same as before. But let's leave this naming voodoo aside, how would you tackle this? Well, your doTest has the correct type:

doTest :: IO [(Int, Int)]
doTest = do
    query  <- getLine
    rs     <- replicateM (read query) readPair
    return rs

So all that's missing is calling validFunction. We can do that like in fooDo:

doTest :: IO Bool
doTest = do
    query  <- getLine
    rs     <- replicateM (read query) readPair
    return (validFunction rs)
--         ^^^^^^^^^^^^^^^^^^
--         no IO  inside here
--  ^^^^^^ 
--   back 
--  to  IO

Or we can fmap over another IO value, like replicateM (read query) readPair:

doTest :: IO Bool
doTest = do
    query  <- getLine
    fmap validFunction (replicateM (read query) readPair)

The latter is harder to read, though. But you write your fooDo doTest as you want to do.



来源:https://stackoverflow.com/questions/43230210/unwrapping-a-from-io-a

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!