Ответ 1
map :: (a -> b) -> [a] -> [b]
putStrLn :: Show a => a -> IO ()
map putStrLn :: Show a => [a] -> [IO ()]
У вас есть список действий IO ()
.
main :: IO ()
Вам нужно объединить их в одно действие IO ()
.
Что вы хотите сделать, так это выполнить каждое из этих действий IO ()
в sequence/sequence_:
sequence :: Monad m => [m a] -> m [a]
sequence_ :: Monad m => [m a] -> m ()
Для удобства mapM/mapM_ будет отображать функцию над списком и упорядочить полученные монадические результаты.
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
Итак, ваш фиксированный код будет выглядеть так:
main = mapM_ putStrLn $ map fizzBuzz [1..100]
Хотя я бы, наверное, написал его вот так:
main = mapM_ (putStrLn . fizzBuzz) [1..100]
Или даже это:
main = putStr $ unlines $ map fizzBuzz [1..100]
Давайте напишем наш собственный sequence
. Что мы хотим сделать?
sequence [] = return []
sequence (m:ms) = do
x <- m
xs <- sequence ms
return $ x:xs
- Если в списке ничего не осталось, верните (введите в монаду) пустой список результатов.
- В противном случае, в монаде,
- Привязать (для монады
IO
это означает выполнение) первого результата. -
sequence
остальная часть списка; свяжите этот список результатов. - Возвращает минус первого результата и список других результатов.
- Привязать (для монады
Библиотека GHC использует нечто большее, чем foldr (liftM2 (:)) (return [])
, но это сложнее объяснить новичкам; на данный момент, просто возьмите мое слово, что они эквивалентны.
sequence_
проще, так как он не заботится о том, чтобы отслеживать результаты. Библиотека GHC реализует ее как sequence_ ms = foldr (>>) (return ()) ms
. Допустим только разложение определения foldr
:
sequence [a, b, c, d]
= foldr (>>) (return ()) [a, b, c, d]
= a >> (b >> (c >> (d >> return ())))
Другими словами, "do a
, отбросить результат, do b
, отбросить результат, & hellip; наконец, вернуть ()
".
mapM f xs = sequence $ map f xs
mapM_ f xs = sequence_ $ map f xs
С другой стороны, вам даже не нужно знать монады вообще с альтернативным решением unlines
.
Что делает unlines
? Ну, lines "a\nb\nc\nd\n" = ["a", "b", "c", "d"]
, поэтому, конечно, unlines ["a", "b", "c", "d"] = "a\nb\nc\nd\n"
.
unlines $ map fizzBuzz [1..100]
= unlines ["1", "2", "Fizz", ..]
= "1\n2\nFizz\n..."
, и он переходит в putStr
. Благодаря магии Haskell лень, полная строка никогда не должна быть построена в памяти, так что это будет радостно идти к [1..1000000]
или выше:)