Ekspresi di mana-mana

if, blok, dan match semuanya menghasilkan nilai, sehingga program tersusun dari ekspresi.

Daftar isi

Pada banyak bahasa, if dan blok kode hanya mengatur alur program, dan hasilnya harus disimpan ke variabel sementara agar dapat digunakan di tempat lain. Scala mengambil jalan lain. Hampir setiap konstruksi bahasa merupakan expression, yaitu potongan kode yang menghasilkan nilai ketika dievaluasi. Topik ini membahas if, blok, dan match sebagai ekspresi, kemudian menutup dengan program kecil yang tersusun seluruhnya dari nilai kembalian konstruksi-konstruksi tersebut.

if menghasilkan nilai#

Ekspresi if memilih salah satu dari beberapa nilai, sehingga hasil pemilihannya dapat langsung diikat dengan val. Tidak ada variabel sementara yang diisi di setiap cabang dan tidak ada penugasan ganda, karena pemilihan dan pengikatan terjadi dalam satu langkah.

Simpan program berikut sebagai ekspresi-if.scala.

scala ekspresi-if.scala
// if memilih nilai, bukan hanya mengatur alur
@main def ekspresiIf(): Unit =
  val skor = 78

  val predikat =
    if skor >= 90 then "A"
    else if skor >= 80 then "B"
    else if skor >= 70 then "C"
    else "D"

  println("Skor " + skor + " memperoleh predikat " + predikat)

Jalankan program tersebut dengan scala-cli.

bash
# mengompilasi dan menjalankan program ekspresi if
scala-cli run ekspresi-if.scala
text Keluaran
Skor 78 memperoleh predikat C

Nilai skor adalah 78, sehingga cabang ketiga terpilih dan predikat terikat ke String “C”. Rantai else if bekerja seperti biasa, yaitu kondisi diperiksa dari atas ke bawah sampai satu kondisi terpenuhi. Yang penting diperhatikan adalah letak hasilnya: seluruh rangkaian if ... else ... berdiri di sebelah kanan tanda sama dengan, persis seperti satu ekspresi biasa.

Cabang else memegang peran penting pada bentuk ini. Apabila cabang itu tidak ditulis, ekspresi if tidak memiliki nilai untuk kondisi yang gagal, sehingga tipenya menjadi Unit dan nilainya adalah (). Hasil pemilihan yang ditunggu oleh val dengan demikian tidak tersedia.

Blok menghasilkan nilai#

Blok adalah deretan deklarasi dan ekspresi yang ditulis bersama dalam satu tingkat indentasi. Blok juga merupakan ekspresi, dan nilainya berasal dari ekspresi terakhirnya. Deklarasi di dalam blok tidak terlihat dari luar, sehingga nilai antara tidak bocor ke bagian lain program.

Simpan program berikut sebagai ekspresi-blok.scala.

scala ekspresi-blok.scala
// Blok bernilai sesuai ekspresi terakhirnya
@main def ekspresiBlok(): Unit =
  val jumlahBarang = 12
  val hargaSatuan = 2500

  val totalBayar =
    val dasar = jumlahBarang * hargaSatuan
    val pajak = dasar / 10
    dasar + pajak

  println("Jumlah : " + jumlahBarang)
  println("Total  : " + totalBayar)

Jalankan program tersebut.

bash
# mengompilasi dan menjalankan program ekspresi blok
scala-cli run ekspresi-blok.scala
text Keluaran
Jumlah : 12
Total  : 33000

Perhitungan pada blok berjalan sebagai berikut. Nilai dasar adalah 12 * 2500 = 30000, nilai pajak adalah sepersepuluh dari dasar, yaitu 3000, dan ekspresi terakhir dasar + pajak menghasilkan 33000. Nilai 33000 itulah yang menjadi nilai blok, sehingga totalBayar terikat ke Int dengan nilai tersebut. Nama dasar dan pajak hanya berlaku di dalam blok, sehingga lingkup program tetap bersih dari nama sementara.

Bentuk ini menggantikan kebiasaan lama mendeklarasikan variabel bantu di awal fungsi. Nilai antara dideklarasikan tepat di tempat penggunaannya, terbaca sebagai satu kesatuan, lalu hilang setelah blok selesai dievaluasi.

match menghasilkan nilai#

Ekspresi match menguji sebuah nilai terhadap beberapa pola dari atas ke bawah, lalu menghasilkan nilai dari cabang pertama yang cocok. Bagian ini menggunakan pola paling sederhana, yaitu pola literal, sedangkan pola yang lebih kaya dibahas pada bagian tersendiri.

Simpan program berikut sebagai ekspresi-match.scala.

scala ekspresi-match.scala
// match memetakan nilai ke hasil melalui pola
@main def ekspresiMatch(): Unit =
  val kode = 2

  val namaHari =
    kode match
      case 1 => "Senin"
      case 2 => "Selasa"
      case 3 => "Rabu"
      case _ => "di luar jangkauan"

  println("Kode " + kode + " adalah " + namaHari)

Jalankan program tersebut.

bash
# mengompilasi dan menjalankan program ekspresi match
scala-cli run ekspresi-match.scala
text Keluaran
Kode 2 adalah Selasa

Kode 2 dicocokkan mulai dari case 1, tidak cocok, lalu cocok dengan case 2, sehingga namaHari terikat ke String “Selasa”. Pola case _ bertindak sebagai penangkap sisa untuk nilai yang tidak cocok dengan cabang mana pun. Hasil pada setiap cabang di contoh ini semuanya String, sehingga tipe keseluruhan ekspresi match adalah String.

Diagram berikut merangkum ketiga konstruksi beserta nilai yang dihasilkannya.

Konstruksi if, blok, dan match menghasilkan nilaiifif skor >= 70 then "C" else "D""C"blokblok: ... dasar + pajak33000matchcase 2 => "Selasa""Selasa"
Konstruksi if, blok, dan match masing-masing menghasilkan satu nilai

Menyusun program dari ekspresi#

Ketika setiap konstruksi bernilai, ekspresi dapat disarangkan ke dalam ekspresi lain tanpa variabel sementara yang tersebar. Program berikut menggabungkan blok, if, dan match dalam satu pengikatan.

Simpan program berikut sebagai status-pengumpulan.scala.

scala status-pengumpulan.scala
// Program kecil yang tersusun dari ekspresi bersarang
@main def statusPengumpulan(): Unit =
  val jumlahTerkumpul = 7
  val target = 10

  val status =
    val proporsi = jumlahTerkumpul * 100 / target
    if proporsi >= 100 then "selesai"
    else
      val sisa = target - jumlahTerkumpul
      val fase = sisa match
        case 1 => "satu berkas lagi"
        case n if n <= 3 => "tahap akhir"
        case _ => "tahap awal"
      "berjalan pada " + fase + ", sisa " + sisa + " berkas"

  println("Status: " + status)

Jalankan program tersebut.

bash
# mengompilasi dan menjalankan program status pengumpulan
scala-cli run status-pengumpulan.scala
text Keluaran
Status: berjalan pada tahap akhir, sisa 3 berkas

Alur nilai pada program tersebut dapat ditelusuri dari dalam ke luar. Blok luar menghitung proporsi, yaitu 7 * 100 / 10 = 70, sehingga cabang else yang dievaluasi. Di dalam cabang tersebut, sisa bernilai 3, lalu match menentukan fase: pola case n if n <= 3 adalah pola dengan syarat, dan kondisi 3 <= 3 terpenuhi sehingga hasilnya “tahap akhir”. Ekspresi terakhir menyusun String lengkap, dan String itulah nilai blok luar yang terikat ke status.

Gaya ekspresif ini mempersempit ruang kesalahan. Nilai antara tidak pernah terlihat di luar lingkupnya, sehingga tidak ada kesempatan bagi kode lain untuk membaca keadaan yang belum selesai dihitung.

Kesalahan umum#

Kesalahan yang mudah terjadi pada blok bernilai adalah meletakkan perintah pencetakan di baris terakhirnya. Ketika println menjadi ekspresi terakhir, nilai blok menjadi Unit, sehingga val yang mengikat blok tersebut menyimpan () alih-alih nilai perhitungan yang dimaksud. Perbaikannya, letakkan perintah pencetakan di luar blok dan pastikan ekspresi terakhir di dalam blok benar-benar nilai yang hendak diambil.

Kesalahan kedua adalah menulis cabang if dengan tipe yang berbeda. Rangkaian if ... else ... menghasilkan tipe yang merupakan gabungan tipe seluruh cabangnya. Apabila satu cabang mengembalikan String dan cabang lain mengembalikan Int, hasilnya adalah tipe union String | Int. Nilai semacam itu sah, tetapi penggunaannya menuntut penanganan kedua kemungkinan dan sering kali tidak sesuai maksud program. Perbaikannya, samakan tipe keluaran setiap cabang atau sengajakan union tersebut sebagai bagian dari desain.

Ringkasan#

  • if, blok, dan match merupakan ekspresi yang menghasilkan nilai, sehingga hasilnya dapat langsung diikat dengan val.
  • Nilai sebuah blok berasal dari ekspresi terakhirnya, sedangkan deklarasi di dalamnya hanya berlaku pada lingkup blok.
  • match mencocokkan nilai dengan pola dari atas ke bawah dan menghasilkan nilai cabang pertama yang cocok.
  • if tanpa cabang else bertipe Unit dan menghasilkan (), sehingga hasil pemilihannya tidak dapat digunakan.

Referensi#