DEV Community

Gophernment
Gophernment

Posted on

Generic methods ใน Go 1.27: ฟีเจอร์ที่เคยถูกบอกว่า "ไม่มีทาง" แต่ community ผลักดันจนสำเร็จ

📅 เขียนเมื่อ: กันยายน 2026 | Go 1.27
⚠️ API/เวอร์ชันอาจเปลี่ยนแปลง — ตรวจสอบเอกสารล่าสุดก่อนใช้งาน

ปลายเดือนสิงหาคมที่ผ่านมา บัญชีทางการของ GoLand โพสต์คลิปวิดีโอสั้น ๆ ความยาว 36 วินาที ของ Robert Griesemer (หนึ่งในผู้ร่วมสร้างภาษา Go) เล่าเบื้องหลังของฟีเจอร์หนึ่ง พร้อมคำถามชวนคิดว่า:

"ทำไมทีม Go ถึงข้าม generic methods ตอน 1.18 แล้วกลับมาใส่ใน 1.27?"

คำตอบสั้น ๆ คือมันเป็นเรื่องราวยาว 5 ปี ที่เริ่มจากทีมออกแบบบอกว่า "ทำไม่ได้/ไม่น่าจะทำ" ไปจนถึงวันที่ community ผลักดันจนฟีเจอร์นี้กลายเป็นจริง — บทความนี้จะเล่าเรื่องนั้นให้ฟัง พร้อมอธิบายว่า generic methods คืออะไร และทำไมมันถึงสำคัญ

เกริ่น: Go 1.18 เปิดตัว generics แต่ทิ้งช่องว่างไว้

ย้อนไป กุมภาพันธ์ 2022 — Go 1.18 เปิดตัว generics (type parameters) ซึ่งถือเป็นการเปลี่ยนแปลงภาษา Go ครั้งใหญ่ที่สุดครั้งหนึ่ง หลังรอคอยกันมานานหลายปี

แต่ในดีไซน์นั้นมีข้อจำกัดหนึ่งที่หลายคนสังเกตเห็นทันที: method ประกาศ type parameters ของตัวเองไม่ได้

แปลง่าย ๆ ว่า generics ตอน 1.18 ใช้ได้กับ

  • ฟังก์ชันระดับ package: func Max[T cmp.Ordered](a, b T) T
  • และ type: type List[T any] struct { ... }

แต่ใช้กับ method ไม่ได้ — method ที่ผูกกับ receiver จะรับ type parameters เพิ่มเองไม่ได้ ต้องใช้ type parameters ของ type ต้นทางเท่านั้น

Generic methods คืออะไร

ขอทำให้เห็นภาพด้วยตัวอย่างคลาสสิก — สมมติมี List[T] แล้วอยากได้ method .Map ที่แปลง element จาก type T เป็น type U

ก่อน Go 1.27 เราทำแบบนี้ได้อย่างเดียว (เป็นฟังก์ชันระดับ package):

func Map[T, U any](l *List[T], f func(T) U) *List[U] {
    result := &List[U]{}
    for _, item := range l.items {
        result.items = append(result.items, f(item))
    }
    return result
}

// ใช้แบบนี้ — อ่านแล้วต้องไล่จากขวาไปซ้าย
mapped := Map(list, func(n int) string { return strconv.Itoa(n) })
Enter fullscreen mode Exit fullscreen mode

ส่วนแบบที่หลายคนอยากได้ — เรียกเป็น method ต่อท้ายแบบ chain อย่างนี้ — เขียนไม่ได้:

// ❌ ก่อน Go 1.27: method ประกาศ type parameter U เองไม่ได้
func (l *List[T]) Map[U any](f func(T) U) *List[U] { ... }
Enter fullscreen mode Exit fullscreen mode

Go 1.27 ทำให้แบบหลังนี้เป็นจริงแล้ว — method ประกาศ type parameters ของตัวเองได้ ซึ่งทำให้เขียน DSL และ chain ของ method ได้สะอาดขึ้นมาก (ตัวอย่างจริงใน standard library คือ math/rand/v2 ที่เปลี่ยนจาก generic function N[Int intType](Int) Int มาเป็น generic method (*Rand).N[Int intType](Int) Int)

ทำไมตอน 1.18 ถึงข้ามไป

เหตุผลหลักคือความซับซ้อนในการ implement ไม่ใช่ "ไม่อยากทำ"

  • เรื่อง dictionary — Go implement generics ด้วยการส่ง "type dictionary" ตามไปกับค่า การให้ method มี type parameters เอง แปลว่า dictionary ต้องถูกส่งผ่าน method value และ interface ซึ่งซับซ้อนกว่าฟังก์ชันธรรมดามาก
  • คำถามเรื่อง interface — ข้อถกเถียงที่ค้างมานาน: ถ้า method มี type parameters แล้วมันจะ "implement interface" ได้ไหม? ถ้าได้ จะตรวจ type ยังไง? ถ้าไม่ได้ แล้วทำไมไม่ใช้ฟังก์ชันธรรมดาแทน?

ประเด็นนี้ชัดเจนจากคอมเมนต์ของ Ian Lance Taylor (ผู้ร่วมออกแบบ generics) ใน proposal วันที่ 21 ตุลาคม 2021:

"This proposal is a non-starter unless someone can explain how to implement it."

และที่น่าจำคือตอนนั้น Go FAQ ถึงกับเขียนไว้ว่า:

"We do not anticipate that Go will ever add generic methods."

แล้ว community เปลี่ยนเกมยังไง

แต่สิ่งที่ทีมออกแบบประเมินไว้ต่ำไป คือแรงกดดันจากคนใช้งาน

proposal ตัวนี้ (golang/go #49085) ถูกเปิดไว้ตั้งแต่วันที่ 20 ตุลาคม 2021 — สองสามเดือนก่อน Go 1.18 จะออกด้วยซ้ำ — และสะสมความเห็นจากชุมชนไปเรื่อย ๆ จนมี 👍 กว่า 906 ครั้ง (ตัวเลขที่สูงมากสำหรับ proposal ภาษา Go)

use case ที่คนเอามาโชว์กันส่วนใหญ่เป็นเรื่องจริงจัง ไม่ใช่แค่ "อยากได้":

  • DSL สำหรับ stream processing.Map(...).Filter(...) ต่อเป็น chain แบบที่ไลบรารีด้าน data pipeline ใช้กัน
  • DSL สำหรับ testingAssert(actual).ToBe(expected) อ่านเป็นประโยคได้
  • DSL สำหรับ mockingOn(obj.Sum).WithArgs(7, 8).ThenReturn(15)

ทั้งหมดนี้เขียนด้วย method chaining ได้สวย แต่ถ้าไม่มี generic methods จะต้องถอยกลับไปเขียนเป็นฟังก์ชันระดับ package ซึ่งอ่านย้อนหลังและอุ้ยอ้ายกว่า

เมื่อเวลาผ่านไป 5 ปี คำตอบของคำถามเชิงเทคนิคก็ค่อย ๆ ชัดขึ้น จนในที่สุด Go 1.27 (สิงหาคม 2026) ก็ประกาศรองรับ generic methods อย่างเป็นทางการ — โดยปิด proposal #49085 เดิมเป็น "duplicate" ของ issue #77273 ที่ implement จริง

นี่คือจังหวะที่ Robert Griesemer เล่าในวิดีโอ — ว่าจุดเปลี่ยนจริง ๆ คือเสียงตอบรับจาก community ที่ยืนกรานว่า use case พวกนี้ "จำเป็น" ไม่ใช่ "ของเล่น"

ข้อจำกัดที่ยังเหลือ (สำคัญ)

อย่าเพิ่งดีใจจนลืมอ่าน footnote ใน release notes — Go 1.27 ระบุชัดว่า:

  • method ของ interface ยังประกาศ type parameters ไม่ได้
  • และ generic method ไม่สามารถ ไป implement interface method ได้

แปลว่า generic methods ใช้ได้กับ concrete type (struct ฯลฯ) แต่ยังแตะเรื่อง "generic interface method" ไม่ได้ — นี่คือขอบเขตที่ทีม Go ตั้งใจจำกัดไว้ให้ออกแบบต่อได้ในอนาคต

สรุป

เรื่องนี้เป็นกรณีศึกษาที่ดีมากของวิธีที่ Go (ในฐานะภาษา open source) รับมือกับฟีเจอร์ที่ขอเข้ามามาก:

  1. ตอน 1.18 ทีมออกแบบตัด generic methods ออกอย่างจงใจ เพราะ implement ไม่ลงตัวและคำถามเรื่อง interface ยังค้าง
  2. ชุมชนเปิด proposal (#49085) ยื่น use case จริง และกด 👍 กันเป็นพัน
  3. ทีมตั้งท่าไว้ก่อน ("non-starter", FAQ บอก "ไม่มีทาง") แต่ก็ไม่ได้ปิดประตูถาวร
  4. 5 ปีต่อมา ปมเชิงเทคนิคถูกแก้ได้จริง → Go 1.27 ใส่ generic methods เข้ามา

ข้อคิดสำหรับคนเขียน Go: ฟีเจอร์ที่ทีมเคยบอกว่า "ไม่น่ามี" ก็มีวันเป็นจริงได้ ถ้า use case มัน "จริง" พอ — และตอนนี้ generic methods ก็พร้อมให้ใช้แล้วใน Go 1.27


แหล่งอ้างอิง

Top comments (0)