Dlaczego funkcja sekwencji Haskella nie może być leniwa lub dlaczego rekurencyjne funkcje monadyczne nie mogą być leniwe
Z pytaniemWyświetlanie całej zawartości katalogu według pierwszego rzędu skutkuje niską wydajnościąDowiedziałem się, że niska wydajność wynika z dziwnego zachowania rekurencyjnych funkcji monad.
Próbować
sequence $ map return [1..]::[[Int]]
sequence $ map return [1..]::Maybe [Int]
a ghci popadnie w niekończące się obliczenia.
Jeśli przepisamy funkcję sekwencji w bardziej czytelnej formie, jak poniżej:
sequence' [] = return []
sequence' (m:ms) = do {x<-m; xs<-sequence' ms; return (x:xs)}
i próbuj:
sequence' $ map return [1..]::[[Int]]
sequence' $ map return [1..]::Maybe [Int]
mamy taką samą sytuację, nieskończoną pętlę.
Wypróbuj skończoną listę
sequence' $ map return [1..]::Maybe [Int]
wyłoni oczekiwany wynikJust [1,2,3,4..]
po długim oczekiwaniu.
Z tego, co próbowaliśmy, możemy dojść do wniosku, że chociaż definicja sekwencji „wydaje się być leniwa, jest ścisła i musi zawierać wszystkie liczby, zanim wynik sekwencji” może zostać wydrukowany.
Nie tylko sekwencja, jeśli zdefiniujemy funkcję
iterateM:: Monad m => (a -> m a) -> a -> m [a]
iterateM f x = (f x) >>= iterateM0 f >>= return.(x:)
i próbuj
iterateM (>>=(+1)) 0
wtedy niekończące się obliczenia.
Jak wszyscy wiemy, nie-monadyczna iteracja jest zdefiniowana tak samo jak powyższa iteracjaM, ale dlaczego iteracja jest leniwa i iteracjaM jest ścisła. Jak widać z powyższego, zarówno iteracja M, jak i sekwencja są rekurencyjnymi funkcjami monadycznymi. Czy jest coś dziwnego w rekurencyjnych funkcjach monadycznych