Il pattern HKT leggero
Implementi il trucco di defunzionalizzazione Kind/URI.
Il pattern HKT leggero è una lezione TypeScript Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento TypeScript Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso TypeScript Academy include 4 lezioni in totale.
Defunzionalizzazione
Il meccanismo alla base della codifica ha un nome: defunzionalizzazione. Invece di usare una vera funzione di tipo di ordine superiore, rappresentiamo ogni funzione tramite un tag e la risolviamo con un'unica ricerca di primo ordine. Il registro URItoKind è proprio questa tabella di ricerca.
type Kind<F extends URIS, A> = URItoKind<A>[F];
// One lookup replaces true type-function applicationIl tipo Kind
Kind<F, A> è l'interfaccia pubblica del modello. Lo si può leggere come "il costruttore F applicato a A". Nasconde l'indicizzazione del registro dietro un nome chiaro.
type A = Kind<"Array", number>; // number[]
type B = Kind<"Option", string>; // string | nullVincolare F a URIS
F deve essere un URI registrato. Lo si vincola con extends URIS, così è possibile usare solo tag validi e gli errori di battitura diventano errori di compilazione.
type Kind<F extends URIS, A> = URItoKind<A>[F];
// Kind<"Arrey", number> -> error, not a valid URIScrivere firme generiche
Ora è possibile scrivere la firma di Functor che prima era impossibile. Sostituendo ogni F<A> con Kind<F, A>, il compilatore la accetta.
interface Functor<F extends URIS> {
map<A, B>(fa: Kind<F, A>, f: (a: A) => B): Kind<F, B>;
}La risoluzione in azione
Quando si fissa F a un URI concreto, Kind viene risolto nel tipo reale del contenitore. Un Functor<"Array"> ha un map da A[] a B[].
type ArrayFunctor = Functor<"Array">;
// map: <A, B>(fa: A[], f: (a: A) => B) => B[]Più argomenti di tipo
Alcuni costruttori accettano due argomenti, come Either<E, A>. fp-ts estende il modello con URItoKind2 e Kind2<F, E, A> per il kind * -> * -> *. L'idea si estende aggiungendo altri registri.
interface URItoKind2<E, A> {
Either: { left: E } | { right: A };
}
type Kind2<F extends URIS2, E, A> = URItoKind2<E, A>[F];La convenzione di fp-ts
fp-ts ha stabilito la convenzione seguita in tutto questo corso: una stringa URI per ogni tipo di dato, un registro URItoKind esteso tramite la fusione delle dichiarazioni e Kind per la risoluzione. Riconoscerla permette di leggere le firme di tipo di fp-ts ed Effect.
// In fp-ts you will see exactly:
// declare module "fp-ts/HKT" { interface URItoKind<A> { ... } }Un record di istanza
Un'"istanza" di un'astrazione è un valore che la implementa per uno specifico URI. L'istanza contiene il map concreto. Nella prossima lezione scriveremo l'istanza di Array; qui ne vediamo la struttura.
declare const arrayFunctor: Functor<"Array">;
const out = arrayFunctor.map([1, 2, 3], n => n * 2); // number[]Perché è "leggero"
Si parla di modello HKT leggero perché non richiede modifiche al compilatore né una sintassi speciale, ma solo interfacce e accessi indicizzati. Il costo è un po' di verbosità (URI e registri), in cambio di un codice realmente generico rispetto ai contenitori.
type Kind<F extends URIS, A> = URItoKind<A>[F];
// No magic, just a typed lookup tableLimitazioni
Il modello è verboso e i messaggi di errore possono essere criptici, perché fanno riferimento alla codifica invece che al dominio dell'applicazione. Inoltre, ogni contenitore deve essere registrato. Nella maggior parte delle applicazioni si usano librerie che lo fanno già, invece di costruirlo autonomamente.
// Trade-off: real abstraction power vs. encoding ceremonyMettere insieme i pezzi
Lo schema completo dell'HKT leggero è il seguente: definire un URI, registrarlo in URItoKind, definire le astrazioni usando Kind<F, A> e fornire istanze per ogni URI. Questo è l'intero insieme di strumenti alla base delle librerie funzionali generiche in 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>;
}Verifica rapida
Verifichi la sua comprensione del modello HKT leggero.
Riepilogo
Ha assemblato il modello HKT leggero.
- La defunzionalizzazione sostituisce l'applicazione di funzioni di tipo con una ricerca.
Kind<F, A>risolve un URI più un argomento in un tipo concreto.- Le firme generiche usano
Kindal posto diF<A>. - Il modello si estende a due argomenti tramite
Kind2ed è la convenzione di fp-ts.
Prossimo argomento: un map generico con istanze reali.
Domande Frequenti
La lezione «Il pattern HKT leggero» è gratuita?
Sì — il testo completo di «Il pattern HKT leggero» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso TypeScript Academy, passa a CoddyKit PRO. Il corso TypeScript Academy include 4 lezioni in totale.
Cosa imparerò in «Il pattern HKT leggero»?
Implementi il trucco di defunzionalizzazione Kind/URI. Eserciti TypeScript Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare TypeScript Academy?
Non è richiesta alcuna esperienza precedente. TypeScript Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.
Quanto tempo richiede la lezione «Il pattern HKT leggero»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione TypeScript Academy?
Sì. Ogni lezione TypeScript Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Il problema HKT in TypeScript
- Definire costruttori di tipi
- Il pattern HKT leggero
- Functor e mapper generici