0Pricing
TypeScript Academy · บทเรียน

รูปแบบ HKT แบบน้ำหนักเบา

ใช้งานเทคนิคแปลง Kind/URI ให้เป็นฟังก์ชัน

รูปแบบ HKT แบบน้ำหนักเบา เป็นบทเรียน TypeScript Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 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 ต้องเป็นตัวระบุทรัพยากรที่ลงทะเบียนไว้ คุณสามารถจำกัดมันด้วย 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 ให้เป็นตัวระบุทรัพยากรที่เป็นรูปธรรม 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> { ... } }

ระเบียนอินสแตนซ์

"อินสแตนซ์" ของนามธรรมคือค่าที่นำไปใช้งานกับตัวระบุทรัพยากรเฉพาะตัวหนึ่ง อินสแตนซ์นั้นเก็บ map ที่เป็นรูปธรรมไว้ เราจะเขียนอินสแตนซ์ของอาร์เรย์ในบทเรียนถัดไป ส่วนนี้จะแสดงโครงสร้างของมัน

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

เหตุผลที่เรียกว่า "น้ำหนักเบา"

รูปแบบนี้เรียกว่า HKT แบบน้ำหนักเบา เพราะไม่ต้องแก้ไขคอมไพเลอร์และไม่ต้องมีไวยากรณ์พิเศษ ใช้เพียงอินเทอร์เฟซกับการเข้าถึงด้วยดัชนีเท่านั้น ต้นทุนคือมีขั้นตอนประกอบเพิ่มเล็กน้อย (ตัวระบุทรัพยากรและรีจิสทรี) แลกกับโค้ดที่เป็นทั่วไปเหนือคอนเทนเนอร์อย่างแท้จริง

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 แบบน้ำหนักเบาคือ: กำหนดตัวระบุทรัพยากร ลงทะเบียนใน URItoKind กำหนดนามธรรมโดยใช้ Kind<F, A> และจัดเตรียมอินสแตนซ์สำหรับตัวระบุทรัพยากรแต่ละตัว นี่คือชุดเครื่องมือทั้งหมดที่อยู่เบื้องหลังไลบรารีเชิงฟังก์ชันทั่วไปใน 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> คลี่ตัวระบุทรัพยากรกับอาร์กิวเมนต์ออกมาเป็นชนิดรูปธรรม
  • ลายเซ็นทั่วไปใช้ Kind แทน F<A>
  • รูปแบบนี้ขยายไปสู่อาร์กิวเมนต์สองตัวผ่าน Kind2 และเป็นแบบแผนของ fp-ts

ถัดไป: map ทั่วไปที่มีอินสแตนซ์จริง

คำถามที่พบบ่อย

บทเรียน “รูปแบบ HKT แบบน้ำหนักเบา” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “รูปแบบ HKT แบบน้ำหนักเบา” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส TypeScript Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “รูปแบบ HKT แบบน้ำหนักเบา”

ใช้งานเทคนิคแปลง Kind/URI ให้เป็นฟังก์ชัน คุณปฏิบัติ TypeScript Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 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