Читать книгу Programowanie funkcyjne w języku C#. Jak pisać lepszy kod - Enrico Buonanno - Страница 7
Spis treści
Оглавление1. Wprowadzenie do programowania funkcyjnego
1.1 Czym jest to, co nazywamy programowaniem funkcyjnym?
1.1.1 Funkcje jako elementy pierwszoklasowe
1.1.3 Pisanie programów o silnych gwarancjach
1.2 W jakim stopniu język C# jest funkcyjny?
1.2.2 Elementy funkcyjne w C# 6 i C# 7
1.2.3 Bardziej funkcyjna przyszłość dla języka C#?
1.3 Myślenie w kategoriach funkcji
1.3.1 Funkcje jako odwzorowania
1.3.2 Reprezentacja funkcji w C#
1.4 Funkcje wyższego rzędu
1.4.1 Funkcje zależne od innych funkcji
1.4.2 Funkcje adapterowe
1.4.3 Funkcje, które tworzą inne funkcje
1.5 Używanie funkcji HOF do unikania duplikacji
1.5.1 Enkapsulacja przygotowania i zwolnienia w HOF
1.5.2 Włączanie instrukcji using do HOF
1.5.3 Kompromisy HOF
1.6 Korzyści wynikające z programowania funkcyjnego
Ćwiczenia
Podsumowanie
2. Dlaczego czystość funkcji ma znaczenie
2.1 Czym jest czystość funkcji?
2.1.1 Czystość i efekty uboczne
2.1.2 Strategie zarządzania efektami ubocznymi
2.2 Czystość i współbieżność
2.2.1 Czyste funkcje są dobre do równoległego przetwarzania
2.2.2 Równoległe wykonywanie funkcji nieczystych
2.2.3 Unikanie zmiany stanu
2.3 Czystość i testowalność
2.3.1 W praktyce: scenariusz walidacji
2.3.2 Testowanie funkcji nieczystych
2.3.3 Dlaczego testowanie nieczystych funkcji jest trudne
2.3.4 Parametryzowane testy jednostkowe
2.3.5 Unikanie interfejsów nagłówkowych
2.4 Czystość a ewolucja obliczeń
Ćwiczenia
Podsumowanie
3. Projektowanie sygnatur funkcji i typów
3.1 Projektowanie sygnatury funkcji
3.1.1 Notacja strzałkowa
3.1.2 Ile informacji niesie sygnatura?
3.2 Przechwytywanie danych za pomocą obiektów danych
3.2.1 Typy podstawowe często nie są dostatecznie konkretne
3.2.2 Ograniczenie wejść za pomocą typów niestandardowych
3.2.3 Pisanie „uczciwych” funkcji
3.2.4 Łączenie wartości za pomocą krotek i obiektów
3.3 Modelowanie braku danych za pomocą Unit
3.3.1 Dlaczego void nie jest idealny
3.3.2 Usuwanie luki między Action a Func za pomocą Unit
3.4 Modelowanie możliwego braku danych za pomocą Option
3.4.1 Kiepskie API, których używamy na co dzień
3.4.2 Wprowadzenie do typu Option
3.4.3 Implementacja Option
3.4.4 Większa niezawodność dzięki zastosowaniu Option zamiast null
3.4.5 Option jako naturalny typ wyniku funkcji częściowych
Ćwiczenia
Podsumowanie
4. Wzorce w programowaniu funkcyjnym
4.1 Stosowanie funkcji do wewnętrznych wartości struktury
4.1.1 Odwzorowanie funkcji na elementach ciągu
4.1.2 Odwzorowanie funkcji na Option
4.1.3 Jak Option zwiększa poziom abstrakcji
4.1.4 Wprowadzenie do funktorów
4.2 Wykonywanie efektów ubocznych za pomocą ForEach
4.3 Łączenie funkcji za pomocą Bind
4.3.1 Połączenie ze sobą funkcji zwracających Option
4.3.2 Spłaszczanie zagnieżdżonych list za pomocą Bind
4.3.3 To nazywamy monadą!
4.3.4 Funkcja Return
4.3.5 Relacje między funktorami a monadami
4.4 Filtrowanie wartości za pomocą Where
4.5 Połączenie Option i IEnumerable za pomocą Bind
4.6 Kodowanie na różnych poziomach abstrakcji
4.6.1 Wartości regularne a podniesione
4.6.2 Przekraczanie poziomów abstrakcji
4.6.3 Map kontra Bind – ponownie
4.6.4 Praca na właściwym poziomie abstrakcji
Ćwiczenia
Podsumowanie
5. Projektowanie programów za pomocą składania funkcji
5.1 Złożenie funkcji
5.1.1 Przegląd informacji o złożeniu funkcji
5.1.2 Łańcuch metod
5.1.3 Złożenie w świecie podwyższonego poziomu
5.2 Myślenie w kategoriach przepływu danych
5.2.1 Używanie składalnego API z LINQ
5.2.2 Pisanie funkcji, które łatwo podlegają złożeniu
5.3 Programowanie przepływów pracy
5.3.1 Prosty przepływ pracy w celu weryfikacji
5.3.2 Refaktoryzacja z uwględnieniem przepływu danych
5.3.3 Złożenie prowadzi do większej elastyczności
5.4 Wprowadzenie do funkcyjnego modelowania dziedziny
5.5 Kompleksowy przepływ pracy po stronie serwera
5.5.1 Wyrażenia kontra instrukcje
5.5.2 Deklaratywnie kontra imperatywnie
5.5.3 Funkcyjne spojrzenie na warstwy
Ćwiczenia
Podsumowanie
Część II W stronę funkcyjności
6. Funkcyjne obsługiwanie błędów
6.1 Bezpieczniejszy sposób przedstawiania wyników
6.1.1 Uchwycenie szczegółów dotyczących błędów za pomocą Either
6.1.2 Podstawowe funkcje do wykorzystania z Either
6.1.3 Porównanie Option i Either
6.2 Łączenie działań, które mogą się nie udać
6.3 Walidacja: doskonały przypadek zastosowania Either
6.3.1 Wybór odpowiedniej reprezentacji dla błędów
6.3.2 Definiowanie API opartego na Either
6.3.3 Dodawanie kodu walidacji
6.4 Przedstawianie wyników aplikacjom klienckim
6.4.1 Udostępnianie interfejsu typu Option
6.4.2 Udostępnianie interfejsu typu Either
6.4.3 Zwracanie wynikowego obiektu (DTO)
6.5 Wariacje na temat Either
6.5.1 Zmiana reprezentacji błędów
6.5.2 Specjalizowane wersje Either
6.5.3 Refaktoryzacja do Validation i Exceptional
6.5.4 Odejście od wyjątków?
Ćwiczenia
Podsumowanie
7. Budowanie aplikacji za pomocą funkcji
7.1 Aplikacja częściowa: dostarczanie fragmentów argumentów
7.1.1 Ręczne włączanie częściowej aplikacji
7.1.2 Uogólnianie aplikacji częściowej
7.1.3 Kolejność argumentów ma znaczenie
7.2 Pokonywanie kaprysów rozwiązywania metod
7.3 Funkcje rozwinięte: zoptymalizowane pod kątem częściowej aplikacji
7.4 Tworzenie API przyjaznego dla aplikacji częściowej
7.4.1 Typy jako dokumentacja
7.4.2 Wyszczególnianie funkcji dostępu do danych
7.5 Modularyzacja i budowanie aplikacji
7.5.1 Modułowość w programowaniu obiektowym
7.5.2 Modułowość w FP
7.5.3 Porównanie dwóch podejść
7.5.4 Budowanie aplikacji
7.6 Redukowanie listy do jednej wartości
7.6.1 Metoda Aggregate w LINQ
7.6.2 Agregowanie wyników walidacji
7.6.3 Zbieranie błędów walidacji
Ćwiczenia
Podsumowanie
8. Efektywne wykorzystywanie funkcji wieloargumentowych
8.1 Aplikacja funkcji w świecie podniesionych typów
8.1.1 Zrozumienie aplikatyw
8.1.2 Podnoszenie poziomu funkcji
8.1.3 Wprowadzenie do testowania opartego na własnościach
8.2 Funktory, aplikatywy, monady
8.3 Prawa monad
8.3.1 Prawa tożsamość
8.3.2 Lewa tożsamość
8.3.3 Łączność
8.3.4 Używanie Bind z funkcjami wieloargumentowymi
8.4 Poprawianie czytelności przez użycie LINQ z dowolną monadą
8.4.1 Korzystanie z LINQ z arbitralnymi funktorami
8.4.2 Używanie LINQ z dowolnymi monadami
8.4.3 let, where i inne klauzule LINQ
8.5 Kiedy używać Bind, a kiedy Apply
8.5.1 Walidacja za pomocą inteligentnych konstruktorów
8.5.2 Zbieranie błędów za pomocą przepływu aplikatywnego
8.5.3 Szybka porażka przy przepływie monadycznym
Ćwiczenia
Podsumowanie
9. Rozważania o funkcyjności danych
9.1 Pułapki mutacji stanu
9.2 Zrozumienie stanu, tożsamości i zmiany
9.2.1 Niektóre rzeczy nigdy się nie zmieniają
9.2.2 Reprezentowanie zmian bez mutacji
9.3 Wymuszanie niemutowalności
9.3.1 Całkowita niemutowalność
9.3.2 Metody kopiowania bez szablonowego kodu?
9.3.3 Wykorzystywanie F# do typów danych
9.3.4 Porównywanie strategii dla niemutowalności: konkurs brzydoty
9.4 Krótkie wprowadzenie do funkcyjnych struktur danych
9.4.1 Klasyczna lista wiązana w stylu funkcyjnym
9.4.2 Drzewa binarne
Ćwiczenia
Podsumowanie
10. Event sourcing: funkcyjne podejście do zapisu
10.1 Funkcyjne rozważania o przechowywaniu danych
10.1.1 Dlaczego przechowywanie danych powinno polegać wyłącznie na ich dołączaniu
10.1.2 Zrelaksujmy się i zapomnijmy o przechowywaniu stanu
10.2 Podstawy event sourcingu
10.2.1 Reprezentacja zdarzeń
10.2.2 Przechowywanie zdarzeń
10.2.3 Reprezentacja stanu
10.2.4 Przerwa na dopasowywanie do wzorca
10.2.5 Reprezentacja zmian stanu
10.2.6 Odtwarzanie bieżącego stanu na podstawie przeszłych zdarzeń
10.3 Architektura systemu opartego na zdarzeniach
10.3.1 Obsługa poleceń
10.3.2 Obsługa zdarzeń
10.3.3 Dodanie walidacji
10.3.4 Tworzenie widoków danych na podstawie zdarzeń
10.4 Porównanie różnych podejść do niemutowalnego magazynu
10.4.1 Datomic kontra Event Store
10.4.2 Jak bardzo nasza dziedzina jest sterowana zdarzeniami?
Podsumowanie
Część III Zaawansowane techniki
11. Leniwe wartościowanie, kontynuacje oraz piękno kompozycji monadycznej
11.1 Zalety leniwego wartościowania
11.1.1 Leniwe API działające z Option
11.1.2 Złożenie leniwych wartościowań
11.2 Obsługa wyjątków za pomocą Try
11.2.1 Reprezentowanie obliczeń, które mogą się nie powieść
11.2.2 Bezpieczne pobieranie informacji z obiektu JSON
11.2.3 Złożenia obliczeń, które mogą się nie udać
11.2.4 Złożenie monadyczne: co to znaczy?
11.3 Tworzenie potoku oprogramowania pośredniczącego na potrzeby dostępu do bazy danych
11.3.1 Złożenie funkcji, które inicjalizują/czyszczą
11.3.2 Przepis na uniknięcie piramidy potępienia
11.3.3 Przechwycenie istoty oprogramowania pośredniczącego
11.3.4 Implementacja wzorca zapytania dla oprogramowania pośredniczącego
11.3.5 Dodawanie oprogramowania pośredniczącego, które mierzy czas działania
11.3.6 Dodawanie oprogramowania pośredniczącego zarządzającego transakcją
Podsumowanie
12. Stanowe programy i obliczenia
12.1 Programy, które zarządzają stanem
12.1.1 Utrzymywanie pamięci podręcznej uzyskanych zasobów
12.1.2 Refaktoryzacja w celu umożliwienia testowania i obsługi błędów
12.1.3 Obliczenia stanowe
12.2 Język do generowania danych losowych
12.2.1 Generowanie całkowitych liczb losowych
12.2.2 Generowanie innych wartości podstawowych
12.2.3 Generowanie bardziej złożonych struktur
12.3 Ogólny wzorzec obliczeń stanowych
Podsumowanie
13. Posługiwanie się obliczeniami asynchronicznymi
13.1 Obliczenia asynchroniczne
13.1.1 Potrzeba działania asynchronicznego
13.1.2 Reprezentowanie działań asynchronicznych za pomocą Task
13.1.3 Zadanie jako kontener na przyszłą wartość
13.1.4 Obsługa błędów
13.1.5 API HTTP API dla konwersji walut
13.1.6 W razie niepowodzenia próbujmy kilka razy
13.1.7 Równoległe uruchamianie działań asynchronicznych
13.2 Funkcje przesuwne: praca z listami podniesionych wartości
13.2.1 Weryfikacja listy wartości za pomocą monadycznej funkcji Traverse
13.2.2 Zbieranie błędów walidacji za pomocą aplikatywnej funkcji Traverse
13.2.3 Zastosowanie wielu walidatorów do jednej wartości
13.2.4 Stosowanie Traverse z Task, jeśli możliwych jest wiele wyników
13.2.5 Definiowanie Traverse dla struktur o pojedynczej wartości
13.3 Łączenie asynchroniczności i walidacji (lub dowolnych innych efektów monadycznych)
13.3.1 Problem złożenia monad
13.3.2 Zmniejszenie liczby efektów
13.3.3 Wyrażenia LINQ ze złożeniem monad
Podsumowanie
14. Strumienie danych i Reactive Extensions
14.1 Reprezentowanie strumieni danych za pomocą IObservable
14.1.1 Ciąg wartości w czasie
14.1.2 Subskrypcja IObservable
14.2 Tworzenie IObservables
14.2.1 Tworzenie zegara
14.2.2 Używanie Subject, aby poinformować IObservable, kiedy ma wysyłać sygnał
14.2.3 Tworzenie IObservables na podstawie subskrypcji opartych na wywołaniach zwrotnych
14.2.4 Tworzenie IObservables z prostszych struktur
14.3 Przekształcanie i łączenie strumieni danych
14.3.1 Przekształcenia strumienia
14.3.2 Łączenie i podział strumieni
14.3.3 Obsługa błędów w IObservable
14.3.4 Składamy wszystko razem
14.4 Implementacja kodu, który łączy wiele zdarzeń
14.4.1 Wykrywanie ciągów naciśnięć klawiszy
14.4.2 Reagowanie na wiele źródeł zdarzeń
14.4.3 Powiadamianie, kiedy na koncie pojawia się debet
14.5 Kiedy należy stosować IObservable?
Podsumowanie
15. Wprowadzenie do współbieżności z przesyłaniem komunikatów
15.1 Zapotrzebowanie na współdzielony stan mutowalny
15.2 Zrozumienie współbieżności z przesyłaniem komunikatów
15.2.1 Implementowanie agentów w C#
15.2.2 Pierwsze kroki z agentami
15.2.3 Użycie agentów do obsługi równoczesnych żądań
15.2.4 Agenty kontra aktory
15.3 Funkcyjne API, implementacje oparte na agentach
15.3.1 Agenty jako szczegóły implementacji
15.3.2 Ukrywanie agentów za tradycyjnym API
15.4 Współbieżność z przesyłaniem komunikatów w aplikacjach LOB
15.4.1 Korzystanie z agenta do synchronizacji dostępu do danych konta
15.4.2 Przechowywanie rejestru kont
15.4.3 Agent nie jest obiektem
15.4.4 Łączenie wszystkiego ze sobą
Podsumowanie
Epilog: co dalej?