…обобщить или факторизовать на что-то стандартное. Написался вот такой тип и вот такой пример работы с ним:
data IOList a = IolNil | IolCons a (IO (IOList a))
numbers4 :: Int -> IOList Int
numbers4 n = IolCons n (return $ numbers4 (n + 1))
-- ещё и код не ахти какой, надо было переписать с do и вообще по-другому
takeIol :: Int -> IOList a -> IO [a]
takeIol n | n <= 0 = \_ -> return []
| otherwise = f where
f IolNil = return []
f (IolCons x mxs) = mxs >>= \xs' -> (takeIol (n-1) xs' >>= \xs -> return $ x:xs)
ghci> takeIol 10 (numbers4 0)
[0,1,2,3,4,5,6,7,8,9]
и у меня складывается ощущение чего-то знакомого. [Сделать
numbers4 :: Int -> IO [Int] не получается по понятным соображениям: придётся сначала связать результат вычисления хвоста, чтобы получить действие-результат, и так мы будем бесконечно вычислять хвосты.]
___
Мне тут пока видится вот что. Получить тип списков мы можем как фиксированную точку функтора
data ListLevel a next = Nil | Cons a next deriving Functor
type List a = Fix (ListLevel a)Описанный тип можно получить аналогично, но обработав тип перед этим конструктором типа монады:
data IOList a = Fix (IO (ListLevel a))— конечно, на самом деле мы так получим
IO (IOList a) в смысле старого
IOList, но оно, наверно, и более правильно.
Остаётся думать, что
takeIol можно будет написать, используя свёртку, идущую в комплекте с
Fix. Так ли это?