TypeScript Academy · Урок

Облегчённый шаблон HKT

Реализуйте приём дефункционализации Kind/URI

Урок 3 из 413 шагов

«Облегчённый шаблон HKT» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 4 уроков всего.

Дефункционализация

Приём, лежащий в основе кодирования, имеет название: дефункционализация. Вместо настоящей типовой функции высшего порядка мы представляем каждую функцию тегом и разрешаем её с помощью одного поиска первого порядка. Реестр URItoKind и есть такая таблица поиска.

type Kind<F extends URIS, A> = URItoKind<A>[F];
// One lookup replaces true type-function application

Тип Kind

Kind<F, A> — это публичное представление шаблона. Читайте его как «конструктор F, применённый к A». Он скрывает индексацию реестра за понятным именем.

type A = Kind<"Array", number>;  // number[]
type B = Kind<"Option", string>; // string | null

Ограничение F множеством URIS

F должен быть зарегистрированным URI. Ограничьте его с помощью extends URIS, чтобы можно было использовать только допустимые теги, а опечатки превращались в ошибки компиляции.

type Kind<F extends URIS, A> = URItoKind<A>[F];

// Kind<"Arrey", number> -> error, not a valid URI

Написание универсальных сигнатур

Теперь мы можем записать ранее невозможную сигнатуру Functor. Замените каждое F<A> на Kind<F, A>, и компилятор примет её.

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

Разрешение на практике

Когда вы фиксируете F конкретным URI, Kind разрешается в настоящий тип контейнера. У Functor<"Array"> есть map, преобразующий A[] в B[].

type ArrayFunctor = Functor<"Array">;
// map: <A, B>(fa: A[], f: (a: A) => B) => B[]

Несколько аргументов типа

Некоторые конструкторы принимают два аргумента, например Either<E, A>. fp-ts расширяет этот шаблон с помощью URItoKind2 и Kind2<F, E, A> для вида * -> * -> *. Идея масштабируется добавлением новых реестров.

interface URItoKind2<E, A> {
  Either: { left: E } | { right: A };
}
type Kind2<F extends URIS2, E, A> = URItoKind2<E, A>[F];

Соглашение fp-ts

В fp-ts закреплено соглашение, которому следует весь этот курс: отдельная строка URI для каждого типа данных, реестр URItoKind, расширяемый слиянием объявлений, и Kind для разрешения. Распознавая это соглашение, вы сможете читать сигнатуры типов в fp-ts и Effect.

// In fp-ts you will see exactly:
// declare module "fp-ts/HKT" { interface URItoKind<A> { ... } }

Запись экземпляра

«Экземпляр» абстракции — это значение, реализующее её для конкретного URI. Экземпляр содержит конкретный map. В следующем уроке мы напишем экземпляр для массива, а здесь приведём его форму.

declare const arrayFunctor: Functor<"Array">;
const out = arrayFunctor.map([1, 2, 3], n => n * 2); // number[]

Почему он «лёгкий»

Его называют шаблоном лёгкого HKT, потому что он не требует изменений компилятора или специального синтаксиса — нужны только интерфейсы и доступ по индексу. В обмен на настоящее обобщение кода по контейнерам приходится добавить немного служебных конструкций: URI и реестры.

type Kind<F extends URIS, A> = URItoKind<A>[F];
// No magic, just a typed lookup table

Ограничения

Шаблон многословен, а сообщения об ошибках могут быть трудными для понимания, поскольку в них упоминается кодирование, а не предметная область. Кроме того, каждый контейнер необходимо зарегистрировать. В большинстве приложений вы используете библиотеки, где это уже сделано, вместо того чтобы создавать всё самостоятельно.

// Trade-off: real abstraction power vs. encoding ceremony

Собираем всё вместе

Полный рецепт лёгкого HKT: определить URI, зарегистрировать его в URItoKind, определить абстракции с помощью Kind<F, A> и предоставить экземпляры для каждого URI. Это весь набор инструментов, лежащий в основе универсальных функциональных библиотек для TypeScript.

type Kind<F extends URIS, A> = URItoKind<A>[F];
interface Functor<F extends URIS> {
  map<A, B>(fa: Kind<F, A>, f: (a: A) => B): Kind<F, B>;
}

Быстрая проверка

Проверьте, насколько хорошо вы понимаете шаблон лёгкого HKT.

Повторение

Вы собрали шаблон лёгкого HKT.

  • Дефункционализация заменяет применение типовой функции поиском.
  • Kind<F, A> разрешает URI и аргумент в конкретный тип.
  • В универсальных сигнатурах используется Kind вместо F<A>.
  • Шаблон поддерживает два аргумента через Kind2 и соответствует соглашению fp-ts.

Далее: универсальный map с настоящими экземплярами.

Можно начать бесплатно

Изучай TypeScript с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
101
Уроки
352

Часто задаваемые вопросы

Урок «Облегчённый шаблон HKT» бесплатный?

Да — полный текст урока «Облегчённый шаблон HKT» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 4 уроков всего.

Чему я научусь в уроке «Облегчённый шаблон HKT»?

Реализуйте приём дефункционализации Kind/URI Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать TypeScript Academy?

Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Облегчённый шаблон HKT»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке TypeScript Academy?

Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Проблема HKT в TypeScript
  2. Определение конструкторов типов
  3. Облегчённый шаблон HKT
  4. Обобщённые функторы и мапперы
← Назад к TypeScript Academy