Haskell: Monadic takeWhile?
Mam kilka funkcji napisanych w C, które nazywam z Haskell. Te funkcje powracająIO (CInt)
. Czasami chcę uruchamiać wszystkie funkcje bez względu na to, co z nich powróci, a to jest łatwe. Ze względu na przykładowy kod jest to ogólna idea tego, co dzieje się obecnie:
Prelude> let f x = print x >> return x
Prelude> mapM_ f [0..5]
0
1
2
3
4
5
Prelude>
Dostaję pożądane efekty uboczne i nie dbam o wyniki. Ale teraz muszę przerwać wykonywanie natychmiast po pierwszym elemencie, który nie zwraca pożądanego rezultatu. Powiedzmy, że wartość powrotu 4 lub wyższa wymaga wykonania, aby zatrzymać - a następnie co jachcieć zrobić to:
Prelude> takeWhile (<4) $ mapM f [0..5]
Co daje mi ten błąd:
<interactive>:1:22: Couldn't match expected type `[b]' against inferred type `IO a' In the first argument of `mapM', namely `f' In the second argument of `($)', namely `mapM f ([0 .. 5])' In the expression: takeWhile (< 4) $ mapM f ([0 .. 5])
I to ma dla mnie sens - wynik jest nadal zawarty w monadzie IO i nie mogę po prostu porównać dwóch wartości zawartych w monadzie IO. Wiem, że to jest właśnie cel monad - łączenie wyników razem i odrzucanie operacji, gdy spełniony jest pewien warunek - ale czy w tym przypadku jest łatwy sposób na „zamknięcie” monady we / wy, aby przestać wykonywać łańcuch pod warunkiem według mojego wyboru, bez pisania wystąpieniaMonadPlus
?
Czy mogę po prostu „odsunąć” wartości odf
, w celu podjęcia, chociaż?
Czy to rozwiązanie pasuje do funktorów? Funktorzy jeszcze „nie kliknęli” ze mną, ale mam wrażenie, że to może być dobra sytuacja, by z nich skorzystać.
Aktualizacja:@sth ma najbliższą odpowiedź na to, czego chcę - w rzeczywistości jest to prawie dokładnie to, do czego dążyłem, ale nadal chciałbym sprawdzić, czy istniejestandard rozwiązanie, które nie jest wyraźnie rekurencyjne - w końcu to Haskell! Patrząc wstecz na to, jak sformułowałem moje pytanie, teraz widzę, że nie byłem wystarczająco jasny o moim pożądanym zachowaniu.
Thef
funkcja użyta powyżej dla przykładu była tylko przykładem. Prawdziwe funkcje są napisane w języku C i używane wyłącznie do ich efektów ubocznych. Nie mogę użyć sugestii @ TomamapM_ f (takeWhile (<4) [0..5])
ponieważ nie mam pojęcia, czy dane wejściowe naprawdę spowodują sukces lub niepowodzenie do czasu wykonania.
Tak naprawdę nie zależy mi na zwróconej liście - po prostu chcę wywołać funkcje C, dopóki lista nie zostanie wyczerpana lub pierwsza funkcja C nie zwróci kodu błędu.
W pseudokodzie w stylu C moim zachowaniem byłoby:
do {
result = function_with_side_effects(input_list[index++]);
} while (result == success && index < max_index);
Tak więc odpowiedź @ sth wykonuje dokładnie to, czego chcę, z wyjątkiem tego, że wyniki mogą (powinny?) Zostać odrzucone. ZAdropWhileM_
funkcja byłaby równoważna moim celom. Dlaczego nie ma takiej funkcji lubtakeWhileM_
w Control.Monad? Widzę, że byłopodobna dyskusja na liście mailingowej, ale wydaje się, że nic z tego nie wyszło.