Satu Cara untuk Memberi Nama Nilai
Di Zero, kamu memberi nama pada sebuah nilai dengan let:
let answer = 42
Hanya itu sintaksnya. Tidak ada var, tidak ada const, tidak ada auto. Satu kata kunci binding membuat bahasanya tetap kecil: agen dan manusia cukup mempelajarinya sekali lalu memakainya di mana saja.
let membuat binding lokal di scope yang sedang berlaku. Setelah baris ini, answer merujuk ke 42 sampai akhir blok yang melingkupinya.
Inferensi Tipe
Compiler menyimpulkan tipe dari sisi kanan. Literal 42 bertipe i32 secara default, jadi answer adalah i32. Literal "hello" adalah string, jadi:
let greeting = "hello"
mengikat greeting ke sebuah nilai string. Kalau kamu memanggil fungsi yang mengembalikan Pair<i32, u8>, binding-nya bertipe Pair<i32, u8>:
let pair = makePair(40, 2_u8)
Kamu tidak perlu menulis tipe di setiap binding, sehingga kodenya tetap mudah dibaca.
Anotasi Tipe Eksplisit
Saat kamu ingin mendokumentasikan tipenya, atau memaksakan tipe tertentu ketika inferensi akan memilih tipe lain, tulis tipenya setelah titik dua:
let count: u8 = 10
let pair: Pair<i32, u8> = makePair(40, 2_u8)
Anotasi juga menjadi petunjuk bagi compiler saat sebuah literal bisa bertipe salah satu dari beberapa tipe. Literal 10 bisa berupa i32, i64, u8, dan seterusnya, dan anotasi menetapkan tipe yang mana.
Kamu juga akan melihat akhiran tipe pada literal sebagai alternatif dari anotasi pada binding:
let small = 10_u8 // u8 from the literal suffix
let big = 10_i64 // i64 from the literal suffix
Kedua bentuk itu valid. Pilih yang membuat maksudmu paling jelas di tempat kode itu ditulis.
Binding dalam Praktik
Contoh lengkap yang memakai bentuk inferensi dan bentuk eksplisit. Klik Jalankan untuk mencobanya:
point diberi anotasi secara eksplisit karena sisi kanannya adalah literal struct. total diinferensi: sum dideklarasikan mengembalikan i32, jadi binding-nya juga i32.
Scope dan Shadowing
Binding let berlaku mulai dari baris yang mendeklarasikannya sampai akhir blok yang melingkupinya. Blok bersarang membuat scope baru:
pub fun main(world: World) -> Void raises {
let value = 1
if true {
let value = 2 // shadows the outer 'value' inside this block
// here, value == 2
}
// back outside the if, value == 1 again
}
value yang di dalam tidak mengubah binding yang di luar. Itu binding terpisah yang keluar dari scope di kurung kurawal penutup blok if. Model ini sama dengan Rust dan bahasa keluarga ML. Pola ini sering dipakai saat kamu ingin mengubah sebuah nilai lewat serangkaian langkah tanpa harus membuat nama baru untuk setiap hasil antara.
Yang Tidak Dilakukan let
Beberapa hal yang mungkin kamu harapkan dari bahasa lain, tetapi sengaja tidak disertakan dalam let:
- Deklarasi tanpa nilai. Tidak ada bentuk
let x: i32;yang membuat binding tanpa inisialisasi. Sebuah binding harus punya nilai di tempat ia dideklarasikan. - Destructuring pola (belum). Beberapa bahasa membolehkan
let (a, b) = pair. Zero sengaja dibuat kecil dan saat ini berfokus pada binding nama biasa. Cek dokumentasi terbaru untuk tahu apakah destructuring sudah tersedia. - Beberapa kata kunci untuk masa hidup yang berbeda. Tidak ada
static,const,let mutyang terpisah, atau varian dengan scope blok dan scope fungsi. Hanya satu kata kunci.
Kalau latar belakangmu JavaScript, analogi terdekatnya adalah const: sebuah nama yang terikat ke sebuah nilai sampai akhir blok, dengan shadowing di scope dalam. Kalau latar belakangmu Rust, let di sini punya peran yang sama dengan let di Rust, hanya tanpa kata kunci mut yang eksplisit.
Pola: Membangun Nilai Langkah demi Langkah
Binding sangat berguna saat kamu ingin menulis sebuah perhitungan sebagai serangkaian langkah antara yang diberi nama. Ini bagus untuk manusia yang membaca dan juga untuk agen yang menalar setiap baris secara lokal:
Setiap baris menambahkan satu fakta baru yang bisa dipakai di sisa fungsi. Compiler tetap menghasilkan kode yang ringkas, karena memberi nama pada nilai antara tidak menambah biaya saat runtime.
Berikutnya: Tipe Primitif
let tidak banyak artinya tanpa sesuatu untuk diikat. Dokumen berikutnya membahas tipe primitif Zero: lebar integer, float, string, serta tipe Void dan Bool yang paling sering kamu lihat.
Pertanyaan yang sering diajukan
Bagaimana cara mendeklarasikan variabel di Zero?
Pakai let. Bentuknya let name = value untuk tipe yang diinferensi, atau let name: Type = value untuk menulis tipenya secara eksplisit. Contohnya: let answer = 42 atau let answer: i32 = 42. Keduanya mengikat nama answer ke nilai 42 di scope yang sedang berlaku.
Apakah Zero menginferensi tipe untuk binding let?
Ya. Kalau kamu menulis let total = sum(point) dan sum mengembalikan i32, tipe binding itu diinferensi sebagai i32. Kamu tetap bisa menulis anotasi secara eksplisit saat ingin mendokumentasikan tipenya atau memaksakan tipe tertentu, misalnya let count: u8 = 10.
Apakah binding let di Zero bisa diubah (mutable)?
let biasa membuat binding lokal untuk dipakai di dalam scope-nya. Aturan mutabilitas di Zero pra-1.0 masih terus berkembang. Bahasa ini menekankan efek yang eksplisit dan memori yang mudah ditebak, jadi apa pun yang mengubah state lewat sebuah binding harus terlihat jelas. Cek dokumentasi Zero terbaru untuk sintaks mutabilitas yang tepat di versi toolchain kamu.
Apa bedanya let dan const di Zero?
Zero memakai let untuk binding lokal biasa di dalam isi fungsi. Zero tidak menyediakan beberapa kata kunci binding seperti let/const/var di JavaScript, karena menjaga bahasanya tetap kecil adalah pilihan desain yang disengaja. Konstanta saat kompilasi biasanya diungkapkan lewat sistem tipe atau deklarasi di tingkat atas, bukan lewat kata kunci terpisah.
Apakah binding let bisa dideklarasikan ulang di Zero?
Binding berlaku di dalam scope yang melingkupinya. let baru dengan nama yang sama di scope bersarang adalah binding terpisah yang menutupi (shadowing) binding luar selama scope dalam itu berlaku. Binding luar tidak terpengaruh setelah scope dalam berakhir. Model ini sama dengan yang dipakai Rust dan bahasa keluarga ML.