I need to run a function that takes two arguments several times. I have two lists containing these arguments and I'd like to be able to use map
or something similar to call the function with the corresponding args.
The function I want to call has this type:
runParseTest :: String -> String -> IO()
The lists are created like this:
-- 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 lets 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 I could create a helper function that does this fairly easily, but out of interest, is it possible to do in one line using map
?
This is what I have so far, but obviously the first argument is always "test":
mapM_ (runParseTest "test") libs
I apologise if this is unclear. I can provide more info if necessary.
While waiting for answers I created a solution of my own with a new function called
map2M_
based on the source code formap
andmapM_
:It's possible to do that with
zip
:Or maybe
uncurry runParseTest
:Or with
zipWith
:And like Ozgur said, there are some analogues for monads:
You are looking for zipWithM_.
You say you could write a helper function which does this. Which means you know the type of the function you are looking for. In such cases you can use hoogle.
(Try: Monad m => [a] -> [b] -> m ())
This is a great time to use Hoogle! Hoogle is a search engine for searching Haskell types. For instance, a Hoogle query for
(a -> b) -> [a] -> [b]
pulls upmap
. Here, you have a function of typeString -> String -> IO ()
; you want a function of type(String -> String -> IO ()) -> [String] -> [String] -> IO ()
. Hoogle can often generalize by itself, but it's having trouble here, so let's help it out: You just want(a -> a -> IO ()) -> [a] -> [a] -> IO ()
for anya
. If you Hoogle for that type signature, the first result iszipWithM_ :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m ()
in the Control.Monad module, which does exactly what you want. This is part of a family of functions, with varying degrees of generality:zip :: [a] -> [b] -> [(a,b)]
, which pairs up two lists, truncating the shorter one.zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
, which runs a supplied function on elements from each of the two lists;zip = zipWith (,)
.zipWithM :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m [c]
, which is likezipWith
inside a monad;zipWithM f xs ys = sequence $ zipWith f xs ys
.zipWithM_ :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m ()
, which is likezipWithM
but discards its result;zipWithM_ f xs ys = zipWithM f xs ys >> return () = sequence_ $ zipWith f xs ys
.zip3 :: [a] -> [b] -> [c] -> [(a, b, c)]
, whose functionality I'm sure you can figure out :-)zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
, which is likezipWith
on three lists;zipWith3 = zip (,,)
.zipN
andzipWithN
functions in Data.List, going up throughzip7
/zipWith7
. (Arguably, this starts withid :: [a] -> [a]
aszip1
andmap :: (a -> b) -> [a] -> [b]
aszipWith1
, which is where your question comes from.)ZipList
applicative functor. Given some listsxs1
…xsN
, thenrunZipList $ f <$> ZipList xs1 <*> ZipList xs2 <*> ... <*> ZipList xsN = runZipList $ liftAN f (ZipList xs1) ... (ZipList xsN
) behaves just likezipWithN f xs1 ... xsN
.So, in your specific use case, we're going to have—with a few extra changes—the following: