Is there a general way to decompose a free command over a monad of failures into a “stream of values ​​and a final error”?

Cofree comonad is useful for iterating over partial functions in a way that is error polymorphic. Its coiter resembles forM -looping in monad errors, but it collects the received values ​​in a clean / lazy way, and you only see the error at the end, down in the data structure.

For example, Cofree Identity (no failure allowed!) Is an endless stream, while Cofree Maybe is isomorphic to NonEmpty , and Cofree (Either e) a mostly (NonEmpty a, e) (list of successful iterative values ​​plus the error that occurs at the end).

Now I'm wondering what is the best way to evaluate the results, without specifically matching patterns on a single error monad. Retrieving all values ​​is very easy thanks to the Foldable instance (like toList ), but I'm not sure how best to get errors. You could use Foldable to do this, just to get rid of the values ​​and leave some of the error:

 vals'n'err :: (Monad m, Foldable m) => Cofree ma -> (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 

but it's a bit of hacks. Is there a better solution?

+6
source share
1 answer

I think this is a bad transformation for large threads, because you might have a space leak on laziness. But for small threads this can be useful.

We can divide this transformation into two:

 vals :: Foldable f => Cofree fa -> NonEmpty a vals = NonEmpty.fromList . Foldable.toList err :: Monad m => Cofree ma -> mb err (_ :< m) = m >>= err 

And then combine together:

 vals'n'err :: (Monad m, Foldable m) => Cofree ma -> (NonEmpty a, mb) vals'n'err w = (vals w, err w) 
+4
source

Source: https://habr.com/ru/post/1013422/


All Articles