Using a card with two lists, not one. Can you nest?

I need to run a function that takes two arguments multiple times. I have two lists containing these arguments, and I would like to be able to use map or something similar to call a function with the corresponding arguments.

The function I want to call has this type:

 runParseTest :: String -> String -> IO() 

Lists are created as follows:

 -- Get list of files in libraries directory files <- getDirectoryContents "tests/libraries" -- Filter out ".." and "." and add path let names = filter (\x -> head x /= '.') files let libs = ["tests/libraries/" ++ f | f <- names] 

So, let's say that names contains ["test1.js", "test2.js", "test3.js"] and libs contains ["tests/libraries/test1.js", "tests/libraries/test2.js", "tests/libraries/test3.js"]

I want to call them like this:

 runParseTest "test1.js" "tests/libraries/test1.js" runParseTest "test2.js" "tests/libraries/test2.js" runParseTest "test3.js" "tests/libraries/test3.js" 

I know that I can create a helper function that makes this pretty easy, but out of interest, is it possible to use map on one line?

This is what I have so far, but obviously the first argument is always "test":

 mapM_ (runParseTest "test") libs 

I apologize if this is unclear. If necessary, I can provide additional information.

+6
source share
4 answers

So let's say that the names contain ["test1.js", "test2.js", "test3.js"] and the libs contains ["tests/libraries/test1.js", "tests/libraries/test2.js", "tests/libraries/test3.js"]

I want to call them like this:

runParseTest "test1.js" "tests/libraries/test1.js" runParseTest "test2.js" "tests/libraries/test2.js" runParseTest "test3.js" "tests/libraries/test3.js"

This can be done using zip :

 map (\(a,b) -> runParseTest ab) $ zip names libs 

Or maybe uncurry runParseTest :

  map (uncurry runParseTest) $ zip names libs 

Or using zipWith :

  zipWith runParseTest names libs 

And, like Ozgur , there are several analogues for monads:

 > :t zipWithM zipWithM :: Monad m => (a -> b -> mc) -> [a] -> [b] -> m [c] > :t zipWithM_ zipWithM_ :: Monad m => (a -> b -> mc) -> [a] -> [b] -> m () 
+10
source

This is a great time to use Hoogle ! Hoogle is a Haskell type search engine. For example, a Hoogle request for (a -> b) -> [a] -> [b] calls map . Here you have a function of type String -> String -> IO () ; you need a function like (String -> String -> IO ()) -> [String] -> [String] -> IO () . Hoogle can often generate on its own, but it has problems, so let's help it: you just want (a -> a -> IO ()) -> [a] -> [a] -> IO () for any a . If you are Hoogle for this type signature, the first result is zipWithM_ :: Monad m => (a -> b -> mc) -> [a] -> [b] -> m () in the Control.Monad module, which does exactly that you want. This is part of a family of functions with varying degrees of generality:

So, in your specific use case, we will have - with a few additional changes:

 import Data.List (isPrefixOf) ... -- I got rid of `head` because it a partial function, and I prefer `map` to -- list comprehensions for simple things do files <- getDirectoryContents "tests/libraries" let names = filter (not . ("." `isPrefixOf`)) files libs = map ("tests/libraries/" ++) names zipWithM_ runParseTest names libs 
+15
source

You are looking for zipWithM_ .

You say you can write a helper function that does this. This means that you know the type of function you are looking for. In such cases, you can use hoogle .

(Try: Monad m => [a] → [b] → m ())

+3
source

While waiting for answers, I created my own solution with a new map2M_ function based on the source code for map and mapM_ :

 map2 :: (a -> b -> c) -> [a] -> [b] -> [c] map2 _ [] _ = [] map2 _ _ [] = [] map2 f (a:as) (b:bs) = fab : map2 f as bs map2M_ :: Monad m => (a -> b -> mc) -> [a] -> [b] -> m () map2M_ f as bs = sequence_ (map2 f as bs) 
+1
source

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


All Articles