Operator = pada Elixir tidak sekadar mengisi variabel, melainkan mencocokkan pola (pattern matching) di sisi kiri dengan nilai di sisi kanan. Topik ini membahas = sebagai asersi, destructuring, yaitu pembongkaran nilai tuple dan list menjadi bagian-bagiannya, serta bentuk error yang muncul ketika pencocokan gagal. Setelah topik ini selesai, data berbentuk tuple dan list dapat dibongkar dengan tepat, termasuk penanganan kegagalannya.
Operator = sebagai asersi#
Pada sebagian besar bahasa pemrograman, = adalah operator penugasan yang menyalin nilai ke variabel. Pada Elixir, sisi kiri = diperlakukan sebagai pola dan sisi kanan sebagai nilai. Variabel yang muncul di sisi kiri terikat dengan bagian nilai yang sesuai, sedangkan bagian pola yang berupa nilai tetap, misalnya angka 1, wajib sama persis dengan nilai di sisi kanan. Karena aturan itu, 1 = 1 adalah pernyataan yang sah dan berhasil dieksekusi.
Sifat inilah yang menjadikan = sebagai asersi, yakni pernyataan yang wajib benar agar eksekusi dapat berlanjut. Apabila pola tidak cocok dengan nilainya, Elixir melempar MatchError pada saat eksekusi berlangsung. Error semacam ini bukan cacat yang harus selalu dihindari, melainkan sinyal bahwa bentuk data tidak sesuai dugaan. Contoh berikut memperlihatkan pencocokan yang berhasil.
Bagian pola yang berupa nilai tetap tidak terbatas pada angka. String, atom, dan tuple yang isinya bernilai tetap dapat menjadi bagian pola, dan seluruhnya wajib sama dengan nilai yang bersangkutan. Variabel yang belum terikat adalah satu-satunya bagian pola yang bebas menerima nilai apa pun.
# = mencocokkan pola di kiri dengan nilai di kanan
x = 1
IO.puts(x)
1 = 1
IO.puts("pola 1 cocok dengan nilai 1")1
pola 1 cocok dengan nilai 1Pencocokan yang gagal menghentikan eksekusi dengan error mentah. Untuk mempelajarinya secara terkendali, contoh berikut membungkus pencocokan dalam try/rescue sehingga pesan errornya dapat dicetak alih-alih menghentikan program.
# angka 2 tidak cocok dengan nilai 1 sehingga MatchError dilempar
try do
2 = 1
rescue
e in MatchError -> IO.puts("MatchError: #{Exception.message(e)}")
endMatchError: no match of right hand side value:
1Pesan errornya menyebut nilai sisi kanan yang tidak cocok, yaitu 1. Pada program sungguhan, try/rescue digunakan hanya di tempat kegagalan memang diperkirakan, misalnya di batas antara komponen yang saling memanggil. Di tempat lain, kegagalan pencocokan dibiarkan menghentikan eksekusi karena bentuk data yang salah lebih baik terlihat sejak dini daripada mengalir diam-diam.
Destructuring tuple#
Destructuring membongkar sebuah nilai berstruktur menjadi beberapa variabel sekaligus dalam satu =. Untuk tuple, polanya ditulis dalam bentuk yang sama dengan nilainya. Pencocokan {:ok, isi} = {:ok, "berhasil"} mengikat isi ke string "berhasil" karena kedua tuple memiliki ukuran dan elemen pertama yang sama. Pola semacam ini mendasari konvensi yang berlaku luas di ekosistem Elixir, yaitu fungsi mengembalikan {:ok, nilai} ketika berhasil dan {:error, alasan} ketika gagal.
{:ok, isi} = {:ok, "berhasil"}
IO.puts(isi)
{:status, kode} = {:status, 200}
IO.inspect(kode, label: "kode")berhasil
kode: 200Bentuk pola wajib sama dengan bentuk nilai. Tuple dengan elemen pertama :error tidak pernah cocok dengan pola yang menuntut :ok, seberapa pun mirip bentuk sisanya. Contoh berikut memperlihatkan kegagalan itu beserta pesannya.
# ukurannya cocok, tetapi tag :ok tidak sama dengan :error
try do
{:ok, _} = {:error, "gagal"}
rescue
e in MatchError -> IO.puts("MatchError: #{Exception.message(e)}")
endMatchError: no match of right hand side value:
{:error, "gagal"}Destructuring list#
List dibongkar dengan dua bentuk pola. Pola berukuran tetap seperti [a, b] menuntut jumlah elemen yang sama persis, sedangkan pola [kepala | ekor] memisahkan elemen pertama dari sisanya. Simbol | dibaca “serta sisanya”, sehingga [kepala | ekor] cocok dengan list apa pun yang tidak kosong. Pola ini menjadi fondasi pengolahan list pada gaya rekursif Elixir.
[kepala | ekor] = [10, 20, 30]
IO.inspect(kepala, label: "kepala")
IO.inspect(ekor, label: "ekor")
[dua | _] = [1, 2, 3]
IO.inspect(dua, label: "dua")kepala: 10
ekor: [20, 30]
dua: 1Pola berukuran tetap bersifat ketat terhadap panjang list. Pola [a, b] hanya cocok dengan list yang tepat memiliki dua elemen, bukan dua atau lebih. Perilaku ini berbeda dari beberapa bahasa lain yang membiarkan sisa elemen terabaikan diam-diam.
Kedua bentuk pola juga dapat digabungkan dalam satu pola. Penulisan [a, b | sisa] mengikat dua elemen pertama sekaligus sisanya, sedangkan pola bersarang seperti [{:ok, nilai} | _] membongkar tuple pertama dari sebuah list hasil kerja. Kedalaman pola mengikuti kedalaman datanya, sehingga satu = dapat menjangkau bagian dalam struktur tanpa langkah perantara.
[a, b] = [1, 2]
IO.puts(a + b)
# list tiga elemen tidak cocok dengan pola dua elemen
try do
[_, _] = [1, 2, 3]
rescue
e in MatchError -> IO.puts("MatchError: #{Exception.message(e)}")
end3
MatchError: no match of right hand side value:
[1, 2, 3]Contoh utuh: berkas profil.exs#
Berkas berikut menggabungkan ketiga bentuk destructuring pada satu alur data. Sumber data diasumsikan berupa tuple {:ok, data} sesuai konvensi pada bagian sebelumnya, isinya berupa map profil, dan salah satu lapangannya berupa list hobi. Berkas ini tidak memerlukan dependensi selain Elixir itu sendiri, dan seluruh barisnya dieksekusi dari atas ke bawah ketika dijalankan. Jalankan berkasnya dengan perintah elixir profil.exs dari direktori topik.
# profil.exs - destructuring data profil dengan operator =
profil = {:ok, %{nama: "Citra", umur: 24, kota: "Bandung", hobi: ["membaca", "bersepeda"]}}
{:ok, data} = profil
%{nama: nama, umur: umur, kota: kota} = data
[hobi_utama | hobi_lain] = data.hobi
IO.puts("Nama: #{nama}")
IO.puts("Umur: #{umur}")
IO.puts("Kota: #{kota}")
IO.puts("Hobi utama: #{hobi_utama}")
IO.inspect(hobi_lain, label: "Hobi lainnya")Nama: Citra
Umur: 24
Kota: Bandung
Hobi utama: membaca
Hobi lainnya: ["bersepeda"]Baris {:ok, data} = profil bekerja ganda, yaitu memastikan status berhasil sekaligus mengambil isinya. Pola map %{nama: nama, umur: umur, kota: kota} hanya mencantumkan kunci yang dibutuhkan, sedangkan list hobi dibongkar dengan [hobi_utama | hobi_lain]. Apabila pembungkusnya ternyata {:error, alasan}, baris pertama melempar MatchError karena berkas ini memang mengasumsikan data yang sah. Pada program nyata, asersi semacam ini biasanya berada di dalam fungsi yang mengendalikan sendiri bentuk keluarannya.
Kesalahan umum#
Empat kesalahan berikut paling sering muncul ketika operator = baru dipraktikkan.
- Membaca
=sebagai penugasan biasa. Penulisan1 = 2tidak mengganti nilai1, melainkan berupa pernyataan yang gagal denganMatchError. Perbaikannya, baca sisi kiri sebagai pola dan sisi kanan sebagai nilai yang harus cocok. - Menulis pola yang bentuknya berbeda dari data. Pola
{:ok, isi}untuk data{:error, alasan}selalu gagal, sekecil apa pun perbedaan yang terlihat. Perbaikannya, samakan tag dan ukurannya, atau sediakan pola terpisah untuk setiap bentuk yang mungkin muncul. - Menggunakan pola list berukuran tetap untuk list yang panjangnya berubah. Pola
[a, b]menolak list tiga elemen. Perbaikannya, gunakan[a | sisa]ketika panjang list tidak pasti. - Menganggap
_sebagai variabel yang dapat dibaca kembali. Garis bawah sama sekali tidak menyimpan nilai, sehingga pencocokan[_, _] = [1, 2]berhasil tanpa meninggalkan nilai yang dapat digunakan. Perbaikannya, beri nama variabel apabila nilainya masih dibutuhkan.
Ringkasan#
- Operator
=mencocokkan pola di kiri dengan nilai di kanan. Variabel pada pola terikat nilainya, sedangkan nilai tetap pada pola wajib sama. - Kegagalan pencocokan melempar
MatchErrorpada saat eksekusi, bukan saat kompilasi. - Destructuring tuple menuntut tag dan ukuran yang sama, sedangkan list dibongkar dengan
[kepala | ekor]atau pola berukuran tetap. - Garis bawah
_mengabaikan bagian nilai tanpa mengikat variabel.
Referensi#
- Pattern matching — HexDocs — Panduan resmi tentang operator pencocokan dan destructuring pada tuple serta list.
- Kernel.=/2 — HexDocs
— Dokumentasi operator
=beserta perilakunya sebagai pencocokan pola. - Syntax reference — HexDocs — Rujukan sintaks Elixir, termasuk aturan pola pada tuple dan list.