Haskell: filtering through a dictionary / hash / map with IO () values

I have a function whose signature is String β†’ String β†’ IO (Maybe String) Now I use this function to create values ​​for the dictionary, and as a result I get: [(String, IO (Maybe String))]

I need to analyze the values ​​in the dictionary and return the corresponding key based on the result. I was hoping to use a filter on the map to get through it, but I can't think of a way to extract this IO action on the fly. So, how do I display / filter through a dictionary that performs an I / O action, and based on the result of the value of the I / O function, return the corresponding dictionary key? Is there an easy way to do this, or am I just messed up?

Thanks.

+4
source share
2 answers

Perhaps the solution is to use sequence with something like

 sequence . map (\(a,mb) -> (mb >>= \b -> return (a,b))) 

then you can simply use liftM to apply the filter to the resulting IO [(String,Maybe String)] .

liftM is located in Control.Monad. Alternatively, in the notation

 myFilter :: (String,(Maybe String)) -> Bool) -> [(String,IO (Maybe String))] -> IO [(String,(Maybe String))] myFilter f ml = do l <- sequence . map (\(a,mb) -> (mb >>= \b -> return (a,b))) $ ml return $ filter f ml 

Perhaps some refactoring is fine. Often when working with monads, you want to use mapM instead of map .

Control.Monad also has a filterM function. This may be what you need.


Edit: in the comments it was stated that

 sequence . map (\(a,mb) -> (mb >>= \b -> return (a,b))) $ ml 

equivalently

 mapM (\(a,mb) -> fmap ((,) a) mb) ml 

So

 myFiter' f = (liftM $ filter f) . mapM (\(a,mb) -> fmap ((,) a) mb) 
+3
source

I have a function whose signature is String -> String -> IO (Maybe String) Now I use this function to create values ​​for the dictionary, and as a result I get: [(String,IO (Maybe String))]

This part does not add up for me. How do you enter only 2 lines and end with a significant result like [(String, IO (Maybe String))] ?

With no data, let's assume your situation is something like this:

 f :: String -> String -> IO (Maybe String) magic :: String -> (String, String) 

magic takes some key and somehow breaks the key into two input lines needed for f . So, suppose you have a list of keys, you can use [(String, IO (Maybe String))] as follows:

 -- ks :: [String] myMap = map (\k -> let (a,b) = magic k in (k, fab)) ks 

But ... wouldn't it be better if there were [(String, Maybe String)] instead? Assuming we are inside the action of IO ...

 someIOAction = do ... myMap <- monadicMagicks (\k -> let (a,b) = magic k in (k, fab)) ks 

But what monadicMagicks could we use to do it right? Let's look at the types that we expect from these expressions.

 monadicMagicks (\k -> ...) ks :: IO [(String, Maybe String)] (\k -> let (a,b) = magic k in (k, fab)) :: String -> (String, IO (Maybe String)) ks :: [String] -- therefore monadicMagicks :: (String -> (String, IO (Maybe String))) -> [String] -> IO [(String, Maybe String)] 

Stop ... Google time. Let's first summarize what we are looking for. Here we have two main data types: String , which is represented as "input", and Maybe String , which is represented as "output". Therefore, replace String with a and Maybe String with b . So, we are looking for (a -> (a, IO b)) -> [a] -> IO [(a, b)] . Hmm No results found. Well, if we could only get the result type IO [b] , then in the do block we could get it from IO, and then zip with the original list of keys. It also means that the function we are inserting would not have to pair the key with the result. Therefore, let's simplify what we are looking for and try again: (a -> IO b) -> [a] -> IO [b] . Hey check it out! mapM matches this type signature fine.

 monadicMagicks :: (a -> IO b) -> [a] -> IO [(a, b)] monadicMagicks f xs = do ys <- mapM f xs return $ zip xs ys 

This can be written more concisely and with a more general signature like:

 monadicMagicks :: Monad m => (a -> mb) -> [a] -> m [(a, b)] monadicMagicks f xs = zip xs `liftM` mapM f xs someIOAction = do ... -- Like we said, the lambda no longer has to pair up the key with the result myMap <- monadicMagicks (\k -> let (a,b) = magic k in fab) ks 

This last line can also be rewritten a little shorter if you are comfortable with the composition of functions:

  myMap <- monadicMagicks (uncurry f . magic) ks 

Please note that monadicMagicks may not make much sense for some monads, so this is probably not in standard libraries. (For example, using mapM in a monad list usually means that the result will have a different length than the input):

 ghci> mapM (\x -> [x, -x]) [1,2] [[1,2],[1,-2],[-1,2],[-1,-2]] 
0
source

Source: https://habr.com/ru/post/1396116/


All Articles