Zdaję sobie sprawę, że spóźniam się na przyjęcie, ale masz tutaj dwie teoretyczne odpowiedzi i chciałem zapewnić praktyczną alternatywę do przeżuwania. Dochodzę do tego jako krewny Haskell noob, który mimo to został ostatnio wymuszony przez temat Arrows do projektu, nad którym aktualnie pracuję.
Po pierwsze, możesz produktywnie rozwiązać większość problemów w Haskell bez sięgania po strzały. Niektórzy znani Haskellerzy naprawdę ich nie lubią i nie używają ich (zobacz tutaj , tutaj i tutaj, aby uzyskać więcej na ten temat). Więc jeśli mówisz do siebie: „Hej, nie potrzebuję ich”, zrozum, że naprawdę możesz mieć rację.
To, co najbardziej frustrowało mnie w Arrows, kiedy po raz pierwszy się nauczyłem, to sposób, w jaki samouczki na ten temat nieuchronnie sięgały po analogię obwodów. Jeśli spojrzysz na kod strzałki - przynajmniej odmiany cukrowej - nie przypomina on nic więcej niż język definicji sprzętu. Twoje wejścia ustawiają się po prawej stronie, twoje wyjścia po lewej, a jeśli nie uda ci się poprawnie je wszystkie połączyć, po prostu nie będą strzelać. Pomyślałem sobie: naprawdę? Czy to tam skończyliśmy? Czy stworzyliśmy język na tak wysokim poziomie, że znów składa się z drutów miedzianych i lutu?
Prawidłowa odpowiedź na to pytanie, o ile udało mi się ustalić, brzmi: Właściwie tak. Przypadkiem użycia zabójców w Arrows jest teraz FRP (pomyśl Yampa, gry, muzyka i systemy reaktywne w ogóle). Problem, przed którym stoi FRP, jest w dużej mierze tym samym problemem, z którym borykają się wszystkie inne synchroniczne systemy przesyłania wiadomości: jak połączyć ciągły strumień danych wejściowych w ciągły strumień danych wyjściowych bez upuszczania odpowiednich informacji lub powodowania przecieków. Możesz modelować strumienie jako listy - kilka najnowszych systemów FRP stosuje to podejście - ale gdy masz dużo danych wejściowych, zarządzanie listami staje się prawie niemożliwe. Musisz odizolować się od prądu.
Strzały dopuszczają w systemach FRP składanie funkcji w sieć, przy jednoczesnym całkowitym oderwaniu wszelkich odniesień do podstawowych wartości przekazywanych przez te funkcje. Jeśli dopiero zaczynasz przygodę z FP, może to być na początku mylące, a następnie oszałamiające, gdy wchłonąłeś jego implikacje. Dopiero niedawno zrozumiałeś, że funkcje można wyodrębnić i jak rozumieć listę taką [(*), (+), (-)]
jak typ [(a -> a -> a)]
. Za pomocą strzałek możesz przesuwać abstrakcję o jedną warstwę dalej.
Ta dodatkowa umiejętność abstrakcji niesie ze sobą własne niebezpieczeństwa. Po pierwsze, może popchnąć GHC do narożnych przypadków, w których nie wie, co zrobić z założeniami typu. Musisz być przygotowany do myślenia na poziomie typu - jest to doskonała okazja do zapoznania się z rodzajami i typami RankNTyp oraz innymi podobnymi tematami.
Istnieje również wiele przykładów tego, co nazwałbym „głupimi sztuczkami ze strzałami”, w których programista sięga po kombinator strzał, tylko dlatego, że chce popisać się zgrabną sztuczką z krotkami. (Oto mój własny trywialny wkład w szaleństwo .) Zapraszam do ignorowania takich upartych pechów, gdy natkniesz się na nie na wolności.
UWAGA: Jak wspomniałem powyżej, jestem względnym noobem. Jeśli ujawniłem powyżej jakiekolwiek nieporozumienia, proszę o poprawienie mnie.