2016-12-22 44 views
6

Komonadka Cofree jest przydatna do iterowania funkcji częściowych w sposób polimorficzny dla typu błędu. Jego coiter przypomina forM -zachowanie w monadzie błędu, ale zbiera wygenerowane wartości w sposób czysty/leniwy i widzisz tylko błąd na końcu, w strukturze danych.Czy istnieje ogólny sposób na dekomponowanie darmowego comonada przez monadę awarii w "strumieniu wartości i ostatecznym błędzie"?

Na przykład Cofree Identity (bez awarii dozwolone!) Jest nieskończony strumień, natomiast Cofree Maybe jest izomorficzna NonEmpty i Cofree (Either e) a jest w zasadzie (NonEmpty a, e) (lista wartości udany-iteracji plus błąd, który pojawia się na końcu).

Teraz zastanawiam się, jaki jest najlepszy sposób oceny wyników, bez konkretnego dopasowania wzorców w monadach pojedynczego błędu. Wyodrębnianie wszystkich wartości jest bardzo łatwe dzięki instancji Foldable (np.), ale nie jestem pewien, jak najlepiej uchwycić błędy . Byłoby to możliwe do wykorzystania Foldable na to, aby po prostu pozbyć z wartości i pozostawić część błędzie:

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 

ale czuje się trochę hackish. Czy istnieje lepsze rozwiązanie?

+0

Od konstruktywny POV uzyskiwanie '(niepusty A, E)' z 'Cofree (albo E) a 'nie ma dla mnie większego sensu, ponieważ' Cofree' jest typem koincypcyjnym i całkiem dobrze jest, gdy 'Cofree (Eide e) a' jest nieskończonym strumieniem przeplatanym przez' Right's, ale obiecujesz zawsze zwróć 'e'. Nic dziwnego, że "vals'n'err" wydaje się hackish. – user3237465

+0

@ user3237465: yeah. Więc jaki podpis byś zaproponował? – leftaroundabout

+0

Nie widzę problemu do rozwiązania tutaj. Zużywanie "Cofree (e e) a" w streamingu i zatrzymywanie się na "Left" jest powszechnym i rozsądnym sposobem, dlaczego warto ryzykować, aby uzyskać nieprzeniknioną przeciekającą dno typu 'e'? – user3237465

Odpowiedz

4

Uważam, że jest to zła transformacja dla dużych strumieni, ponieważ można leczyć przestrzeń dzięki lenistwu. Ale w przypadku małych strumieni może być użyteczny.

Możemy podzielić tej transformacji na dwie części:

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 

a następnie połączyć ze sobą:

vals'n'err :: (Monad m, Foldable m) => Cofree m a -> (NonEmpty a, m b) 
vals'n'err w = (vals w, err w) 
+0

Tak, zrobiłem. Dzięki! – freestyle

+0

'vals' powinien z pewnością użyć' foldMap1'. – dfeuer

+0

@dfeuer Może, ale instancja 'Foldable1 (Cofree f)' ma ograniczenie 'Foldable1 f'. Spowoduje to zawężenie zestawu możliwych typów. Z drugiej strony nie rozumiem takiego ograniczenia, dlaczego nie "instancja Foldable f => Foldable1 (Cofree f)'. – freestyle