Czy funkcje Haskella można udowodnić/sprawdzić / zweryfikować z poprawnością właściwości?

Kontynuując od idei w: Czy istnieją jakieś udowodnione języki świata rzeczywistego?

Nie wiem jak wy, ale mam dość pisania kodu, którego nie mogę zagwarantować.

Po zadaniu powyższego pytania i uzyskaniu fenomenalnej odpowiedzi (dzięki wszystkim!) Postanowiłem zawęzić moje poszukiwania do udowodnionego, pragmatycznego podejścia do Haskell . Wybrałem Haskell, ponieważ jest to rzeczywiście przydatne (istnieje Wiele web Framework napisany dla niego, wydaje się dobrym benchmarkiem) i myślę, że jest wystarczająco ścisły, funkcjonalnie , że może być udowodniony, lub przynajmniej pozwolić na testowanie niezmienników.

Oto czego chcę (i nie byłem w stanie znaleźć)

Chcę mieć framework, który może spojrzeć na funkcję Haskella, dodać, napisane w psudocode:

add(a, b):
    return a + b

- i sprawdzić, czy pewne niezmienniki utrzymują się nad każdym stanem wykonania. Wolałbym jednak jakiś dowód formalny Zadowoliłbym się czymś takim jak sprawdzanie modeli.
W tym przykładzie niezmiennikiem będzie to, że podane wartości a i b , wartością zwracaną jest zawsze suma A+b .

Jest to prosty przykład, ale nie wydaje mi się, aby taki framework istniał. Z pewnością byłaby górna granica złożoności funkcji, która mogłaby być testowana (10 wejść ciągów do funkcji z pewnością zajęłoby dużo czasu!) ale to zachęci bardziej staranne projektowanie funkcji i nie różni się niczym od stosowania innych metod formalnych. Wyobraź sobie, że używasz Z lub B, kiedy definiujesz zmienne/zbiory, upewniasz się, że dajesz zmienne najmniejsze możliwe zakresy. Jeśli twój INT nigdy nie będzie powyżej 100, upewnij się, że zainicjowałeś go jako taki! Techniki takie jak te, i właściwego rozkładu problemu powinny - myślę-pozwalają na zadowalające sprawdzenie czysto funkcjonalnego języka jak Haskell.

Nie jestem-jeszcze-bardzo-doświadczony metodami formalnymi lub Haskell. Daj mi znać, czy mój pomysł jest dźwięczny, a może uważasz, że haskell nie pasuje? Jeśli sugerujesz inny język, upewnij się, że przeszedł test "has-a-web-framework" i przeczytaj oryginalne pytanie :-)

Author: Community, 2010-11-02

11 answers

Cóż, kilka rzeczy na początek, skoro jedziesz szlakiem Haskell: {]}

  • Znasz korespondencję Curry-Howarda? Istnieją systemy sprawdzane maszynowo oparte na tym, które są pod wieloma względami po prostu funkcyjnymi językami programowania o bardzo potężnych systemach typów.

  • Czy znasz obszary abstrakcyjnej matematyki, które dostarczają użytecznych pojęć do analizy kodu Haskella? Różne smaki algebra i trochę teorii kategorii.

  • Należy pamiętać, że Haskell, podobnie jak wszystkie języki Turinga-kompletne, zawsze ma możliwość nieterminacji. Ogólnie rzecz biorąc, o wiele trudniej jest udowodnić, że coś będzie zawsze było prawdą, niż udowodnić, że albo coś będzie prawdą, albo będzie zależeć od wartości nieterminalnej.

Jeśli naprawdę szukasz dowodu, a nie tylko testów , są to takie o rzeczach, o których należy pamiętać. Podstawowa zasada jest taka: Make invalid states cause compiler errors. W pierwszej kolejności zapobiegaj zakodowaniu nieprawidłowych danych, a następnie pozwól, aby kontroler typu wykonał za Ciebie żmudną część.

Jeśli chcesz pójść jeszcze dalej, jeśli pamięć mnie nie myli, asystent proof Coq posiada funkcję "extract to Haskell", która pozwoli Ci udowodnić dowolne właściwości dotyczące funkcji krytycznych, a następnie przekształcić dowody w kod Haskell.

Do wykonywania fantazyjnego systemu typu wszystko bezpośrednio w Haskell, Oleg Kiselyov jest wielkim mistrzem. Na jego stronie można znaleźć przykłady fajnych trików, takich jak typy polimorficzne wyższej rangi do kodowania statycznych dowodów sprawdzania granic tablic.

Dla bardziej lekkich rzeczy, można zrobić rzeczy, takie jak za pomocą certyfikatu poziomu typu , aby oznaczyć kawałek danych jako sprawdzony pod kątem poprawności. Nadal jesteś sam do sprawdzenia poprawności, ale inny kod może przynajmniej polegać na tym, że niektóre dane zostały sprawdzone.

Kolejnym krokiem, który możesz wykonać, opierając się na lekkiej weryfikacji i fantazyjnych sztuczkach systemowych, jest wykorzystanie faktu, że Haskell działa dobrze jako język hosta do osadzania języków specyficznych dla domeny ; najpierw skonstruuj starannie Ograniczony podjęzyk (najlepiej taki, który nie jest Turing-complete), o którym możesz łatwiej udowodnić użyteczne właściwości, a następnie użyj programów w tym DSL, aby zapewnić kluczowe elementy podstawowej funkcjonalności w Twoim komputerze. ogólny program. Na przykład, można udowodnić, że funkcja dwuargumentowa jest asocjacyjna w celu uzasadnienia równoległej redukcji zbioru elementów za pomocą tej funkcji (ponieważ kolejność aplikacji funkcji nie ma znaczenia, tylko kolejność argumentów).


Jeszcze jedno. Kilka rad na temat unikania pułapek, które zawiera Haskell, które mogą sabotować kod, który w przeciwnym razie byłby bezpieczny-by-construction: twoi zaprzysiężeni wrogowie tutaj są {58]}ogólne recursion , the IO monad , And partial functions :
  • Tego ostatniego jest stosunkowo łatwo uniknąć: nie pisz ich i nie używaj ich. Upewnij się, że każdy zestaw wzorców pasuje do każdego możliwego przypadku i nigdy nie używaj error Ani undefined. Jedyną trudną częścią jest unikanie standardowych funkcji bibliotecznych, które mogą powodować błędy. Niektóre są oczywiście niebezpieczne, jak fromJust :: Maybe a -> a lub head :: [a] -> a, ale inne mogą być bardziej subtelne. Jeśli znajdziesz się pisząc funkcję, która naprawdę, naprawdę nie można nic zrobić z niektórymi wartościami wejściowymi, wtedy zezwalasz na zakodowanie nieprawidłowych stanów przez typ wejścia i musisz to najpierw naprawić.

  • Drugi jest łatwy do uniknięcia na poziomie powierzchniowym przez rozpraszanie rzeczy za pomocą różnych czystych funkcji, które są następnie używane z wyrażenia IO. Lepiej jest, w miarę możliwości, przenieść cały program do czystego kodu, tak aby mógł być oceniany niezależnie ze wszystkim, oprócz rzeczywistego I / O. to głównie staje się trudne tylko wtedy, gdy potrzebujesz rekurencji, która jest napędzana przez zewnętrzne wejście, co prowadzi mnie do ostatniego elementu:

  • Słowa dla mądrych: dobrze ugruntowana rekurencja i produktywne corecursion . Zawsze upewnij się, że funkcje rekurencyjne albo przechodzą z jakiegoś punktu początkowego do znanego przypadku podstawowego, albo generują szereg elementów na żądanie. W czystym kodzie najprostszym sposobem na to jest rekurencyjnie zwijanie skończonej struktury danych (np. zamiast funkcji wywołującej się bezpośrednio podczas zwiększania licznika do pewnego maksimum, Utwórz listę zawierającą zakres wartości licznika i złóż go) lub rekurencyjnie generując leniwą strukturę danych( np. listę progresywnych przybliżeń do pewnej wartości), podczas gdy ostrożnie nigdy nie mieszaj tych dwóch bezpośrednio (np. nie po prostu "znajdź pierwszy element w strumieniu spełniający pewien warunek"; może nie istnieć. Zamiast tego pobieraj wartości ze strumienia do maksymalnej głębokości, a następnie przeszukuj skończoną listę, Postępowanie w przypadku nie odnalezionym).

  • Łącząc dwa ostatnie elementy, dla części, w których naprawdę potrzebujesz IO z ogólną rekurencją, spróbuj zbudować program jako komponenty przyrostowe, a następnie skondensować wszystkie niezręczne bity w jedną funkcję "sterownika". Na przykład, możesz napisać pętlę zdarzeń GUI z czystą funkcją, taką jak mainLoop :: UIState -> Events -> UIState, test wyjścia, taki jak quitMessage :: Events -> Bool, funkcja pobierania oczekujących zdarzeń getEvents :: IO Events i funkcja aktualizacji updateUI :: UIState -> IO (), a następnie uruchomić rzecz z funkcja uogólniona jak runLoopIO :: (b -> a -> b) -> b -> IO a -> (b -> IO ()) -> IO (). Dzięki temu skomplikowane części są naprawdę czyste, co pozwala uruchomić cały program za pomocą skryptu zdarzenia i sprawdzić Wynikowy stan interfejsu użytkownika, jednocześnie izolując niewygodne rekurencyjne części We / Wy w jedną, abstrakcyjną funkcję, która jest łatwa do zrozumienia i często jest nieuchronnie poprawna przez parametryczność {18]}.

 62
Author: C. A. McCann,
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-11-02 15:00:57

Prawdopodobnie najbliższą rzeczą do tego, o co prosisz jest Haskabelle, narzędzie, które jest dostarczane z asystentem dowodu Isabelle, które może przetłumaczyć pliki Haskella na teorie Isabelle i pozwala udowodnić rzeczy na ich temat. O ile rozumiem, to narzędzie jest rozwijane w ramach projektu hol-ML-Haskell i strona dokumentacji zawiera pewne informacje o teorii stojącej za tym.

Nie jestem zbytnio zaznajomiony z tym projektem i niewiele wiem o tym, co z nim zrobiono. Ale wiem, że Brian Huffman bawił się tymi rzeczami, sprawdzał jego dokumenty i rozmowy, powinny zawierać istotne rzeczy.

 21
Author: svenningsson,
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-10-04 05:45:45

Nie jestem pewien, czy to, o co prosisz, jest tym, co cię uszczęśliwi. :-)

Sprawdzanie modelu w języku ogólnego przeznaczenia jest prawie niemożliwe, ponieważ modele muszą być specyficzne dla danej dziedziny, aby były praktyczne. Ze względu na twierdzenie Gödla o niekompletności nie ma po prostu metody automatycznego znajdowania dowodów w wystarczająco ekspresyjnej logice.

Oznacza to, że musisz samemu napisać dowody , co rodzi pytanie, czy wysiłek jest wart czasu wydane. Oczywiście wysiłek tworzy coś bardzo cennego, a mianowicie pewność, że twój program jest poprawny. Pytanie nie brzmi, czy jest to must-have, ale czy czas spędzony jest zbyt duży koszt. Rzecz w dowodach polega na tym, że chociaż możesz mieć "intuicyjne" zrozumienie, że twój program jest poprawny , często bardzo trudno jest sformalizować to zrozumienie jako dowód. Problem z intuicyjnym zrozumieniem polega na tym, że jest bardzo podatny na przypadkowe błędy (literówki i inne głupie błędy). Jest to podstawowy dylemat pisania poprawnych programów.

Badania poprawności programu polegają więc na ułatwieniu sformalizowania dowodów i automatycznego sprawdzania ich poprawności. Programowanie jest integralną częścią "łatwości formalizacji"; bardzo ważne jest, aby pisać programy w stylu, który jest łatwy do rozumowania. Obecnie mamy następujące spektrum:

  • Język imperatywny jak C, C++, Fortran, Python: bardzo trudno tu cokolwiek sformalizować. Testy jednostkowe i ogólne rozumowanie to jedyny sposób na uzyskanie przynajmniej pewnej pewności. Statyczne typowanie łapie tylko trywialne błędy (co znacznie lepsze niż ich nie łapanie!).

  • Języki czysto funkcjonalne, takie jak Haskell, ML: Expressive type system pomaga złapać nietrywialne błędy i błędy. Ręczne udowodnienie poprawności jest praktyczne dla fragmentów do około 200 linii, powiedziałbym. (Zrobiłem dowód dla mojego pakiet operacyjny , na przykład.) Quickcheck testowanie jest tanim substytutem sformalizowanych dowodów.

  • Zależne typy języków i asystentów sprawdzających, takich jak Agda, Epigram, Coq: udowodnienie poprawności całych programów jest możliwe dzięki automatycznej pomocy w formalizacji i wykrywaniu dowodów. Jednak obciążenie jest nadal wysokie.

Moim zdaniem aktualnym slodkim plusem pisania poprawnych programów jest programowanie czysto funkcjonalne . Jeśli życie zależy od poprawności programu, lepiej przejść poziom wyżej i użyć Asystenta dowodu.

 19
Author: Heinrich Apfelmus,
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-11-02 14:55:36
 5
Author: sclv,
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-11-02 13:39:58

Widziałeś quickcheck? Może zaoferować niektóre z rzeczy, których potrzebujesz.

Http://www.haskell.org/haskellwiki/Introduction_to_QuickCheck

 4
Author: Jonno_FTW,
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-11-02 13:21:56

Twój pozornie prosty przykład, add (a, b), jest naprawdę trudny do zweryfikowania - zmiennoprzecinkowy, overflow, underflow, interrupts, czy kompilator jest zweryfikowany, czy sprzęt jest zweryfikowany, itp.

Habit jest uproszczonym dialektem Haskell, który pozwala na udowodnienie właściwości programu.

Hume jest językiem z 5 poziomami, z których każdy jest bardziej ograniczony, a więc łatwiejszy do weryfikacji:

Full Hume
  Full recursion
PR−Hume
  Primitive Recursive functions
Template−Hume
  Predefined higher−order functions
  Inductive data structures
  Inductive  Non−recursive first−order functions
FSM−Hume
  Non−recursive data structures
HW−Hume
  No functions
  Non−recursive data structures

Oczywiście najpopularniejszą obecnie metodą sprawdzania właściwości programu jest jednostka testowanie, które dostarcza silnych twierdzeń, ale te twierdzenia są zbyt specyficzne. "typy uważane za szkodliwe", Pierce, slide 66

 3
Author: ja.,
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-11-03 13:32:56

Spójrz na Zeno. Cytowanie strony wiki:

Zeno jest zautomatyzowanym systemem sprawdzającym Właściwości programu Haskell; opracowany w Imperial College London przez Williama Sonnexa, Sophię Drossopoulou i Susan Eisenbach. Jego celem jest rozwiązanie ogólnego problemu równości dwóch terminów Haskella dla dowolnej wartości wejściowej.

Wiele dostępnych obecnie narzędzi do weryfikacji programów ma odmianę sprawdzania modeli; jest w stanie przemierzyć bardzo dużą, ale skończoną przestrzeń wyszukiwania szybko. Są one dobrze dostosowane do problemów z dużym opisem, ale bez rekurencyjnych typów danych. Zeno z drugiej strony jest zaprojektowany do indukcyjnego wykazywania właściwości w nieskończonej przestrzeni wyszukiwania, ale tylko tych z małą i prostą specyfikacją.

 3
Author: Petr,
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
2012-10-17 16:45:58

Z pewnością można formalnie udowodnić Niektóre właściwości programów Haskell. Musiałem to zrobić na egzaminie FP: podając dwa wyrażenia, udowodnij, że oznaczają tę samą funkcję. Nie jest to możliwe w ogóle, ponieważ Haskell jest Turing-complete, więc każdy mechaniczny Prower musi być albo asystent dowodu (półautomatyczny z wytycznymi użytkownika) lub sprawdzania modelu.

Były próby w tym kierunku, patrz np. P-logic: weryfikacja właściwości dla Programy Haskell lub dowodzące poprawności programów funkcjonalnych za pomocą Mizar . Oba są pracami naukowymi opisującymi metody bardziej niż implementacje.

 2
Author: Fred Foo,
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-11-02 13:28:50
 1
Author: rsinha,
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
2012-08-16 05:39:42

Narzędzie AProVE jest (przynajmniej) w stanie udowodnić zakończenie programów Haskell, co jest częścią udowodnienia poprawności. Więcej informacji można znaleźć w niniejszej pracy (krótsza wersja ).

Poza tym mogą Cię zainteresować typy zależne . Tutaj system typów jest rozszerzony i używany w celu uniemożliwienia niewłaściwych programów.

 1
Author: C-Otto,
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
2012-10-17 15:19:44

Możesz użyć narzędzia hs-to-coq aby przekształcić Haskell głównie automatyczne do Coq, a następnie użyć pełnej mocy Coq udowodnić asystent do weryfikacji kodu Haskell. Zobacz dokumenty https://arxiv.org/abs/1803.06960 i https://arxiv.org/abs/1711.09286 aby uzyskać więcej informacji.

 1
Author: Joachim Breitner,
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-03-24 02:18:58