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

ฟังก์ชันและตัวแมปแบบเจเนอริก

เขียนฟังก์ชัน map ที่ใช้ได้กับคอนเทนเนอร์ทุกชนิด

ฟังก์ชันและตัวแมปแบบเจเนอริก เป็นบทเรียน TypeScript Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน TypeScript Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 4 บทเรียน

Functor แบบทั่วไป

ตอนนี้เราจะใช้การเข้ารหัสตามจุดประสงค์ของมัน: map แบบทั่วไป ที่ทำงานกับคอนเทนเนอร์ที่ลงทะเบียนไว้ชนิดใดก็ได้ อินเทอร์เฟซ Functor ซึ่งกำหนดพารามิเตอร์ด้วยตัวระบุทรัพยากร จะประกาศเมธอดหนึ่งตัว

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

อินสแตนซ์ของอาร์เรย์

อินสแตนซ์หนึ่งตัวจะนำ map ไปใช้กับตัวระบุทรัพยากรหนึ่งตัว อินสแตนซ์ของอาร์เรย์ใช้เมธอดอาร์เรย์ในตัวเพื่อทำการแมป ชนิด Kind<"Array", A> ของมันคลี่ออกมาเป็น A[]

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

การใช้อินสแตนซ์ของอาร์เรย์

การเรียก map บนอินสแตนซ์ของอาร์เรย์จะแปลงสมาชิกแต่ละตัว โดยยังคงชนิดคอนเทนเนอร์อาร์เรย์ไว้

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

ชนิดออปชัน

กำหนดออปชันให้เป็นค่าหรือ null แล้วลงทะเบียนออปชันนั้น map จะใช้ฟังก์ชันเฉพาะเมื่อมีค่าอยู่ มิฉะนั้นจะส่งต่อ null

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

อินสแตนซ์ของออปชัน

อินสแตนซ์ของออปชันจะคงคอนเทนเนอร์ออปชันไว้: ฟังก์ชันจะถูกใช้กับค่าที่อยู่ภายใน และ null จะยังคงเป็น null

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

การใช้อินสแตนซ์ของออปชัน

ตอนนี้รูปแบบ map เดียวกันทำงานกับคอนเทนเนอร์ที่แตกต่างไปโดยสิ้นเชิง ค่าที่มีอยู่จะถูกแปลง ส่วนค่าที่ไม่มีอยู่จะคงเดิม

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

นามธรรมหนึ่งแบบ หลายคอนเทนเนอร์

นี่คือผลลัพธ์ที่ต้องการ arrayFunctor และ optionFunctor ใช้อินเทอร์เฟซ Functor เดียวกัน โค้ดที่เขียนบน Functor<F> จึงทำงานกับทั้งสองชนิดได้โดยไม่ต้องแก้ไข

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

การเรียกฟังก์ชันทั่วไป

bumpAll ไม่รู้และไม่สนใจว่าคอนเทนเนอร์ที่ได้รับคือชนิดใด เพียงส่งอินสแตนซ์กับข้อมูลเข้าไป แล้วผลลัพธ์จะคงชนิดคอนเทนเนอร์ไว้

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

กฎของ Functor

Functor ที่ถูกต้องต้องปฏิบัติตามกฎสองข้อ: การแมปฟังก์ชันเอกลักษณ์ต้องไม่เปลี่ยนแปลงอะไร และการแมปฟังก์ชันสองตัวตามลำดับต้องให้ผลเท่ากับการแมปองค์ประกอบของฟังก์ชัน ทั้งอินสแตนซ์ของอาร์เรย์และออปชันข้างต้นปฏิบัติตามกฎเหล่านี้

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

การเพิ่มอินสแตนซ์เพิ่มเติม

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

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

เหตุผลที่เรื่องนี้สำคัญ

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

ตรวจสอบความเข้าใจ

ทดสอบความเข้าใจของคุณเกี่ยวกับ Functor ทั่วไป

ทบทวน

คุณเขียน map ที่ใช้ได้กับคอนเทนเนอร์ทั่วไปแล้ว

  • Functor<F> ประกาศ map เหนือ Kind<F, A>
  • อินสแตนซ์ของอาร์เรย์และออปชันนำไปใช้กับตัวระบุทรัพยากรของตน
  • โค้ดทั่วไปอย่าง bumpAll ทำงานกับคอนเทนเนอร์ที่ลงทะเบียนไว้ชนิดใดก็ได้
  • กฎของ Functor ช่วยให้อินสแตนซ์ทำงานได้อย่างถูกต้อง นามธรรมที่มีความสามารถมากขึ้นจะต่อยอดจากสิ่งนี้

บทเรียนที่ 24 ถัดไป: การสร้างตัวแยกวิเคราะห์ที่ปลอดภัยด้านชนิดข้อมูล

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

บทเรียน “ฟังก์ชันและตัวแมปแบบเจเนอริก” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “ฟังก์ชันและตัวแมปแบบเจเนอริก”

เขียนฟังก์ชัน map ที่ใช้ได้กับคอนเทนเนอร์ทุกชนิด คุณปฏิบัติ TypeScript Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน TypeScript Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน TypeScript Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “ฟังก์ชันและตัวแมปแบบเจเนอริก” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน TypeScript Academy นี้ได้ไหม

ได้ บทเรียน TypeScript Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ปัญหา HKT ใน TypeScript
  2. การกำหนดตัวสร้างชนิดข้อมูล
  3. รูปแบบ HKT แบบน้ำหนักเบา
  4. ฟังก์ชันและตัวแมปแบบเจเนอริก
← กลับไปที่ TypeScript Academy