TGViewer
Khraks dev Khraks dev @khraks_dev · 108 subscribers
Post #52 104
Итак HKT на основе TypeLambda

interface TypeLambda {
readonly Arg1: unknown;
readonly Arg2: unknown;
readonly Arg3: unknown;
readonly Return: unknown;
}

type Call<TL extends TypeLambda, A1, A2, A3> = (TL & {
readonly Arg1: A1;
readonly Arg2: A2;
readonly Arg3: A3;
})["Return"];

interface MyEffect<A, E, R> {
(r: R): Promise<A | E>;
}

interface MyEffectLambda extends TypeLambda {
Return: MyEffect<this["Arg1"], this["Arg2"], this["Arg3"]>;
}

type Result = Call<MyEffectLambda, 1, 2, 3>;
// ^? MyEffect<1, 2, 3>

Реализация очень простая:
Основной трюк - это полиморфный this
Благодаря ему MyLambda и выглядит как самостоятельная функция - взять переданные аргументы и как-то их заколбасить. В Call<...> видно, что мы передаем лямбду и типо-аргументы, далее пересекаем TL & {...Args} (это и есть момент передачи аргументов) - после чего polymorphic this начинает видеть конкретные типы. Наконец, просто забираем результат через ['Return'] - очень просто и изящно.
Так же в отличие от предыдущего примера это легче масштабируется - нужен еще один параметр? просто добавьте Arg4 в TypeLambda и существующие лямбды не надо исправлять - Arg4 в них просто игнорируется
Что на счет разной разрядности типов - метод Call<> выглядит одинаково как для Array<T> так и для Either<A, E>

interface ArrayLambda extends TypeLambda {
Return: Array<this['Arg1']>
}
interface EitherLambda extends TypeLambda {
Return: Either<this['Arg1'], this['Arg2']>
}

type ArrayResult = Call<ArrayLambda, number, void, void>
// ^? Array<number>
type EitherResult = Call<EitherLambda, number, string, void>
// ^? Either<number, string>

Ещё есть такое понятие, как натуральная трансформация. Это когда мы хотим заменить один функтор на другой, не изменяя его содержимое. Например, преобразовать Either<A, E> в Option<A>, сохранив тот же A.

interface OptionLambda extends TypeLambda {
Return: Option<this["Arg1"]>;
}

type NaturalTransformation<F extends TypeLambda, G extends TypeLambda> = <A1, A2, A3>(fa: Call<F, A1, A2, A3>) => Call<G, A1, A2, A3>;

type nt = NaturalTransformation<MyEffectLambda, OptionLambda>;
// ^? <A1, A2, A3>(fa: MyEffect<A1, A2, A3>) => Option<A1>

Вот так она просто выражается в энкодинге на TypeLambda - в случае с дефункционализацией пришлось бы городить все возможные перегрузки чтобы сочетать (`URI2<A, B>` и URI<A> и URI3<A, B, C>`) собсна вот в `fp-ts https://gcanti.github.io/fp-ts/modules/NaturalTransformation.ts.html

https://tsplay.dev/Nl4PrN
fp-ts NaturalTransformation.ts Functional programming in TypeScript
  • 👍 2
  • 🔥 2
More from @khraks_dev
  1. Sep 10, 2026Добавил еще PR с Context.Mixin, чтобы подмешивать к классу поведение Context.Service Тепер…
  2. Sep 10, 2026Post #56
  3. Sep 7, 2026Post #55
  4. Sep 3, 2026Post #54
  5. Aug 30, 2026# HKT Дефункционализация В TypeScript нет настоящих Higher-Kinded Types (HKT). А они приме…
  6. Mar 8, 2026Combiner & Reducer В v4 добавили новые модули которые являются адаптированными Semigroup &…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →