TypeScript Academy · Lekcja

Generyczne funktory i mapery

Pisz funkcje map ogólne dla dowolnego kontenera

Lekcja 4 z 413 kroki

Generyczne funktory i mapery to bezpłatna lekcja TypeScript Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej TypeScript Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs TypeScript Academy zawiera 4 lekcji w sumie.

Generyczny funktor

Teraz wykorzystamy kodowanie zgodnie z jego przeznaczeniem: utworzymy generyczne map, które działa na każdym zarejestrowanym kontenerze. Interfejs Functor, sparametryzowany przez URI, deklaruje jedną metodę.

interface Functor<F extends URIS> {
  readonly URI: F;
  map<A, B>(fa: Kind<F, A>, f: (a: A) => B): Kind<F, B>;
}

Instancja Array

Instancja implementuje map dla jednego URI. Instancja Array mapuje elementy za pomocą wbudowanej metody tablicowej. Jej Kind<"Array", A> rozstrzyga się do A[].

const arrayFunctor: Functor<"Array"> = {
  URI: "Array",
  map: (fa, f) => fa.map(f)
};

Używanie instancji Array

Wywołanie map na instancji Array przekształca każdy element, zachowując typ kontenera tablicy.

const doubled = arrayFunctor.map([1, 2, 3], n => n * 2);
// doubled: number[] -> [2, 4, 6]

Typ Option

Zdefiniujmy Option jako wartość albo null i zarejestrujmy go. Jego map stosuje funkcję tylko wtedy, gdy wartość jest obecna; w przeciwnym razie przekazuje dalej null.

interface URItoKind<A> { Option: A | null }
type Option<A> = A | null;

Instancja Option

Instancja Option zachowuje kontener opcji: funkcja jest stosowana do wartości, a null pozostaje null.

const optionFunctor: Functor<"Option"> = {
  URI: "Option",
  map: (fa, f) => (fa === null ? null : f(fa))
};

Używanie instancji Option

Ten sam kształt map działa teraz na zupełnie innym kontenerze. Obecne wartości są przekształcane, a nieobecne pozostają bez zmian.

const a = optionFunctor.map(5, n => n + 1);    // 6
const b = optionFunctor.map(null, n => n + 1); // null

Jedna abstrakcja, wiele kontenerów

Na tym polega korzyść. arrayFunctor i optionFunctor współdzielą ten sam interfejs Functor. Kod napisany dla Functor<F> działa w obu przypadkach bez zmian.

function bumpAll<F extends URIS>(
  F: Functor<F>,
  fa: Kind<F, number>
): Kind<F, number> {
  return F.map(fa, n => n + 1);
}

Wywoływanie funkcji generycznej

bumpAll nie wie i nie musi wiedzieć, jaki kontener otrzymuje. Należy przekazać instancję i dane; wynik zachowa typ kontenera.

const arr = bumpAll(arrayFunctor, [1, 2]); // number[] -> [2, 3]
const opt = bumpAll(optionFunctor, 9);     // number | null -> 10

Prawa funktora

Poprawny funktor przestrzega dwóch praw: mapowanie funkcją identycznościową niczego nie zmienia, a mapowanie dwiema funkcjami po kolei jest równoważne mapowaniu ich złożeniem. Obie przedstawione wcześniej instancje, Array i Option, spełniają te prawa.

// map(fa, x => x) === fa
// map(map(fa, f), g) === map(fa, x => g(f(x)))

Dodawanie kolejnych instancji

Ponieważ abstrakcja jest otwarta, nowe kontenery można podłączać przez zarejestrowanie URI i udostępnienie instancji. Tree, Result, Task — każdy z nich może być mapowany przez ten sam generyczny kod.

// Register URI, implement Functor<"Tree">,
// and bumpAll works on trees too - no edits to bumpAll

Dlaczego ma to znaczenie

Generyczne funktory są punktem wejścia do abstrakcji funkcyjnych w TypeScript: aplikatywów, monad i trawersali. Każda z nich jest interfejsem nad Kind<F, A> z instancjami dla poszczególnych kontenerów. Znają już Państwo mechanizm, dzięki któremu działają fp-ts i Effect.

interface Monad<F extends URIS> extends Functor<F> {
  of<A>(a: A): Kind<F, A>;
  chain<A, B>(fa: Kind<F, A>, f: (a: A) => Kind<F, B>): Kind<F, B>;
}

Szybki test

Proszę sprawdzić zrozumienie generycznych funktorów.

Podsumowanie

Napisali Państwo map działające generycznie na kontenerach.

  • Functor<F> deklaruje map działające na Kind<F, A>.
  • Instancje Array i Option implementują je dla swoich URI.
  • Generyczny kod, taki jak bumpAll, działa dla każdego zarejestrowanego kontenera.
  • Prawa funktora zapewniają poprawne zachowanie instancji, a bogatsze abstrakcje buduje się na tej podstawie.

Następnie, w kursie 24: tworzenie bezpiecznych typowo parserów.

Bezpłatny start

Ucz się TypeScript dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
101
Lekcje
352

Często zadawane pytania

Czy lekcja „Generyczne funktory i mapery” jest bezpłatna?

Tak — pełny tekst „Generyczne funktory i mapery” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu TypeScript Academy, przejdź na CoddyKit PRO. Kurs TypeScript Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Generyczne funktory i mapery”?

Pisz funkcje map ogólne dla dowolnego kontenera Ćwiczysz TypeScript Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć TypeScript Academy?

Nie wymagamy żadnego doświadczenia. TypeScript Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.

Ile czasu zajmuje lekcja „Generyczne funktory i mapery”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji TypeScript Academy?

Tak. Każda lekcja TypeScript Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Problem HKT w TypeScript
  2. Definiowanie konstruktorów typów
  3. Lekki wzorzec HKT
  4. Generyczne funktory i mapery
← Powrót do TypeScript Academy