Traversable
| traverse | |
| sequence | |
| Effects in order | |
| for and mapM |
traverse
traverse maps a function that returns a context and flips the two layers. A list of Maybe becomes a Maybe of a list:
traverse :: Applicative f => (a -> f b) -> [a] -> f [b]
traverse (\x -> if x > 0 then Just x else Nothing) [1,2,3]
-- Just [1,2,3]
traverse (\x -> if x > 0 then Just x else Nothing) [1,-2,3]
-- Nothing
One failure anywhere makes the whole result fail, and the error is the first one encountered. That is exactly the behaviour of a chain of >>=, written once for the whole list.
sequence
When the list already holds the contexts, sequence does the flipping without a function. It is traverse id:
sequence [Just 1, Just 2, Just 3] -- Just [1,2,3]
sequence [Just 1, Nothing, Just 3] -- Nothing
sequence [[1,2], [3,4]] -- four combinations
The list instance produces the cartesian product, which is the same behaviour that made <*> surprising in lesson 30.
Effects in order
In IO, traversal is how a program runs an action per element and keeps the results. The order is the order of the list:
readAll :: [FilePath] -> IO [String]
readAll = traverse readFile
main = do
texts <- readAll ["a.txt", "b.txt"]
putStrLn (show (map length texts))
traverse over IO runs the effects left to right and collects the values. Nothing is parallel, and nothing is skipped on failure, because IO has no short-circuit.
for and mapM
Two aliases make the same function read better. for flips the arguments so the body comes last, and mapM is the monadic name you will see in older code:
for [1,2,3] \x -> do
putStrLn (show x)
pure (x * 2)
mapM print [1,2,3] -- the same as traverse print
Use for when the body is a block and
traverse when it is a single expression. Both
do the same work, and the compiler treats them identically.
Next:
Monad transformers
Article author: Arthur Isaev