Scala for Backend Engineering & Functional Programming · บทเรียน

มาโครเชิงปฏิบัติ

กรณีใช้งาน

บทเรียน 4 จาก 413 ขั้นตอน

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

แมโครในโลกจริง

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

กรณีใช้งาน: assert พร้อมข้อความ

แมโครสามารถเก็บ ข้อความต้นฉบับ ของเงื่อนไขที่ไม่ผ่าน เพื่อนำไปสร้างข้อความของการยืนยันที่ช่วยอธิบายปัญหาได้โดยอัตโนมัติ

import scala.quoted.*

inline def myAssert(inline cond: Boolean): Unit =
  ${ assertImpl('cond) }

การเก็บซอร์สโค้ด

ภายในแมโคร cond.show (ผ่าน API ของ reflect) จะคืนค่าซอร์สจริงของนิพจน์ ซึ่งคุณสามารถ splice เข้าไปในข้อความข้อผิดพลาดได้

import scala.quoted.*

def assertImpl(cond: Expr[Boolean])(using q: Quotes): Expr[Unit] = {
  import q.reflect.*
  val src = Expr(cond.asTerm.show)
  '{ if (!${ cond }) throw new AssertionError("failed: " + ${ src }) }
}

กรณีใช้งาน: การตรวจสอบขณะคอมไพล์

ตรวจสอบลิเทอรัล เช่น นิพจน์ประจำหรือหมายเลขพอร์ต ขณะคอมไพล์ เพื่อไม่ให้ค่าที่ไม่ถูกต้องไปถึงช่วงรันไทม์

import scala.quoted.*

inline def port(inline n: Int): Int = ${ portImpl('n) }

ยุติการทำงานเมื่ออินพุตไม่ถูกต้อง

ดึงค่าลิเทอรัลออกมา ตรวจสอบค่า แล้วเรียก report.errorAndAbort เมื่อค่าไม่ถูกต้อง วิธีนี้เปลี่ยนข้อผิดพลาดขณะรันไทม์ให้เป็นข้อผิดพลาดขณะคอมไพล์

import scala.quoted.*

def portImpl(n: Expr[Int])(using q: Quotes): Expr[Int] = {
  import q.reflect.*
  val v = n.valueOrAbort
  if (v < 1 || v > 65535) report.errorAndAbort("invalid port")
  n
}

กรณีใช้งาน: การสร้างคลาสชนิด

แมโคร (มักใช้ร่วมกับ inline และ scala.deriving.Mirror) สามารถสร้างอินสแตนซ์ของคลาสชนิด เช่น ตัวเข้ารหัส JSON สำหรับคลาสแบบกรณีใด ๆ โดยแทบไม่ต้องเขียนโค้ดประกอบ

import scala.deriving.Mirror

trait Show[T] { def show(t: T): String }

inline def derived[T](using Mirror.Of[T]): Show[T] = ???

การใช้ Mirror

Mirror เปิดเผยชนิดและป้ายกำกับของฟิลด์ในผลิตภัณฑ์ขณะคอมไพล์ เมื่อใช้ร่วมกับ inline คุณจะวนซ้ำผ่านฟิลด์ต่าง ๆ เพื่อสร้างอินสแตนซ์ได้

import scala.deriving.*
import scala.compiletime.*

inline def labels[T](using m: Mirror.ProductOf[T]): List[String] =
  constValueTuple[m.MirroredElemLabels].toList.map(_.toString)

กรณีใช้งาน: การบันทึกข้อมูลพร้อมตำแหน่ง

แมโครสามารถอ่าน Position จาก API ของ reflect เพื่อแนบชื่อไฟล์และหมายเลขบรรทัดไปกับข้อความบันทึก โดยไม่เสียค่าใช้จ่ายขณะรันไทม์

import scala.quoted.*

def posImpl(using q: Quotes): Expr[String] = {
  import q.reflect.*
  val p = Position.ofMacroExpansion
  Expr(s"${p.sourceFile.name}:${p.startLine + 1}")
}

กรณีที่ไม่ควรใช้แมโคร

แมโครมีประสิทธิภาพสูง แต่ดูแลรักษาและแก้จุดบกพร่องได้ยาก ควรใช้ฟังก์ชันธรรมดา เมธอดแบบ inline หรือการสร้างโดยใช้ given ก่อน และเลือกใช้แมโครเมื่อวิธีเหล่านั้นไม่สามารถแสดงความต้องการได้เท่านั้น

การทดสอบแมโคร

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

ตัวอย่างเทียบเท่าขณะรันไทม์

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

object Main {
  def port(n: Int): Int = {
    require(n >= 1 && n <= 65535, "invalid port")
    n
  }
  def main(args: Array[String]): Unit = {
    println(port(8080)) // 8080
  }
}

ตรวจสอบความเข้าใจอย่างรวดเร็ว

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

สรุป

คุณได้สำรวจกรณีใช้งานจริงของแมโคร:

  • assert ที่เก็บซอร์ส
  • การตรวจสอบขณะคอมไพล์ด้วย errorAndAbort
  • การสร้างคลาสชนิดผ่าน Mirror
  • การบันทึกข้อมูลที่รู้ตำแหน่ง
  • เวลาที่ควรหลีกเลี่ยงแมโครและวิธีทดสอบแมโคร

คุณเรียนจบหลักสูตรเมตาโปรแกรมมิงและแมโครแล้ว

เริ่มต้นได้ฟรี

เรียนรู้ Scala ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
39
บทเรียน
143

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

บทเรียน “มาโครเชิงปฏิบัติ” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “มาโครเชิงปฏิบัติ”

กรณีใช้งาน คุณปฏิบัติ Scala for Backend Engineering & Functional Programming ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Scala for Backend Engineering & Functional Programming หรือไม่

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

บทเรียน “มาโครเชิงปฏิบัติ” ใช้เวลานานแค่ไหน

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

ฉันเขียนและรันโค้ดในบทเรียน Scala for Backend Engineering & Functional Programming นี้ได้ไหม

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

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

  1. เมธอด Inline
  2. พื้นฐานมาโคร
  3. เครื่องหมายอัญประกาศและการแทรกโค้ด
  4. มาโครเชิงปฏิบัติ
← กลับไปที่ Scala for Backend Engineering & Functional Programming