Есть ли общий способ разложить свободную комонаду по монаде сбоя в "поток значений и окончательную ошибку"?
Cofree
comonad полезен для итерации частичных функций способом, который полиморфен для типа ошибки. это coiter
походит forM
-переключение в монаде ошибок, но она собирает полученные значения в чистом / ленивом виде, и вы видите только ошибку в конце, вниз в структуре данных.
Например, Cofree Identity
(ошибка не допускается!) является бесконечным потоком, тогда как Cofree Maybe
изоморфен NonEmpty
, а также Cofree (Either e) a
в основном (NonEmpty a, e)
(список значений успешных итераций плюс ошибка, возникающая в конце).
Теперь мне интересно, как лучше оценивать результаты без специального сопоставления с образцом в монаде с одной ошибкой. Извлечь все значения очень легко благодаря Foldable
экземпляр (например, toList
), но я не уверен, как лучше заполучить ошибки. Можно было бы эксплуатировать Foldable
для этого нужно просто избавиться от значений и оставить часть ошибки:
vals'n'err :: (Monad m, Foldable m)
=> Cofree m a -> (NonEmpty a, (m ()))
vals'n'err (a :< q) = case toList q of
[] -> (a:|[], const () <$> q)
l -> first (pure a<>)
$ foldr1 (\(bs,e) (cs,f) -> (bs<>cs, e>>f)) $ vals'n'err<$>l
но это кажется немного хакерским. Есть ли лучшее решение?
1 ответ
Я думаю, что это плохое преобразование для больших потоков, потому что вы можете иметь утечку пространства из-за лени. Но для небольших потоков это может быть полезным.
Мы можем разделить это преобразование на две части:
vals :: Foldable f => Cofree f a -> NonEmpty a
vals = NonEmpty.fromList . Foldable.toList
err :: Monad m => Cofree m a -> m b
err (_ :< m) = m >>= err
А затем объединить вместе:
vals'n'err :: (Monad m, Foldable m) => Cofree m a -> (NonEmpty a, m b)
vals'n'err w = (vals w, err w)