Belajar Scala - Pattern Matching & ADT
Episode 7 of 23

Belajar Scala - Pattern Matching & ADT

Menguasai match expression di Scala 3 — wildcard, guards, dan destructuring — lalu memodelkan domain dengan Algebraic Data Type memakai enum dan case class, plus perbandingannya dengan sealed trait gaya Scala 2.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 6 kalian mahir memanipulasi collections, sekarang kita memakai senjata paling khas Scala: pattern matching. Jika map/filter adalah kata kerja transformasi, maka match adalah mekanisme pengambilan keputusan — tetapi jauh lebih kuat daripada if/else atau switch di bahasa lain.

Mengapa episode ini penting? Karena pattern matching adalah jembatan antara type system dan logika bisnis. Kalian sudah punya tipe (Option, Either, enum); sekarang saatnya belajar "membuka" tipe tersebut secara aman dan ekspresif. Bersama ADT, ini adalah pola yang paling sering membedakan kode Scala yang bagus dari yang sekadar bisa berjalan.

Dasar Match Expression

match di Scala 3 adalah ekspresi — ia menghasilkan nilai, bukan sekadar percabangan:

Match dasar
def describe(n: Int): String = n match
  case 0      => "nol"
  case 1      => "satu"
  case 2      => "dua"
  case _      => "angka lain"

Perhatikan: hasil match langsung menjadi nilai return. Setiap case memeriksa pola; pola _ adalah wildcard yang mencocokkan apa saja.

Pattern Matching pada Nilai Sebarang

match bekerja pada ekspresi apa pun, bukan hanya angka:

Match pada tipe berbeda
def score(s: Any): String = s match
  case i: Int    => s"int $i"
  case s: String => s"string $s"
  case _         => "lainnya"

Ini type test ala Scala — mirip instanceof, tetapi dengan pengaman tipe di dalam setiap cabang.

Wildcard dan Guards

Wildcard: Case Lainnya

Wildcard _ menangkap semua yang tidak cocok. Tempatkan di akhir, karena pola dievaluasi berurutan:

Wildcard di akhir
def classify(n: Int): String = n match
  case 0             => "nol"
  case n if n < 0    => "negatif"
  case _             => "positif"

Guards: Kondisi di Dalam Pola

Guard adalah if yang menempel pada pola — pola hanya cocok jika kondisinya terpenuhi:

Guards menambah syarat
def ageGroup(age: Int): String = age match
  case a if a < 13  => "anak-anak"
  case a if a < 20  => "remaja"
  case a if a < 60  => "dewasa"
  case _            => "lansia"

Guard sangat berguna untuk rentang nilai, sementara pola murni lebih cocok untuk nilai/tipe spesifik.

Binding dan Destructuring

Pola bisa mengikat nilai dan membongkar struktur:

Destructuring tuple
def describePerson(p: (String, Int)): String = p match
  case (name, age) if age >= 17 => s"$name sudah dewasa"
  case (name, age)              => s"$name masih muda ($age)"
Destructuring case class
case class Point(x: Int, y: Int)
 
def quadrant(p: Point): String = p match
  case Point(0, 0)      => "pusat"
  case Point(x, y) if x > 0 && y > 0 => "kanan-atas"
  case Point(_, _)      => "kuadran lain"

Compiler memeriksa ekshaustivitas: jika kalian melupakan satu pola (misal Point(0, 0)), Scala memberi peringatan match may not be exhaustive — penjaga yang jarang ada di bahasa lain.

Mengolah Option dan Either dengan Match

Pattern matching adalah cara idiomatis membuka container seperti Option:

Membuka Option dengan aman
def greetUser(maybeName: Option[String]): String = maybeName match
  case Some(name) => s"Hello, $name!"
  case None       => "Hello, tamu!"
Membuka Either
def handleResult(r: Either[String, Int]): String = r match
  case Right(v) => s"sukses: $v"
  case Left(e)  => s"gagal: $e"

Pola Some(x) dan Right(v) "membongkar" isi container. Perhatikan bahwa kalian wajib menangani None/Left — compiler memaksa kalian berpikir tentang semua kemungkinan.

ADT: Algebraic Data Type

ADT adalah teknik memodelkan domain sebagai "sebuah nilai adalah salah satu dari sekumpulan bentuk yang mungkin". Dua bahan dasarnya:

  • Sum type (pilihan): nilai adalah A atau B — diwakili enum di Scala 3.
  • Product type (kombinasi): nilai adalah A dan B — diwakili case class.

Modeling Domain dengan enum + case class

Contoh klasik: memodelkan pembayaran di e-commerce.

ADT untuk domain pembayaran
enum Payment:
  case Cash
  case Card(brand: String, last4: String)
  case Transfer(bank: String, ref: String)
Pemrosesan dengan match ekshaustif
def process(p: Payment): String = p match
  case Payment.Cash                 => "bayar tunai di kasir"
  case Payment.Card(brand, last4)   => s"kartu $brand ••••$last4"
  case Payment.Transfer(bank, ref)  => s"transfer $bank ref $ref"

Jika nanti kalian menambah Payment.QrCode, compiler langsung berteriak di semua match yang lupa menangani varian baru. Inilah kekuatan ADT: perubahan domain tercermin sebagai error kompilasi, bukan bug runtime.

sealed trait: Gaya Scala 2

Sebelum Scala 3, ADT dibangun dengan sealed trait + case class:

ADT gaya Scala 2
sealed trait Shape
case class Circle(r: Double) extends Shape
case class Rect(w: Double, h: Double) extends Shape

sealed membatasi extension hanya di file yang sama, sehingga compiler tahu semua kemungkinan dan bisa memeriksa ekshaustivitas. Gaya ini masih banyak ditemui di codebase produksi, dan sekarang bisa ditulis lebih ringkas sebagai enum:

ADT gaya Scala 3
enum Shape:
  case Circle(r: Double)
  case Rect(w: Double, h: Double)
Area via match
def area(s: Shape): Double = s match
  case Shape.Circle(r)   => Math.PI * r * r
  case Shape.Rect(w, h)  => w * h

Note

Kapan memakai enum vs sealed trait? Untuk ADT baru, gunakan enum — sintaks Scala 3 yang lebih ringkas dan otomatis menyediakan method standar. sealed trait tetap relevan saat membaca/migrasi kode lama atau memerlukan perilaku inheritance yang rumit.

Common Pitfalls

  • Urutan pola salah — pola spesifik harus lebih dulu; wildcard di akhir. Pola yang lebih umum menelan yang spesifik.
  • Guard vs pola nilai — jangan menaruh logika rumit di guard jika bisa dibentuk sebagai ADT; makin banyak kemungkinan di tipe, makin kuat pemeriksaan compiler.
  • case _ berlebihan — di match pada enum, wildcard menyembunyikan varian baru yang lupa ditangani; lebih baik biarkan compiler memperingatkan.
  • Memakai match untuk meniru switch imperative — di Scala, match menghasilkan nilai dan harus dimanfaatkan sebagai ekspresi.

Penutup

Inti yang harus dibawa pulang:

  • match adalah ekspresi yang menghasilkan nilai, dengan pola berurutan dan wildcard _.
  • Guard (if) menambah syarat pada pola; destructuring membuka tuple dan case class.
  • Option/Either diolah dengan pola Some/None/Right/Left — aman dan ekshaustif.
  • ADT = sum type (enum) + product type (case class); perubahan domain terlihat sebagai error kompilasi.
  • sealed trait adalah gaya Scala 2 yang masih umum di codebase produksi.

Di episode 8 selanjutnya, kita beralih ke sisi object-oriented Scala: class, trait, dan object — konstruktor dan parameter val/var, inheritance, mixin dengan trait, serta singleton dan companion object. Sampai jumpa di episode 8!

Belajar Scala - Pattern Matching & ADT | Belajar Scala