2013-04-22 15 views
10

Należy wziąć pod uwagę ten kod (pobrany z here i zmodyfikowany w celu użycia bajtów zamiast linii znaków).Jak używać IO z Scalaz7 Iteratees bez przepełnienia stosu?

Uruchomienie tego kodu w pliku o przyzwoitej wielkości (8kb) generuje wyjątek StackOverflowException. Okazało się, że wyjątku można uniknąć, używając monostaty Trampolina zamiast IO, ale to nie wydaje się być doskonałym rozwiązaniem - poświęcić funkcjonalną czystość, aby program w ogóle się zakończył. Oczywistym sposobem na naprawienie tego jest użycie IO lub Trampoline jako Monad Transformer, aby owijać drugi, ale nie mogę znaleźć implementacji wersji transformatora któregokolwiek z nich i nie jestem wystarczająco dobrym guru do programowania funkcjonalnego, aby wiem, jak napisać własne (więcej informacji na temat FP jest jednym z celów tego projektu, ale podejrzewam, że tworzenie nowych transformatorów monad jest obecnie nieco powyżej mojego poziomu). Przypuszczam, że mógłbym po prostu owijać dużą akcję IO wokół tworzenia, uruchamiania i zwracania wyniku moich iteracji, ale to wydaje się bardziej rozwiązaniem niż rozwiązaniem.

Przypuszczalnie niektóre monady nie mogą być konwertowane na monadowe transformatory, więc chciałbym wiedzieć, czy można pracować z dużymi plikami bez upuszczania IO lub przepełnienia stosu, a jeśli tak, to w jaki sposób?

Dodatkowe pytanie: Nie mogę wymyślić, że iteratee może zasygnalizować, że napotkało błąd podczas przetwarzania, z wyjątkiem tego, że zwróci je, co powoduje, że tworzenie ich jest trudniejsze. Powyższy kod pokazuje jak używać EitherT do obsługi błędów w module wyliczającym, ale jak to działa dla iteratorów?

+0

Może ci się przydać: http://termsandtruthconditions.herokuapp.com/blog/2013/03/16/free-monad/ – Impredicative

+0

To dobre wyjaśnienie, dlaczego potrzebuję używać trampoliny, aby uniknąć przepełnienia stosu, ale nie obejmuje to korzystania z IO i Trampoliny. – Redattack34

+0

IO jest już trampoliną. – Apocalisp

Odpowiedz

3

Po utworzeniu wyjątków i wydrukowaniu ich długości stosu w różnych miejscach kodu, poczułem, że Twój kod nie jest przepełniony. Wszystko wydaje się działać w stałym rozmiarze stosu. Więc szukałem innych miejsc. Ostatecznie skopiowałem implementację consume i dodałem trochę wydruków głębokości stosu i potwierdziłem, że przepełniła się tam.

Więc ta przelewa:

(I.consume[Int, Id, List] &= EnumeratorT.enumStream(Stream.fill(10000)(1))).run 

Ale, I wtedy okazało się, że tego nie robi:

(I.putStrTo[Int](System.out) &= EnumeratorT.enumStream(Stream.fill(10000)(1))) 
    .run.unsafePerformIO() 

putStrTo wykorzystuje foldM i jakoś nie powoduje przepełnienie. Zastanawiam się, czy można zaimplementować consume pod względem foldM. Właśnie skopiowałem kilka rzeczy z konsumpcji i poprawiłem je, dopóki nie skompilowałem:

def consume1[E, F[_]:Monad, A[_]:PlusEmpty:Applicative]: IterateeT[E, F, A[E]] = { 
    I.foldM[E, F, A[E]](PlusEmpty[A].empty){ (acc: A[E], e: E) => 
    (Applicative[A].point(e) <+> acc).point[F] 
    } 
} 

I zadziałało! Drukowanie długiej listy numerów int.

+0

Wygląda na to, że "konsumuje1" przepełnia Scalaz 7.0.3, przynajmniej dla mnie. Czy otrzymasz taki sam wynik, jeśli zwiększysz rozmiar strumienia? Próbuję wyśledzić [potencjalnie powiązany błąd] (https: // github.com/scalaz/scalaz/issues/554) - Zauważyłem, że dostaję przepełnienie stosu, jeśli uruchamiam w kontekście 'Id', podczas gdy dostaję błąd miejsca sterty, jeśli uruchomię' Trampoline'. W twoim przypadku błąd zniknął w trampolowanym kontekście, co prowadzi mnie do podejrzeń, że problemy mogą nie być ze sobą powiązane ... –

+0

@AaronNovstrup, nadal działa z wersją 100000 i skalą 7.0.3, więc może być Twój problem jest rzeczywiście inny. – huynhjl

+0

Dziwne. Widzę przepełnienie stosu z 'consume1' w konsoli Scala nawet dla stosunkowo małej liczby elementów (100), używając Scala 2.10.2, Scalaz 7.0.3, 64-bitowego serwera OpenJDK VM 1.7.0_25 i wielkość stosu 256k. –

Powiązane problemy