 Witamy na polskim tłumaczeniu wykładu tworzenia poprawnego oprogramowania z zastosowaniem własności generowanych testów i dowodów prowadzonego przez Majka Sperbello na żywo z 36. Kaos Communication Congress w Lipsku. Wszystkie wykłady są tłumaczone na żywo pomiędzy językiem angielskim i niemieckim oraz na jeden dodatkowy język. Więcej informacji oraz instrukcji jak uzyskać dostęp do tłumaczeń znajdziecie na setrzylingo.org. Będziemy wdzięczni za informację zwrotną. Prosimy o użycie hashtaga c3t. Dzisiaj tłumaczą Paweł Kugolewski i Lilo. Ok, zacznijmy od reklamy. Jeśli zainteresuje was ten wykład, zapraszamy na konferencję w Berlinie w lutym. Jest bardzo mała w porównaniu do tej. Bardzo chcielibyśmy was tam spotkać. Ten talk będzie dla początkujących. Jeśli oczekujecie najnowszych wiadomości z rozwoju technik dowodzenia poprawności programów, możecie być rozczarowani. Jeśli ten talk was nudzi, możecie wyjść w każde chwili. Na tym talku będziemy pokazywać przykłady w języku Idris. Czy ktoś z was zna ten język? Jeśli jakakolwiek część tego programu jest dla was niejasna po moim wyjaśnieniu, proszę o informację zwrotną. Możecie także zadawać pytania w każdym momencie, jeżeli coś jest niejasne. Ten talk ma być wstępem, ale może stać się całkiem technicznym. Ten klasyczny przykład programowania funkcyjnego. Dotyczy zwierząt na autostradzie w Texasie. Możecie zobaczyć, że środkowa definicja definiuje zwierzę. Dilo to armadillo, pancernik, a drugim zwierzęciem jest popuga, czyli parot. Obie definicje mają dwie własności, liveness, która jest jedną z własności pancernika. Na górze widzimy definicję liveness, czyli żywotności. Może być żywe lub martwe. Jest tak, że w signaturze występuje także typ. Dla papugi kolejną pierwszą własnością jest string. Papuga mówi. Definiowanie typów jest dość zaskakujące w Idris. Jak mamy tutaj A1 na 2,3 to są trzy przykłady zwierząt. Zawsze musimy mieć deklarację typu. Widzimy, że A1 to jest dilo live ten, czyli to jest pancernik, który jest żywy i na przykład waży 10 kg. Drugie jest martwy, trochę cięższy, 12 kg. A trzecie zwierzę to jest papuga i oczywiście to jest piratka papuga, więc wie gdzie jest skarb i waży 3 kg. Wszystko się zgadza jak na razie. Jeżeli macie pytania, to śmiało. No i teraz na autostadzie w Cexasie ludzie jeżdżą samochodami i będą rozjeżdżać. Teraz w programu wań funkcyjnym macie definicję funkcji. No i jakby przejmujemy zwierzę i wynikiem też jest zwierzę. No i to nie jest dokładnie zwierzę, tylko stan zwierzęcia w danej chwili czasu. Czyli na przykład, że na wejściu mamy zwierzę, a na wejściu wychodzi zwierzę, czyli mamy stan zwierzęcia, zanim je rozjedziemy i po tym jaki rozjedziemy. My wiemy, że są dwa rodzaje zwierząt. To znaczy, że definicja musi mieć tak zwane dwa równania. Pierwsze równanie, mówić co się dzieje z pancernikami, kiedy je rozjeżdżamy. No więc pancernik ma żywotność i wagę, no i używamy do smosowania do wzorca, a w drugim równaniu patrzymy na papugę. No i kiedy rozjeżdżamy pancernicha, to znaczy, że żywotność zmienia się w martwego. No a wagła się nie zmienia. Równanie na dole opisuje papuga, no i papuga staje się naprawdę cicha. Klasyczny przykład. Wrócimy do tego przykładu na koniec. Nie, to na razie tylko chcemy pokazać, jak wygląda język. No więc będziemy trochę skakać w różne miejsca. I jakiś czas temu uczyłem przedmiotu z architektury. No i mamy taki problem, że mam model domeny w bazie danych. No i przychodzi klient i ma nowe wymaganie. I zawsze to się kończy tak samo, dodajemy nowokolumne do bazy. No i to się dzieje wiele razy, po tym jak oprogramowanie się robi coraz starsze, jest to raz więcej kolumn, no ale chcemy zrobić jakieś oprogramowanie, kto jest bardziej elastyczna. No a tutaj mamy coś zupełnie innego. No, czy ktoś to rozpoznaje, czy ktoś wie, czym jest łączność, własność łączności? W jakim stanie chodziliście do szkoły, no to możecie wiedzieć, że to jest własność, którą nazywamy łączność. Jeżeli łączymy najpierw A i B, a potem dopiero C, albo odwrotnia. Jeżeli macie się nauczyć jednej rzeczy z tego wykładu, to będzie to czym jest łączność. Musimy być bardziej specyficzni, powiemy sobie, że to są na pewno liczby, że to jest łączność konkretnie dla liczba, nie dla czego innego. No na początku jest trochę matematycznie wyglądających wzorów. To odwrócone A oznacza dla każdego, czyli foroł, czyli dla każdego A, B i C i ten znak w środku oznacza, że jest elementem, czyli że A, B i C są należą do zbioru liczb naturalnych. To znaczy, że dla wszystkich liczb naturalnych A, B i C własność łączności jest zachowana w momencie, kiedy je dodajemy. Ta własność jest wszędzie, nie dotyczy tylko liczbi i dodawania, pojawia się wszędzie w programach, tu mamy inny przykład, kiedy mamy listy i te takie dziwne dwa plusy, no to jest konkatenacja, czyli sklejanie list. Oczywiście możemy sklejać trzy listy w dowolnej kolejności, no i to też jest łączne, to znaczy nie ma znaczenia, czy na początku sklejamy w B i C, a potem A na początku, czy robimy to odwrotnie, to znaczy najpierw A i B i potem C na koniec. Zawsze dostajemy ten sam wynik, czyli listy i konkatenacja też spełniają ten warunek uczności. Tutaj też jest bardzo obrazowy przykład, możemy również sklejeć ze sobą obrazki, no i ten człowiek Brent Yorkay ma właśnie ciekawą bibliotekę, w której możemy składać obrazki z części. Mamy operatory, które sklejają obrazki, składają je z części. Mamy różne kształty, są tu różne prostokąty, jest ten czarny prostokąty, no to są oczywiście wieże Hanoi. Nie ważne czym są wieże Hanoi, ale ważne, że mamy obrazek, który jest z kilku części. Przed klasycznym programowaniu obiektowym mamy jakieś kanwa z płótno i rysujemy na tym kanwasie, ale tutaj robimy coś innego, tutaj potraktujemy obraz jako typ danych. No i nie jest ważna definicja, ale ważne jest, że mamy różne funkcje, które konstruują proste obrazy. Można sobie wyrazić w funkcję Gold Star, która robi gwiazdkę. Widzimy na górsze deklaracje typu, to znaczy, że ta funkcja Star dostaje najpierw liczbę całkowitą, tryb, kolor i produkuje obraz. Możemy zawałać z trzema argumentami, na przykład 200 solid Gold, czyli wypełniona gwiazdka ze złoto. No i dostajemy obrazek, który jest obiektem. Możemy mieć inną funkcję, poligon, wielokąt, no i poligon dostaje dwie liczby całkowite, czyli wielkości i dość wierzchołków, no i potem tryb, czyli jest wypełnione czy nie wypełnione, potem kolor, no czyli na przykład możemy wziąć wielkość 180, rozmiar 5 i kolor żarwony. No i teraz pomysł jest taki, że tak jak możemy połączyć dwa liczby, to możemy też połączyć dwa obrazy. Najbardziej intuicyjny sposób to jest po prostu ustawienie ich obok siebie, czyli mamy funkcję biseid, czyli obok siebie i tworzy ona nowy obraz. No i jeżeli mówimy o łączności, no to zwykle mamy taki operator długumentowy, który łączy w jakiejś wielcze. Na przykład możemy połączyć ze sobą te dwa obrazy, możemy sobie wywalić też ABOV, czyli NAD, jeden obraz na drugim. Możemy połączyć te dwa operatory, no i jest ważne, że dostaniemy to samo, czyli możemy na tych wynikach obok zapałać też funkcję NAD, czyli ABOV. No i to są dwa możliwe operatory, no ale bardziej fundamentalne to jest overlay, czyli nałożenie jednego obrazu na drugim. Jeżeli weźmiemy złotą gwiazdę i pięci oko dostaniemy obraz, o który wygląda w taki sposób. Możemy sformulować prawo łączności, które może nie wyglądać dokładnie tak samo, ponieważ overlay jest pisany przed argumentami, a nie między nimi, tak w przypadku dodawania, ale to jest ten sam pomysł. Gdy połączymy pierdowe obrazki AB, a potem C, dostajemy taki sam wynik, jakby się pierdowe połączyli B, C, a potem dodajemy przedku A. Czy to ma na początku, czy to się zgadza? Czy są pytania? Tak, mamy pewne pojęcie przejroczystości, obrazki nie muszą wypełniać całego brzegu. Jeżeli pomyślecie o tym obrazku, jako o przyposzątkowaniu każdemu mikselowi koloru, to musimy też mieć kolor przejroczysty. Takie sytuacje można mówić, że możemy łączyć kolor i mamy operację łączenia kolorów, dla której przejrza czystość jest elementem neutronem. Ponieważ ta operacja łączność występuje bardzo często, nazwijmy strukturę, której dziś jest operacja łączna semi grupa. Półgrupem. Półgrupa to zbiór z działaniem, której jest łączna. Ten zbiór może być na przykład zbieramy liczb naturalnych z zadawaniem. N-E-A-B-N-C from that set S, we can use circle as an operator, and we have that associativity property. Operacja oznaczona kukiem może wydawanie mieć naturalnych łączeniem diagranów i podobną pracę. Może to być też konkatenacja liczb czy łączenia. Własność łączności oznacza, że nie ma znaczenia, jak postawimy nawiasy, można je po prostu pominać. Nie ma znaczenia, jak postawimy nawiasy, zawsze zostaniemy taki sam wynik. Takie wrażenie łatwiej czytać. Załóżmy, że mamy bardzo dużo danych. Jeżeli chcemy je połączyć, mamy dane na bardzo wielu dyskach twardych, gdy mamy operację łączną, możemy je wykonywać, możemy taką operację wykonać na różne sposoby, tak jak będzie wygodnie. Na tym opiera się framework map reduce. Łączność jest dużo bardziej przydatna, gdy projektujemy biblioteka i zastanawiamy się, w jaki sposób możemy ją rozczarzyć. Traktujemy naszą dziedzinę nie jako coś, co ma coraz więcej własności, tylko jak pewne klocki, które łączamy na różne sposoby. W tym przypadku tworzymy obrazki łącząc kilka różnych, łącząc obrazki z prostszych atomów. Praca w Brenta i Orgae o monoidach zawiera kilka przykładów, w jaki sposób taką bibliotekę można tworzyć. Tutaj mamy operację Overlays wcześniej. To prace można wyszukać, pisując jej nazwę Google. Monoid w tytule to pół grupa, w której jest element neutralny, to jest taki, który po połączeniu z dowolnym innentem daje ten sam element. Kiedy łączymy cokolwiek z elementem neutralnym, dostajemy to samo. W przypadku lećby i dodawania byłoby to zero. W przypadku list i łączenia byłby to pusta lista. W przypadku operacji Overlays możemy sobie wyobrazić pusty diagram, to jest taki, który cały jest przezioczysty. Wszystkie te przykłady pokazane wcześniej to były nie tylko samej grupy, ale też monojdy. Musicie pamiętać o łączności, ale pamiętajcie też o monojderach. Monoidy też znajdują się wszędzie. Na przykład muzyka też jest monojdy. Możemy opisać strukturę muzyki jako operacje na monojdzie. Animacje w osi czasu. Możemy łączyć ze sobą animacje. Bardzo słabnym przykładem są kontrakty finansowe. W zeszłym roku był wykład na temat produkcji prób przewodników. Monojdy są wszędzie i można znaleźć bardzo dużo przekładów. Jest to klucz do zrozumienia wielodomen. Można powiedzieć, że to są klocki, które możemy łączyć w większą całość. Możemy użyć łączności, można użyć monojdów, żeby w jakiś sposób ułożyć to. Pamiętajcie o operacje łącznie obrazów obok siebie i nad. Możemy nie mieć łączyć rzeczy w osi pionowej poziomej. Działają tak, że liczą jakieś pudełko, które okazuje, że nie ma jakiejś pionowej poziomej, ale nie ma jakiejś pionowej poziomej, a nie ma jakiejś pionowej poziomej, a nie ma jakiejś pionowej. Począ jakieś pudełko, które okala oba te obrazki. To działa dobrze, kiedy nasz obrazek to jest kładę, który jest ładnie ułożony, ale kiedy jest to obrócony, to wtedy te pudełka są za duże i jeżeli próbujemy coś dołączyć w defaultnym kierunku, to wtedy będzie w bieli odstęp. Tak, że obok i nad to może nie są najlepsze operacje do naszej biblioteki, nakładanie rzeczy na siebie mogą być lepiej, ale pytanie, w jaki sposób możemy teraz połączyć obrazki, także było obok siebie, no i Brent wymyślił coś takiego, jak envelope, koperta. Pomysł jest taki, że jeżeli mamy punkt początkowy, ta czerwona kropka i teraz dostaniemy wektor, to możemy powiedzieć, jak daleko musimy iść z dłużtego wektora i narysować linie, która jest akurat na kawędzi. Takie koperty bardzo dobrze działają, kiedy mamy obrazek, każdy obrazek ma nie tylko piksele, ale też funkcje, która mówi, jak wygląda taka koperta i wtedy możemy łączyć te obrazki również pod kątem, nie tylko pianowo i poziomo. Czy to ma sens? No i Brent pokazuje, w jaki sposób można użyć tej inspiracji z monoidów i tak, wszystko musi być monoidem i używa to jako zasady w tej bibliotece. Nie będę wchodził w szczegół, jak to działa, ale to jest bardzo ciekawy wertyku i bardzo piękna biblioteka, której przyjemnie się używa. To znaczy, że musimy znaleźć operacje łączenia w monoidzie dla takich kopert. Musimy nie tylko łączyć piksele, ale też łączyć koperty i to na szczęście jest łatwe, bierzemy po prostu maksimum z tych dwóch obrazków. Widzimy, że jak mamy dwie liczby, no to musimy wziąć ich maksimum, żeby być na zewnątrz tego połączonego kształtu. Pokazałem te własności jako własności matematyczne z takim odwróconym, a dla każdego, no i może powiedzieć, że dla każdego obrazu coś zachodzi, ale możemy sformułować te operacje również jako kod i tutaj też dzieje się pewna magia. No i widzimy, że dalej nie ma wielkiej różnicy, poznaczę, że używamy innego fontuło, ale w języku funkcyjnym możemy też napisać tę linkę na górze jako kod, tak to na przykład wygląda idris, no nie wygląda teraz zupełnie tak samo, ale jest oczywiście struktura. No i mamy tutaj własność, nazywają Overlay Associative, no i widzimy, że używamy tutaj wszędzie znaków waski, czyli piszemy for all zamiast odwrócenego a, no i teraz mówimy, art-image, art-image, art-image, czyli dla dowolnych trójek, dowolnych obrazów, dowolnych trzech obrazów, no i nazwiemy, nazwiemy je image one, image two, image three. Ten bug slash to jest lambda, idris, no i ta własność znaczy, że jeżeli nałożymy to obrazów w jeden sposób i w drugi sposób to wynik będzie łączność, dostaniemy ten sam wynik, no pewnie rozpoznajecie tą strukturę, to jest ta sama struktura, co w notacji matematycznej, tylko jako kawał kodu. Najlepsze jest to, że teraz jak mamy taki kod, to może go mam niej pulować w programie. Jeden z najbardziej uprzedlatnych sposobów, to jest to, co wymyślił John Hughes, czyli quick check. Jeżeli chcecie zapamiętać jeszcze jedno rzecz z tego wykładu, to jest quick check. Można i jest dostępny w wielu niezwykłych pogromowaniach, nie tylko w interesie, tak naprawdę musiałem nachakować sobie wersje w interesie, no ale w różnych, jest są implementacje w innych językach, na przykład w żawie, w fajtonia, więc quick check. Może. Jako przykład. Pogarzę coś, co można łatwo napisać źle. Powiedzmy, że chcemy mieć reprezentację zbiorów przedziałowych. Będziemy reprezentować te zbiory jako listę przedziałów. Jako listę zakresów. Mamy definicję typu list, który składa się z dwóch liczb naturalnych. Ten typ jest zdefiniowany jako synonim na takie listy. Od nabia się nad, oznacza, krótkę dwóch liczb naturalnych. Definicja i to list, pominąłem definicję funkcji, ale jest to funkcja, która zamienia typ i set w listę liczb naturalnych. Ono bierze listę liczb, listę pari naturalnych i zamienia to na listę liczb naturalnych, więc zamienia reprezentację zbioru. Dajemy jej listę przedziałów od 0 do 3, od 5 do 7, od 9 do 10. Wszystkie przedział są dąknięte. Więc ponad pierwszy przedział jest od 0 do 3, więc zaczynamy listę 0, 1, 2, 3. Dalej od 5 do 7, więc mamy 5, 6, 7 i ostatni jest od 9 do 10, więc mamy tylko 9 i 10. Oczywiście, chcielibyśmy, żeby ta lista miała pewną strukturę. Chcielibyśmy, aby każdy przedział miał lewy, aby lewy koniec każdego przedział był mniejszym, niż prawy i tak samo, żeby przedziały rosły. Na przykład, to pozwoli nam efektywnie przetwarzać takie przedział, gdy będziemy wiedzieć, że są posortowane. W przypadku zbioru pustego mówimy, że jest to poprawny zbiór. Mamy tu trzy przypadki. Pierwszy przypadek, gdy lista jest pusta, zwracamy po prostu tru, uznajemy, że to jest poprawna reprezentacja pustego zbiór. Drugi przypadek, gdy mamy tylko jedyny przedział, wymagamy wtedy, żeby l o, czyli lewy końc przedział był mniejszym, niż prawy końc przedziału. Palewa, jobvention. Tak, to jest trochę bardziej komple reconocisuwane. W trzecim przypadku, jeżeli mamy co nam nień, dwa przedziały. Pierwszy od l o, l o, l o k., l o k., l o, l o k., l o, k, l o, k., l o, k., l o, l o, k, l o, k., l o, k., l o, k, l o, k, l o, k., l o, k., l o, k. To z watercolor Hindi. To dodaje wyd 199 kindergartenów, a dalej mamy resztę listu這是aranatów. Chcemy, żeby ten przedział był porządkowany, więc wymagamy, aby ALO-1 było mniejszą oczy do H1. Ale chcemy też, żeby między tymi dwoma przedziałami była luka, bo jeżeli jej nie ma, to w prostu moglibyśmy je połączyć. Zatem przemyżę, że H1 plus 1 jest mniejsze niż L2. Ponadto sprawdzamy rekurrencenie, że resztalisty jest poprawna. Tak samo? Ok. To chyba jest druga, oczy komplekowana pisa kod. Zresztą, więc my możemy wyobrazić funkcję unia. Możemy wyobrazić te funkcje ułączenia zbiorów z kłonący się z przedziałów. Ta funkcja będzie daja poprzednią monoidę. Namy dwa zbiory przedziałowe i dostajemy z powrotem taki zbiór. Oczywiście to będzie bardziej skomplikowane dlatego, bo mamy ten warunek poprawności. To, co możemy zrobić, to możemy zacząć od napisania kryterium, kiedy ta funkcja zwraca popranowatość. Dla każdego par dwóch przedziałów, dwóch zbiorów przedziałów, chcemy, żeby ich suma była poprawna. Chcemy, aby funkcja ułącząca zachowała poprawność tych zbiorów. Inna własność, jeżeli skonwertujemy sumę zbiorów na listę, powinniśmy dostać to samo, niż gdybyśmy skonwertowali te dwa zbiory osobno na dwie listy i następnie je połączyli. To są dwa kryteria, jak to dobrze było być. Nie tak, ale w końcu, zbiór musi być zbiór. Nie może być zbiór, że zbiór jest zbiór, ale to jest zbiór. Nie może być zbiór, ale nie może być zbiór, ale nie będzie zbiór. No i to są dwa kryteria, które dobrze byłoby tutaj mieć, żeby mieć poprawna implementacja. No i zacząłem już pisać przed wykładem, o tutaj. To jest co wymyśliłem. Zignorujcie całą resztę tego kodu, no ale popatrzcie tutaj iUnion, iSet, iSet, iSet. No i pierwsza linika mówi, że jeżeli pierwsze zbiory jest pusty, no to zbierzemy drugi, jeżeli drugi zbiory jest pusty, to bierzemy pierwsze. Klasyczny przypadek, kiedy mama jakąś sumę albo kąsklatynację. No i trzeci przypadek, robię się bardziej skomplikowane. No i trzeci przypadek jest taki. Jeżeli oba argumenty mają przynajmniej jeden element, no i tutaj widzimy pierwszy, to jest low1, hi1, drugi to jest low2, hi2. No i napisałem już trochę kodu, że jeżeli low1 jest większy, to wtedy zaczynamy od tego drugiego i kontynuujemy, ale jeżeli low2 jest większy od hi1, to w jeszcze innym przypadku to żaden z nich nie jest po tym drugim, czyli musimy je jakoś złączyć z sobą. Jeżeli to nie ma sensu, to nie martw się, się wrócimy do tego. No i teraz odpowiedziałem wam o tym narzędziu, quickcheck. No i fajne jest to, że możemy to teraz załadować do idilsa. No i dostajemy interpreter. Mam nadzieję, że robię to dobrze. Chcemy wywołać quickcheck. Używamy tej własności w proprie union correct. Możecie zobaczyć, że przeszło 100 testów. No i to jest to, co robi quickcheck, czyli bierze twój kod tej własności i generuje bardzo dużo testów. No i to jest bardzo wydajne, bardzo efektywne. No i mówi, że to jest zawsze poprawne, czyli zawsze jak weźmiemy listę, to dostaniemy odpowiedni wynik. Ale teraz badamy drugą własność. I tutaj jest ciekawsza część, dlatego że mówi, że ta własność jest falsyfikowalna, to znaczy mamy kontrprzekład. Mówi, że wygenerowałem 9 losowych testów. No i znalazłem test, dla którego wynik jest niepoprawny. No i fajne jest to, że możemy teraz przekopiować ten przykład. Usuwałem przecinki, uruchamiamy to i okazuje się. Tak przy okazji, to jest losowe, czyli za każdym razem jest inna. No i okazuje się, że ta dwa przedziały powinny być włączone i powinny być jednym przedziałem. No ale to nie stało się, to nasza funkcja tego nie zrobiła. Być może zobaczyliście już powód. Mamy tutaj te dwa przykłady, pamiętajcie jak powiedziałam wam, że musi być odstęp pomiędzy tymi dwą przedziałami. No i tutaj mamy błąd przesunięcie o jeden. Czyli te dwie liczby mogą być teraz obok siebie. Musimy się upewnić, że będzie tutaj odstęp, żeby to naprawić. O, w ten sposób. Załadujemy w 3 razy. O nie, znowu mamy kontrprzekład. Spróbujmy to. To jest bardzo dobra, dlatego że dostaniemy przypadek testowe. No i widzimy, że ciągle mamy nakładające się przedziały. Co tu się stało? Widzicie to? Widzicie, że te pierwsze dwa przedziały pewnie podpadają po ten ostatni przykład. To nie zostało związane z tym sześć siedem. No i jak popatrzymy na ten kod, to pewnie stało się to w tej linice. No a potem uruchomiliśmy to na całej reszcie. Co tu się stało? Złączyliśmy tamte przedziały. Mamy tutaj problem z symetrią. Nie mogłem to zrobić dla siebie. To może być trudne do zauważenia, sam bym tego nie zauważył. Ta operacja maksimum może naruszyć własności tego algorytmu. Nigdy mi się to nie zdarzyło. To może być trudne do zauważenia, sam bym tego nie zauważył. To może być trudne do zauważenia, sam bym tego nie zauważył. To może być trudne do zauważenia, sam bym tego nie zauważył. Nigdy mi się to nie zdarzyło. Widzicie, że tutaj musimy mieć symetry. Dobrze, spróbujmy zrobić, żeby było symetrycznie. To będzie działać tylko, jeśli high 1 jest mniejsze od high 2. Większy niż high 2. Nie ma tego miejsca. większe niż H1. Wtedy maksimum będzie H1. W tym momencie wolno nam dodać ten element tutaj. No ale w tym drugim przypadku musimy zrobić coś innego. Czyli musimy ten element umieścić na początku. Teraz mamy symetryczne przypadki. Przeładujemy. No i teraz się udało. Trenowałem przed tym wykładem. No ale zawsze takie rzeczy sprawiają, że trochę się poce, szczególnie jak robi się starszy. No i quick check to jest dobry sposób, żeby właśnie wywołać, usunąć te wszystkie błędy. Chociaż czasem nie mają gwarancji w jakiej kolejności to robi. To jest świetne narzędzie. Spróbujcie swoich się z niego skorzystać. Zobaczmy na kilka prawdziwych przypadków, na przykład w X Windows. Czyli mamy menedżer okienek, X Monad teraz nie robi się, nie ma na nim już zbyt wiele prac, dlaczego, że jest poprawne, a dlaczego jest poprawne? Dlatego, że napisano wiele własności do algorytmów geometrii i rozmieszczania okienek i zweryfikowali je i quick checkiem. Autorzy X Monad napisali kilka postów, gdzie przetłumaczyli prostszą wersję X Monad na indrys. No tutaj mamy bardzo prosty przykład i nie mamy tutaj geometrii, tylko po prostu stosy okienek. Czyli mamy typ danych, który nazywamy stackset. Jest parametryzowany typem window, czyli okno. No nie mówimy tutaj czym są te okna, ale mamy jeden konstruktor i teraz mamy dwa pola. Current to jest... to jest nr bieżącego obszaru roboczego. Stacks to jest mapa z nr obszaru do stosu czy listy okien. Szczegóły nie są tutaj istotne, ale mamy różne operacje, które działają na tych konfiguracjach tego menedżera. Znowu szczegóły nie są istotne, ale możemy stworzyć pusty set. Możemy mieć operacje, która rotuje ten stos okien i wybiera okno, które chcemy, żeby było na górze. Dlatego, że użytkownik na nie patrzy, czyli wykonujemy taką rotację w lewo albo w prawo. Możemy włożyć okno na wierzch, możemy włożyć okno do innego roboczego. Możemy włożyć okno, czyli jest to oznacza, że jakoś przesuwamy te okna. To nie jest ważne co dokładne robią te operacje, ale możemy sobie wyobrazić podobnie jak z tymi spierami, że mama jakieś niezmienniki. Mamy tutaj taką własność, możemy zobaczyć czy dany stos jest spójny. Numer naszego bieżącego stosu musi być mniejsze od ilości wszystkich, ale ta druga część mówi, że okno nie powinna być na kilku obszach oboczych naros. Przy tych definicjach definicje funkcje nie są bezkomplikowane, ale wytłumaczę kilka własności. Jeśli zrozumiecie kilka z nich, zrozumiecie wszystkie. Jeśli wykonam funkcję View, to chcę, żeby ta funkcja stworzyła spójny stos. Na samym dole widzicie niektóre wymagania, które muszą spełnić własności. Jeśli liczba okien jest mniejsza niż rozmiar stosu, naruszymy własność. Jest to dobry sposób na wynajdywanie nowych własności, jeśli potraficie znaleźć niezmiennik, czyli własność, która zawsze powinna być prawdziwa. Nie jest to bardzo skomplikowane, ale możesz sobie wyobrazić, że własność upraszcza to bardzo definicję dla menedżera okien, który oczywiście używa bardziej skomplikowanych. Spisując te własności, możemy je przetestować automatycznie quickcheckiem. Kilka miesięcy temu musieliśmy migrować aplikację w Visual Basic, która była gigantyczna. Tutaj widzimy fragment dotyczący sprawdzania haseł. Własność, którą sprawdzaliśmy dotyczyła porównywania hasła z bazy danych. Okazała się mieć błędy, które wykryliśmy quickcheckiem i poprawiliśmy ją tak, jak widać na tym slajdzie. To znaczy, że możemy użyć quickchecka nie tylko po to, żeby sprawdzić poprawność rzeczy, o których już wiemy, ale także, żeby poprawić istniejące własności. Dla dużego klienta z przemysłu pisaliśmy aplikację służącą do synchronizacji, kiedy różne urządzenia, spotykając się, wymieniały dane. Nie chcieliśmy, żeby wymieniały wszystkie dane za każdym razem. Chcieliśmy, żeby wymieniały tylko te dane, które wymagają uzupełnienia. Są do tego świetne algorytmy o partnerach Merkela. Okazuje się, że własność dla tego jest dość prosta do napisania. Algorytm synchronizacji działa na setach, czyli zbiorach bloków. Nazywamy je tutaj BS1, BS2. To, co chcieliśmy zrobić, to jeśli włam na nich operację union, czyli łączenia zbiorów. Dostajemy wszystkie bloki w systemie, nazywamy je all. Uzyskujemy je, wywołując operację synchronize. Nazywamy uzyskane sety BS1, BS2. I własność mówi, że jeżeli zrobimy union obu zestawów, uzyskamy wszystkie. Chcemy się upewnić, że wszystkie bloki, które przesyłamy, nie mają elementów wspólnych. Bo to nie miałoby sensu. Spędziłem, że nie miałoby sensu. Spędziłem, że nie miałoby sensu. Spędziłem tydzień lub dwa, walcząc z tym algorytmem, ale ten jeden test własności pozwolił mi usunąć wszystkie bloki. James Hughes napisał świetny tekst o ukorzystaniu czeków. Dotyczyło on otwierania i zamykania plików. Nie się tylko otworzymy jakieś okno i sprawdzamy tam jakieś rzeczy, to jeszcze włącznie nie pojawi, ale jeszcze włącznie nie pojawi. Włącznie nie pojawi. Jeżeli spróbujemy wyłączyć i wyłączyć, to wtedy baza się psuje. Tutaj inny przykład, możemy nazwać to tajemnicę Dropboxa. Jeżeli mam coś takiego Dropbox, to naprawdę chcemy, żeby pewne własności były spełnione. No i okazało się, że nigdy nie zapisali tych własności. No a John Hughes je zapisał. I znalazł kilka błędów. Trudno to przeczytać, ale jeżeli pierwszy klient zapisuje A do pliku, który był najpierw pusty, to Anpinysko znacza pusty, a potem kasuje ten plik, a jeżeli drugi klient zastępuje A przez B, no i wtedy znowu klient pierwszy wracuje do pliku, który był pusty. To niestety, no i chcieliśmy zobaczyć B albo C w tym pliku, ale plik został skosowany. Chyba to już naprawi. Oscar Wigstrom ma też kilka przykładów postów na temat edytera screencastów, które również podoba. No i to jest świetne narzędzie do znajdywania błędów, ale to nie to samo, co dostać dowód. Czyli możemy sobie wyobrazić, że nadal znajdziemy jakieś poważne błędy, których Wigstrzek nie wykryje, bo to nie to samo, co upownienie się, że to wszystko jest poprawne. No i to jest świetne narzędzie do znajdywania błędów, ale to nie to samo, co dostać dowód. To jest poprawne. Powód, dla którego wybrałem indrić, dlatego wykładu, to dlatego, że w idrysie można nie tylko zapisać własności, ale także testy w samym języku. No i tutaj mamy operację Konkaten-Apsilis. Na górze mamy definicję funkcji ze standardowej biblioteki. No, czyli te dwa plusy oznaczają, czyli dwie listy na wejściu, jedna listy na wejściu. Przypadek dla listy pustej, to jest po prostu bierzemy tą drugą listę, ale jeżeli łączymy listę, która zaczyna się z Xem i potem z Xs, to znaczy, że X trafia na początek, a potem konkatenujemy całą resztę. Klasyczna definicja w programowaniu funkcyjnym. No i teraz ta dziwna rzecz w idrysie. Tutaj widzimy deklarację typu, też z biblioteki. No i w samym tym typie widzimy własność w łączności dla tych trzech list ABC. Mamy podaną własność, to jeszcze nie jest to samo co dowód. A komu się podobało pisanie, to jest to, co się podobało, to jest to, co się podobało, to jest to, co się podobało, to jest to, co się podobało, to jest to, co się podobało, to jest to, co się podobało do dowódów matematyce. Ja nie lubiłem tego. Idrys na szczęście pomaga nam pisać dowody. Pokażę wam to bardzo szybko. To jest to, co było na slajzie. No i załadowałem to. Idrys mówi, że jeszcze nie skończyłeś, nie napisałeś dowodu, ale piszę mnie, wystarczy, że wciśniemy kilka przycisków, tutaj wciskam przycisk, Teraz wciskamy inny przycisk, no i ponieważ robimy to dla list, to znaczy dla list zawsze mamy dwa przypadki, musimy przypadek pusty i przypadek, gdzie lista zaczyna się z xa i jakiś xs-u dalej. No i teraz mogę jeszcze powiedzieć Idrisowi, że jestem leniwy i znowu naciskam przycisk i Idris mówi, Idris sami w to wpisuje, ja tego nie wpisałem i to mówi Reflu, co to jest Reflu? No co to jest? Możemy sobie o to zapytać, no i widzimy, że to jest taki wbudowany dowód, że dwie rzeczy są równa, jeżeli są identyczne. No i to ma sens dla tego pierwszego równania, dlatego że pierwsze równanie do tego apenta sok odpowiada pierwszemu równaniu na górze, widzicie? Dla pierwszej listy to jest pusta. Widzicie ten przypadek dla pustej listy, tam na górze, dla plus plusa? No i teraz może nie oczywiście, ale jeżeli podstawimy to do definicji, to będzie działać. No i tutaj Idris zaakceptował dowód, drugi przypadek jest trochę trudniejszy, ale też możemy dostać tutaj trochę pomocy. To jest rekursywna funkcja, rekursja jest po pierwszym argumentie, czyli możemy taką samą rekursję wykonać w dowodzie, może powiedzieć Idrisowi, żeby użył tego faktu. Tu jest wywołanie rekursywne, no i jeżeli napcisz na przycisk to znowu pojawi się Reflu. Możecie nie rozumieć, jak to działa, ale to jest dowód tej własności łączności i konkatenacji w Idrisie. No ponieważ Idris pomaga, to jest to niezłosobawa. Dla kogoś, kto zwykle nie lubi pisać dowodów. Idrisie umieszczamy dużo informacji w typach i więcej informacji będzie w typach, tym lepiej Idris potrafi ją wykorzystać i nie musimy pisać za dużo sami. No i takie systemy wspomagające dowodzenia są wykorzystywane w prawdziwych systemach. No i znany przykład to jest CEL-4, to jest Karnel, który jest weryfikowany, działa na przykład w Enklawie Bezpieczeństwa, WIOS, no i jest udowodnione, że nie ma na przykład przepełnienia bufora i innych podatności. Komp ser to jest inny przykład, który jest, chciałbym powiedzieć, że to jest weryfikowane za pomocą narzędzia Isabelle, no i kompser to jest weryfikowany kompilator do C, no i możemy mieć certyfikowany kod, ale jak sprawdzimy, że kod kompilatora jest dobre, no i tutaj kompilator też został udowodniony. No i na przykład alokacjer jest bardzo skomplikowana, ale możemy napisać sprawdzenie tego alokatora. No są różne narzędzia do tego, widzieliśmy Idris, jest więcej narzędzi, są coraz bardziej dojrzałe i świetnie się używa. Wracając trochę, mamy tutaj wiele własności, które mogą być przydatne, na przykład przemienność, jeżeli pamiętacie relacje z lekcji matematyki, no to tutaj mamy różne własności, takie jak relacja zwrotna, symetryczna, antysymetryczna i przechodnia. Antysymetria oznacza intuicyjnie odwrotnienie symetria, jeżeli rzeczy są sobie w relacji pobiez stronę, to też edytą musi być ta sama rzecz, no a przechodność to znaczy, że możemy tak łączyć rzeczy w łańcach, czyli mamy taki słownik przydatnych własności. No to jest własność funkтора, może widzieliście w waszym języku pogromowania mapowanie, nawet Java to ma przez wiele lat. To, co maper robi, to jeżeli mamy daną funkcję, jeżeli na przykład mamy listę, możemy zastosować funkcję na każdym elemencie tej listy, ale można to uogólnić, nie musiało to być listy, mogą to być strumienie, albo opcje. Możemy uogólnić to pojęcie i dostajemy funktor. W Indii się możemy napisać zweryfikowany funktor, jest to funktor, który spełnia obydwa prawa, może tu zignorować dokładne techniczne szczegóły. Funkcja Funktora Identity mówi, że map GX, to znaczy, że mapowanie po G jest funkcją identycznością. Jeżeli zastosuj, natomiast Funktora Composition mówi, że zastosujemy w pierwszą funkcję, a potem drugą, i zastosujemy, że zrobimy to na każdego elementu listy, to tak jakby się zastosuje w pierwszą, też jedną operację na każdą elemencie listy, a potem zastosujemy mapowanie jeszcze raz drugą operacją. Gdzie mogę znaleźć funktor? Nie widziałem z innych innych funktorów. Zaczęliśmy od tego przykładu ze zwierzątami. Gdzie tu możemy znaleźć funktor? Zastanawiałem się, ale się okazuje, że mamy funktor na zwierzątach. Możemy... Po czym mówimy parametr? I w przypadku zwierząt będzie to argument liveness albo string. Obydwa te zwierzęta mają wagę, więc funktor będzie zachować wagę. Zmieniamy wagę z małą miterą nadłużną i dostaniemy wtedy parametryzowane zwierzę, które waga może być dowolnym typem, i operacja Fma będzie zmieniać się. Obydwa te zwierzęta mają wagę, więc w ogóle nie można zmieniać wagi, ponieważ typ jest uniwersalny i nie można w końcu na nim żadnej operacji. Nawet na takim przykładzie można załalić pewną za tę funkcję. Niechą konkluzję. Znaleźć szukać kombinatorów, funkcji, które łączą małe rzeczy w duże rzeczy. Sprawdź, czy możemy sprawić, żeby ta operacja była łączna. Szukamy elementu neutralnego. Staramy się, aby to był monoid. Piszemy własności. Testujemy je używając QuickCheck. Jeżeli mamy już dużo czasu, możesz udowodnić, że one są prawdziwa. Znajdujemy funktor. To może trochę potrwać. Jestem bardzo stary, ale po straningiem będzie to łatwiejsze. Jeżeli udowodnisz albo przetestujesz własności, możesz spokojnie, że twój program będzie działać dobrze. Mamy 3 minuty na pytania. QuickCheck wygenerował 100 testów. Co możemy powiedzieć o jakości? Czy to jest wystarocznie dużo testów? Czy to są poprawne testy? Czy to dobrze? Dobre pytanie. Jeżeli chodzi o jakość testów, jeżeli mamy zastosowania przemysłowe, to QuickCheck ma kilka narzędzi, które pozwalają patrzeć na rozkład przykładów. Nie robiłem tego tutaj, ale dla obiektów naszej domenie zwykle piszemy generatory, które generują te przykłady i potem możemy coś robić z rozkładem tych przykładów i absolutnie powinniśmy to robić. Są narzędzie, które w tym pomagają. Nawet jeżeli tego nie robimy, nawet bez tego znalazłem wiele błędów w swoim programowaniu. No, ale trzeba popatrzeć na rozkład. Dziękuję. Następna, nie? Napisałem program w Javi, w Javi lub C-Sharpie. Jak mogę zastosować to, co się nauczyłem? Gdzie zacząć? Gdy już mam skończony program. Bardzo pragmatycznie, ponieważ jest to program napisany w C-Sharpie. Jeżeli możesz wymyślić własności, które pasują do tego programu, na przykład w C-Sharpie możesz zlinkować z F-Sharpem. Jest w S-Sharpie a QuickCheck na F-Sharpie. I S-Check, mimo że jest napisany w F-Sharpie, można używać go z C-Sharpa, więc masz dwie opcje. Możesz napisać testy w C-Sharpie lub można znikować kod z F-Sharpem i użyć odpowiednika QuickChecka tam. Istnieje implementacja QuickChecka w Javi, całkiem dobra? W skali. I w Closure istnieją bardziej zamansowane implementacje, które może też mogą bądź w Javi. Innymi słowy, dla każdego rodzaju za programowania muszę znaleźć poprawną implementację QuickChecka. Wyglę wystarczy poszukać na przykład QuickCheck i na językę programowania. Dziękuję za uwagę, to było polskie tłumaczenie.