Вы очень близки! То, что вы делаете в readPerss :: (Ord t, Num t) => t -> [IO (String, Int)]
, возвращает список IO
действий; каждое действие возвращает пару String
и Int
при его выполнении. В настоящее время в pFunc
вы только строите этот список действий, сохраняете его в переменной с let
и возвращаете его из pFunc
; вы никогда не выполняете их с помощью оператора <-
«bind».
Есть несколько простых способов сделать то, что вы хотите. Самое маленькое изменение в вашем коде, которое делает то, что вы хотите, это добавить sequence
, который принимает контейнер действий и производит действие , которое возвращает контейнер :
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
Здесь t
равно []
, m
равно IO
, а a
равно (String, Int)
:
sequence :: [IO (String, Int)] -> IO [(String, Int)]
Другой способ - переписать readPerss
, чтобы он выполнял действия напрямую, накапливая (String, Int)
результаты в списке, а не накапливая IO
действий :
readPerss :: (Ord t, Num t) => t -> IO [(String, Int)]
-- Change [IO …] to IO […]: ~~~~~~~~~~~~~~~~~~
readPerss n
| n > 0 = do
pers <- readPers
perss <- readPerss (n - 1)
return (pers : perss)
| otherwise = return []
Я знаю, что вы не должны использовать библиотечные функции, если это домашнее задание или упражнение, но в типичном коде «повторить x
действие n
раз и накапливать результаты» часто обозначают replicateM n x
:
replicateM :: Applicative m => Int -> m a -> m [a]