Scala CLI mengompilasi, menjalankan, menguji, dan mengemas kode Scala dari satu alat baris perintah. Topik ini membahas kemampuan Scala CLI yang melampaui program tunggal, yaitu direktif using pada berkas sumber, skrip dengan ekstensi .sc beserta baris shebang, pemilihan JVM saat eksekusi, pemformatan kode, REPL, pengemasan jar, dan pengelolaan dependensi. Semua contoh dijalankan tanpa berkas konfigurasi terpisah karena konfigurasi ikut tertulis pada berkas kode. Dengan kemampuan ini, program kecil hingga proyek satu folder dapat dikelola sepenuhnya dari terminal.
Direktif using pada berkas sumber#
Direktif using adalah komentar khusus berawalan //> using yang menyatakan konfigurasi proyek langsung di dalam berkas sumber. Scala CLI membaca direktif tersebut sebelum kompilasi, sehingga satu berkas membawa kode sekaligus aturan pembangunannya. Pendekatan ini menghilangkan kebutuhan berkas konfigurasi terpisah untuk proyek kecil.
//> using scala 3.9.0
//> using javaOpt -Dmode.konversi=metrik
object KonversiSatuan:
def celsiusKeFahrenheit(c: Double): Double =
c * 9.0 / 5.0 + 32.0
def kilometerKeMil(km: Double): Double =
km * 0.621371
@main def alatKonversi(): Unit =
val mode = System.getProperty("mode.konversi", "standar")
println(s"Mode konfigurasi: $mode")
println(f"${KonversiSatuan.celsiusKeFahrenheit(25)}%.1f F")
println(f"${KonversiSatuan.kilometerKeMil(10)}%.2f mil")# jalankan program dengan konfigurasi dari direktif
scala-cli run alat-konversi.scala 2>/dev/nullMode konfigurasi: metrik
77.0 F
6.21 milDirektif //> using scala memilih versi kompiler Scala, sedangkan //> using javaOpt meneruskan opsi ke JVM yang menjalankan program. Pada contoh di atas, javaOpt menyatakan properti sistem mode.konversi, dan program membacanya melalui System.getProperty. Opsi lain seperti -Xmx512m untuk batas memori diteruskan dengan cara yang sama. Keluaran menunjukkan efeknya secara langsung, yaitu properti yang dinyatakan pada direktif terbaca sebagai metrik, kemudian kedua fungsi konversi menghitung nilai dari argumen 25 dan 10.
Pesan proses build seperti Compiling project ditulis ke aliran error, bukan ke keluaran program. Contoh perintah pada topik ini menambahkan 2>/dev/null agar terminal hanya menampilkan keluaran program, dan pengalihan itu dapat dibuang apabila pesan proses build ingin dilihat.
Skrip .sc dan baris shebang#
Ekstensi .sc menandai berkas skrip. Pernyataan pada berkas skrip dieksekusi secara berurutan tanpa @main dan tanpa pembungkus objek, sehingga bentuknya sama dengan urutan perintah yang diketik satu per satu. Berkas skrip cocok untuk pekerjaan kecil seperti perhitungan singkat atau pengujian cepat sebuah ide.
val nama = "Scala CLI"
println(s"Halo dari skrip, $nama!")
println(s"3 + 4 = ${3 + 4}")# jalankan berkas skrip
scala-cli run salam.sc 2>/dev/nullHalo dari skrip, Scala CLI!
3 + 4 = 7Scala CLI juga menjalankan skrip sebagai program terminal melalui baris shebang, yaitu baris pertama berawalan #! yang ditafsirkan sistem operasi. Baris #!/usr/bin/env -S scala-cli shebang membuat berkas dapat dieksekusi langsung setelah izin eksekusinya diberikan, dan opsi -S pada env menyatakan bahwa sisa argumen diteruskan utuh ke scala-cli.
#!/usr/bin/env -S scala-cli shebang
val nama = "Ali"
println(s"Halo, $nama")# berikan izin eksekusi pada skrip
chmod +x sapa.sc# panggil skrip seperti program biasa
./sapa.sc 2>/dev/nullHalo, AliHasilnya sama dengan scala-cli run, tetapi pemanggilannya kini menyerupai program biasa pada sistem operasi. Pola ini berguna untuk utilitas kecil yang dipanggil berulang dari terminal, misalnya penghitung kata atau pengonversi satuan.
Kompilasi, eksekusi, dan pemilihan JVM#
Perintah scala-cli compile memeriksa kesalahan kompilasi tanpa menjalankan program, sedangkan scala-cli run mengompilasi apabila diperlukan lalu langsung mengeksekusinya. Keduanya tidak mencetak apa pun ke keluaran program apabila kompilasi berhasil.
# periksa kesalahan kompilasi tanpa menjalankan program
scala-cli compile alat-konversi.scala 2>/dev/nullPerintah di atas tidak menampilkan keluaran apa pun apabila kompilasi berhasil. Saat menyunting kode, jalankan perintah tanpa 2>/dev/null agar pesan error kompilasi terbaca langsung pada terminal.
Flag --jvm memilih JVM untuk kompilasi dan eksekusi. Scala CLI mencari versi yang diminta pada cache lokal dan mengunduhnya apabila belum tersedia, sehingga proyek berjalan pada JDK yang konsisten tanpa pengaturan tambahan di sistem. Pilihan yang sama dapat dituliskan sebagai direktif //> using jvm apabila versi tersebut ingin melekat pada berkas sumber.
@main def infoJvm(): Unit =
println(s"Versi JVM: ${System.getProperty("java.version")}")# jalankan dengan JDK 21
scala-cli run --jvm 21 info-jvm.scala 2>/dev/nullVersi JVM: 21.0.13Angka pada keluaran mencerminkan JDK yang terpasang saat contoh dijalankan, sehingga nilainya wajar apabila berbeda pada mesin lain. Yang penting adalah perintah yang sama menghasilkan lingkungan eksekusi yang dapat ditentukan, bukan bergantung pada JVM bawaan sistem.
Pemformatan kode dengan scala-cli fmt#
Perintah scala-cli fmt menjalankan scalafmt atas berkas yang diberikan dan menulis hasilnya langsung ke berkas itu. Contoh berikut menggunakan salinan kerja agar berkas aslinya tetap dalam keadaan semula dan dapat dibandingkan.
object Pengukuran{
def rataRata(nilai: List[Double]): Double =
if nilai.isEmpty then 0.0 else nilai.sum / nilai.size
}# salin berkas, format salinannya, lalu tampilkan hasilnya
cp pengukuran.scala kerja.scala && scala-cli fmt kerja.scala 2>/dev/null && cat kerja.scalaobject Pengukuran {
def rataRata(nilai: List[Double]): Double =
if nilai.isEmpty then 0.0 else nilai.sum / nilai.size
}Hasil pemformatan menata ulang kurung kurawal dan indentasi tanpa mengubah makna program. Scala CLI menggunakan aturan bawaan scalafmt apabila proyek tidak menyediakan berkas .scalafmt.conf. Jika berkas tersebut ada, aturan di dalamnya dipatuhi.
Eksperimen cepat dengan REPL#
REPL (read-eval-print loop) adalah sesi interaktif yang mengevaluasi ekspresi dan langsung menampilkan hasilnya. Perintah scala-cli repl alat-konversi.scala membuka sesi tersebut, dan karena diberi berkas sumber, seluruh definisi berkas itu dimuat lebih dahulu sehingga fungsinya siap diuji tanpa menyalin ulang kode. Sesi ditutup dengan perintah :quit.
scala> KonversiSatuan.celsiusKeFahrenheit(25)
val res0: Double = 77.0
scala> KonversiSatuan.kilometerKeMil(42)
val res1: Double = 26.097582Setiap hasil diberi nama otomatis res0, res1, dan seterusnya sesuai urutan sesi, sehingga hasil sebelumnya dapat dirujuk pada ekspresi berikutnya. REPL memotong siklus sunting, kompilasi, dan eksekusi ketika tujuannya memeriksa perilaku satu fungsi atau satu ekspresi.
Pengemasan dengan scala-cli package#
Perintah scala-cli package menghasilkan satu berkas keluaran mandiri dari proyek. Subperintah ini termasuk power mode, yakni sekumpulan perintah lanjutan yang harus diaktifkan secara eksplisit, sehingga pemanggilannya menyertakan flag --power.
# tidak-dijalankan
scala-cli package --power alat-konversi.scalaTanpa opsi tambahan, keluarannya bernama sesuai metode @main, yaitu alatKonversi pada contoh ini, dan dapat langsung dieksekusi dari terminal. Opsi -o aplikasi.jar mengubah keluaran menjadi berkas jar yang dijalankan dengan java -jar aplikasi.jar, sedangkan opsi --assembly menggabungkan seluruh dependensi ke dalam satu jar. Bentuk terakhir inilah yang biasa diserahkan ke server atau kepada anggota tim yang belum memasang toolchain Scala.
Mengelola dependensi dengan using dep#
Direktif //> using dep menambahkan dependensi dengan format groupId:artefak:versi. Scala CLI mengunduh artefak tersebut beserta dependensi transitifnya dari Maven Central, menyimpannya pada cache lokal, lalu menyertakannya pada classpath saat kompilasi dan eksekusi. Eksekusi berikutnya tidak memerlukan jaringan lagi karena artefak sudah berada dalam cache.
//> using dep io.circe::circe-generic:0.14.13
import io.circe.generic.auto.*
import io.circe.syntax.*
case class Catatan(id: Int, isi: String)
@main def eksporJson(): Unit =
val daftar = List(
Catatan(1, "belajar scala-cli"),
Catatan(2, "direktif using")
)
println(daftar.asJson.noSpaces)# kompilasi dengan circe lalu ekspor daftar catatan ke JSON
scala-cli run ekspor-json.scala 2>/dev/null[{"id":1,"isi":"belajar scala-cli"},{"id":2,"isi":"direktif using"}]Dua titik dua pada io.circe::circe-generic menandai artefak Scala yang diterbitkan silang, sehingga Scala CLI otomatis memilih varian untuk Scala 3. Artefak Java murni seperti org.slf4j:slf4j-nop ditulis dengan satu titik dua karena tidak memiliki varian per versi Scala.
Ringkasan#
- Direktif
//> usingmenyatakan konfigurasi proyek pada berkas sumber, mencakup versi Scala, opsi JVM, dan dependensi. - Berkas
.scdieksekusi sebagai skrip, dan baris shebang membuatnya dapat dipanggil langsung dari terminal. compilememeriksa tanpa menjalankan,runmengeksekusi dengan JVM pilihan melalui--jvm,replmenguji ekspresi secara interaktif, danfmtmenata format kode.package --powermenghasilkan berkas mandiri atau jar, sedangkanusing depmenambahkan dependensi yang diunduh sekali ke cache lokal.
Referensi#
- Using directives — Scala CLI — Daftar direktif using beserta sintaks dan pengelompokannya.
- Scripting — Scala CLI
— Skrip
.sc, baris shebang, dan perilaku eksekusinya. - run — Scala CLI
— Opsi perintah run, termasuk pemilihan JVM dengan
--jvm. - repl — Scala CLI — Membuka sesi REPL dan memuat berkas sumber ke dalamnya.
- fmt — Scala CLI — Menjalankan scalafmt atas berkas yang diberikan.
- package — Scala CLI — Pengemasan proyek menjadi berkas mandiri atau jar.