Duży projekt w Haskell? [zamknięte]

zamknięte . To pytanie musi być bardziej skoncentrowane . Obecnie nie przyjmuje odpowiedzi. Zamknięty 4 lata temu . zamknięty. To pytanie i jego odpowiedzi są zamknięte , ponieważ pytanie jest off-topic, ale ma znaczenie historyczne. Obecnie nie przyjmuje nowych odpowiedzi ani interakcji.

Jaki jest dobry sposób na projektowanie / tworzenie dużych programów funkcjonalnych, szczególnie w Haskell?

Przejrzałem kilka tutoriali (napisz sobie schemat, który jest moim ulubionym, z prawdziwymi Świat Haskell blisko drugiego) - ale większość programów są stosunkowo małe, i jednofunkcyjne. Dodatkowo nie uważam niektórych z nich za szczególnie eleganckie(np. obszerne tabele wyszukiwania w WYAS).

Chcę teraz pisać większe programy, z bardziej ruchomymi częściami-pozyskiwanie danych z różnych źródeł, czyszczenie ich, przetwarzanie na różne sposoby, wyświetlanie ich w interfejsach użytkownika, utrzymywanie ich, komunikowanie się przez sieć itp. Jak można najlepiej struktura takiego kodu, aby był czytelny, możliwy do utrzymania i przystosowany do zmieniających się wymagań?

Istnieje dość duża literatura dotycząca tych pytań dla dużych, zorientowanych obiektowo programów imperatywnych. Pomysły takie jak MVC, wzorce projektowe itp. są przyzwoite recepty na realizację szerokich celów, takich jak oddzielenie obaw i wielokrotnego użytku w stylu OO. Dodatkowo, nowsze języki imperatywne nadają się do stylu refaktoryzacji "design as you grow", do którego, moim zdaniem początkujących, Haskell wydaje się mniej odpowiedni.

Czy istnieje równoważna Literatura dla Haskella? W jaki sposób zoo egzotycznych struktur sterowania jest dostępne w programowaniu funkcjonalnym(monady, strzałki, aplikacje itp.) najlepiej zatrudniony do tego celu? Jakie najlepsze praktyki możesz polecić?

Dzięki!

EDIT (jest to kontynuacja odpowiedzi Dona Stewarta):

@ dons wspomniał: "monady przechwytują kluczowe projekty architektoniczne w typach."

Moje pytanie brzmi: jak czy należy myśleć o kluczowych projektach architektonicznych w czystym funkcjonalnym języku?

Rozważmy przykład kilku strumieni danych i kilku etapów przetwarzania. Mogę pisać modularne parsery strumieni danych do zestawu struktur danych i mogę zaimplementować każdy etap przetwarzania jako czystą funkcję. Kroki przetwarzania wymagane dla jednego kawałka danych zależą od jego wartości i innych. Po niektórych krokach powinny pojawić się efekty uboczne, takie jak aktualizacje GUI lub baza danych zapytania.

Jaki jest 'właściwy' sposób, aby powiązać dane i kroki parsowania w przyjemny sposób? Można napisać dużą funkcję, która robi to dobrze dla różnych typów danych. Albo można użyć monad, aby śledzić to, co zostało przetworzone do tej pory i aby każdy etap przetwarzania dostawał to, czego potrzebuje od stanu monad. Albo można pisać w dużej mierze oddzielne programy i wysyłać wiadomości (nie bardzo mi się ta opcja podoba).

The slides he linked have a Things we Need "Idioms for mapping design on typy / funkcje/klasy / monady". Czym są idiomy? :)

Author: Dan, 2010-06-20

8 answers

Trochę o tym mówię wprojektowaniu dużych projektów w Haskell oraz w projektowaniu i implementacji XMonad. Inżynieria w dużych polega na zarządzaniu złożonością. Główne mechanizmy strukturyzacji kodu w Haskell do zarządzania złożonością to:

System typów

  • Użyj systemu typów do wymuszania abstrakcji, upraszczając interakcje.
  • wymuszanie niezmienników kluczy za pomocą typów
    • (np. że pewne wartości nie mogą escape some scope)
    • że pewien kod nie ma IO, nie dotyka dysku
  • egzekwuj bezpieczeństwo: zaznaczone wyjątki( może / albo), unikaj mieszania pojęć (słowo, Int, adres)
  • dobre struktury danych (jak suwaki) mogą sprawić, że niektóre klasy testowania będą zbędne, ponieważ wykluczają np. błędy poza granicami statycznie.

Profiler

  • dostarcz obiektywne dowody na stertę programu i profile czasowe.
  • profilowanie jest najlepszym sposobem na wyeliminowanie niepotrzebnego wykorzystania pamięci.

Czystość

  • radykalnie zmniejsz złożoność, usuwając stan. Czysto funkcjonalne skale kodu, ponieważ jest kompozycyjny. Wszystko, czego potrzebujesz, to typ, aby określić, jak użyć jakiegoś kodu - nie złamie się tajemniczo, gdy zmienisz inną część programu.
  • Użyj wielu stylów programowania" model/widok/kontroler": parsuj zewnętrzne dane tak szybko, jak to możliwe do czysto funkcjonalne struktury danych, działają na tych strukturach, a po zakończeniu całej pracy Renderuj/wypłucz / serializuj. Utrzymuje większość kodu w czystości

Testowanie

  • QuickCheck + Haskell pokrycie kodu, aby upewnić się, że testujesz rzeczy, których nie możesz sprawdzić za pomocą typów.
  • [13]}GHC + RTS jest świetny do sprawdzania, czy spędzasz zbyt dużo czasu na GC.
  • QuickCheck może również pomóc w identyfikacji czystych, ortogonalnych interfejsów API dla Twoich modułów. Jeśli właściwości Twojego kodu są trudne do określenia, prawdopodobnie są zbyt złożone. Kontynuuj refaktoryzację, dopóki nie masz czystego zestawu właściwości, które mogą przetestować twój Kod, które dobrze komponują. Więc kod jest prawdopodobnie dobrze zaprojektowany.

Monady do budowy

  • monady przechwytują kluczowe projekty architektoniczne w typach (ten kod ma dostęp do sprzętu, ten kod jest sesją dla jednego użytkownika itp.)
  • np. X monad w xmonadzie, oddaje dokładnie projekt tego, co stan jest widoczny dla jakich elementów systemu.

Klasy typów i typy egzystencjalne

  • użyj klas typów, aby zapewnić abstrakcję: Ukryj implementacje za interfejsami polimorficznymi.

Współbieżność i równoległość

  • wkradnij się par do swojego programu, aby pokonać konkurencję za pomocą łatwego, komponowalnego równoległości.

Refaktor

  • możesz refaktorować w Haskell dużo . Na typy zapewniają, że zmiany na dużą skalę będą bezpieczne, jeśli używasz typów mądrze. Pomoże to w skalowaniu bazy kodowej. Upewnij się, że refaktoring spowoduje błędy typu aż do zakończenia.

Użyj FFI mądrze

  • FFI ułatwia grę obcym kodem, ale ten zagraniczny kod może być niebezpieczny.
  • być bardzo ostrożnym w założeniach dotyczących kształtu zwracanych danych.

Programowanie Meta

  • trochę z szablonu Haskell lub generics można usunąć boilerplate.

Pakowanie i dystrybucja

  • Użyj Cabal. Nie zwijaj własnego systemu budowania. (EDIT: w rzeczywistości prawdopodobnie chcesz użyć Stack teraz na początek.).
  • użyj plamiaka dla dobrych dokumentów API
  • narzędzia takie jak graphmod mogą pokazywać struktury modułów.
  • polegaj na wersjach bibliotek i narzędzi platformy Haskell, jeśli to w ogóle możliwe. Jest to stabilna podstawa. (EDIT: w dzisiejszych czasach prawdopodobnie chcesz użyć stosu do uruchomienia stabilnej bazy.)

Ostrzeżenia

  • użyj -Wall, aby Twój kod był czysty od zapachów. Możesz również spojrzeć na Agda, Isabelle lub złapać, aby uzyskać więcej pewności. Aby sprawdzić podobne do lint, zobacz Wielki hlint , który zaproponuje ulepszenia.

Dzięki tym wszystkim narzędziom możesz zachować kontrolę nad złożonością, usuwając jak najwięcej interakcji między komponentami, jak to możliwe. Idealnie, masz bardzo dużą bazę czystego kodu, który jest naprawdę łatwy w utrzymaniu, ponieważ jest kompozycyjny. Nie zawsze jest to możliwe, ale warto do tego dążyć.

Ogólnie: rozpakuj jednostki logiczne systemu na najmniejsze możliwe przezroczyste komponenty, a następnie zaimplementuj je w Moduły. Globalne lub lokalne środowiska dla zestawów komponentów (lub komponentów wewnętrznych) mogą być mapowane na monady. Użyj danych algebraicznych typy opisujące podstawowe struktury danych. Podziel się tymi definicjami.

 517
Author: Don Stewart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-06 15:42:26

Don dał ci większość powyższych szczegółów, ale oto moje dwa centy od robienia naprawdę nitty-gritty stateful programów, takich jak demony systemowe w Haskell.

  1. W końcu żyjesz w stosie transformatorów monad. Na dole jest IO. Powyżej, każdy główny moduł (w sensie abstrakcyjnym, a nie w sensie modułu w pliku) mapuje swój niezbędny stan do warstwy w tym stosie. Więc jeśli masz kod połączenia z bazą danych ukryty w module, piszesz to wszystko, aby być nad typem Monadreader Connection m=>... - >m ... a wtedy funkcje bazy danych zawsze mogą uzyskać połączenie bez funkcji z innych modułów, które muszą być świadome jego istnienia. Możesz skończyć z jedną warstwą zawierającą połączenie z bazą danych, inną konfigurację, trzecią różne semafory i mvary do rozdzielczości równoległości i synchronizacji, inną obsługę plików dziennika itp.

  2. Najpierw Oblicz obsługę błędów . The greatest słabością w tej chwili dla Haskell w większych systemach jest mnóstwo metod obsługi błędów, w tym nędznych, takich jak Maybe (co jest złe, ponieważ nie możesz zwrócić żadnych informacji o tym, co poszło nie tak; Zawsze używaj albo zamiast Maybe, chyba że naprawdę masz na myśli brakujące wartości). Najpierw dowiedz się, jak to zrobić, i skonfiguruj Adaptery z różnych mechanizmów obsługi błędów używanych w bibliotekach i innych kodach do ostatecznego. To uratuje ci świat smutku później.

Dodatek (wydobyty z komentarzy; dzięki Lii & liminalisht) -
więcej dyskusji na temat różnych sposobów cięcia dużego programu na monady w stosie:

Ben Kolera daje wielkie praktyczne wprowadzenie do tego tematu, a Brian Hurt omawia rozwiązania problemu lift ing monadic działań w niestandardowych monad. George Wilson pokazuje jak używać mtl do pisania kodu, który działa z dowolnym monad, który implementuje wymagane typeklasy, a nie Niestandardowy rodzaj monad. Carlo Hamalainen napisał kilka krótkich, przydatnych notatek podsumowujących przemówienie George ' a.

 118
Author: user349653,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-02 14:00:24

Projektowanie dużych programów w Haskell nie różni się tak bardzo od robienia tego w innych językach. Programowanie w dużym stopniu polega na rozbiciu problemu na łatwe do opanowania kawałki i dopasowaniu ich do siebie; język implementacji jest mniej ważny.

To powiedziawszy, w dużym projekcie miło jest spróbować i wykorzystać system typów, aby upewnić się, że możesz dopasować swoje elementy do siebie tylko w sposób, który jest poprawny. Może to obejmować typy newtype lub phantom, aby rzeczy, które wydają się niech ten sam typ będzie inny.

Jeśli chodzi o refaktoryzację kodu, czystość jest wielkim dobrodziejstwem, więc staraj się utrzymać jak najwięcej kodu w czystości. Czysty kod jest łatwy do refaktoryzacji, ponieważ nie ma ukrytej interakcji z innymi częściami programu.

 43
Author: augustss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-20 09:29:46

Nauczyłem się strukturalnego programowania funkcyjnego za pierwszym razem z tą książką . Może to nie być dokładnie to, czego szukasz, ale dla początkujących w programowaniu funkcyjnym może to być jeden z najlepszych pierwszych kroków, aby nauczyć się struktury programów funkcjonalnych-niezależnie od skali. Na wszystkich poziomach abstrakcji projekt powinien zawsze mieć wyraźnie ułożone struktury.

Rzemiosło funkcjonalne Programowanie

Rzemiosło programowania funkcyjnego

Http://www.cs.kent.ac.uk/people/staff/sjt/craft2e/

 16
Author: comonad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-11 09:35:59

Obecnie piszę książkę o tytule "Functional Design and Architecture". Zawiera kompletny zestaw technik, jak zbudować dużą aplikację przy użyciu czystego podejścia funkcjonalnego. Opisuje wiele wzorców funkcjonalnych i pomysłów podczas budowania aplikacji podobnej do SCADA "Andromeda" do sterowania statkami kosmicznymi od podstaw. Moim głównym językiem jest Haskell. Okładka książki:

  • podejścia do modelowania architektury za pomocą diagramów;
  • Wymagania analiza;
  • Embedded DSL domain modelling;
  • zewnętrzny projekt i wykonanie DSL;
  • monady jako podsystemy z efektami;
  • darmowe monady jako funkcjonalne interfejsy;
  • arrowised eDSLs;
  • odwrócenie sterowania za pomocą darmowych monadycznych edsl;
  • Pamięć Transakcyjna Oprogramowania;
  • soczewki;
  • State, Reader, Writer, RWS, ST monads;
  • Stan nieczysty: IORef, MVar, STM;
  • wielowątkowość i współbieżność domeny modelowanie;
  • GUI;
  • zastosowanie technik i podejść głównego nurtu, takich jak UML, SOLID, GRASP;
  • Interakcja z nieczystymi podsystemami.

Możesz zapoznać się z kodem do książki tutaj {[38] } oraz kodem projektu'Andromeda' .

Spodziewam się skończyć tę książkę pod koniec 2017 roku. Do tego czasu możecie przeczytać mój artykuł" Design and Architecture in Functional Programming " (Rus) tutaj .

UPDATE

Udostępniłem moją książkę online (pierwsze 5 rozdziałów). Zobacz post na Reddit

 11
Author: graninas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-22 01:10:53

Gabriel ' s blog post skalowalne architektury programów może być warta wzmianki.

Wzorce projektowe Haskell różnią się od wzorców projektowych głównego nurtu w jednym ważny sposób:

  • Architektura konwencjonalna : połączenie kilku elementów Typ A do generowania "sieci" lub "topologii" typu B

  • Haskell architecture : połącz ze sobą kilka elementów typu A, aby wygenerować nowy komponent tego samego typu A, nie do odróżnienia w znak z jego części zastępczych

Uderza mnie często, że pozornie elegancka architektura często wypada z bibliotek, które wykazują to ładne poczucie jednorodności, w oddolny sposób. W Haskell jest to szczególnie widoczne-wzorce, które tradycyjnie byłyby uważane za "architekturę odgórną", zwykle są przechwytywane w bibliotekach takich jak mvc[23]}, Netwire i Cloud Haskell . To znaczy, mam nadzieję, że ta odpowiedź nie będzie interpretowana jako próba zastąpienia któregokolwiek z pozostałych w tym wątku, tylko że wybory strukturalne mogą i powinny być idealnie abstrakcyjne w bibliotekach przez ekspertów domeny. Prawdziwą trudnością w budowaniu dużych systemów, moim zdaniem, jest ocena tych bibliotek pod kątem ich architektonicznej "dobroci" wobec wszystkich Twoich pragmatycznych obaw.

Jako liminalisht wspomina w komentarzach, Kategoria design pattern jest kolejnym post by Gabriel on the topic, in a similar vein.

 7
Author: Rehno Lindeque,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-19 08:37:05

I have found the paper "Teaching Software Architecture Using Haskell " (pdf) by Alejandro Serrano przydatne do myślenia o wielkoskalowej strukturze w Haskell.

 5
Author: haroldcarr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 21:01:46

Być może trzeba cofnąć się o krok i pomyśleć, jak przetłumaczyć opis problemu na projekt w pierwszej kolejności. Ponieważ Haskell jest tak wysoki poziom, może uchwycić opis problemu w postaci struktur danych, działań jako procedur i czystej transformacji jako funkcji. Więc masz projekt. Rozwój rozpoczyna się, gdy skompilujesz ten kod i znajdziesz konkretne błędy dotyczące brakujących pól, brakujących instancji i brakujących transformatorów monadycznych w Twoim Kod, ponieważ na przykład wykonujesz dostęp do bazy danych z biblioteki, która wymaga pewnego stanu w ramach procedury IO. I voila, jest program. Kompilator karmić swoje szkice psychiczne i daje spójność do projektowania i rozwoju.

W ten sposób od samego początku korzystasz z pomocy Haskella, a kodowanie jest naturalne. Nie chciałbym robić czegoś "funkcjonalnego" lub "czystego" lub wystarczająco ogólnego, jeśli to, co masz na myśli, jest konkretnym zwykłym problemem. I myślę, że nadinżynieria jest w tym najniebezpieczniejszą rzeczą. Rzeczy są inne, gdy problemem jest stworzenie biblioteki, która abstrakcyjna zestaw powiązanych problemów.

 3
Author: agocorona,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-30 09:13:32