Menu

Fungsi di Zero: fun, pub fun, Parameter, dan Tipe Kembalian

Cara kerja fungsi di Zero: kata kunci fun, parameter bertipe, tipe kembalian, modifier visibilitas pub, dan peran raises di signature fungsi.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Anatomi Sebuah Fungsi

Bentuk umum sebuah fungsi Zero:

fun name(param1: Type1, param2: Type2) -> ReturnType {
    // body
    return value
}

Bagian-bagiannya:

  • fun: kata kunci yang mengawali sebuah fungsi.
  • name: nama fungsi.
  • (param1: Type1, ...): daftar parameter. Setiap parameter punya tipe yang eksplisit.
  • -> ReturnType: tipe kembalian.
  • { ... }: isi fungsi, yaitu sebuah blok berisi pernyataan.
  • return value: keluar dari fungsi dengan value.

Contoh konkret yang kecil:

fun double(value: i32) -> i32 {
    return value * 2
}

Itulah seluruh fungsinya. Fungsi ini menerima satu i32, mengembalikan nilai lain bertipe sama, dan tidak melakukan I/O. Di dalam isi fungsi, value adalah binding seperti let, jadi kamu bisa memakainya seperti variabel lokal lainnya.

Memanggil Fungsi

Pemanggilan fungsi terlihat persis seperti yang kamu harapkan:

let result = double(21)

Argumennya harus cocok dengan tipe parameter. Hasilnya diikat ke result, yang tipenya diinferensi compiler sebagai i32 karena double mengembalikan i32.

Contoh lengkap yang menggabungkan fungsi bantuan dan main. Klik Jalankan untuk melihat hasilnya:

Kamu seharusnya mendapat math works\n di stdout.

pub dan Visibilitas

Secara default, fungsi yang dideklarasikan di sebuah file bersifat privat di file itu (atau module-nya, karena aturan visibilitas makin ketat saat proyek berkembang). Untuk membuka fungsi ke luar module-nya, tambahkan pub di depannya:

pub fun greet() -> String {
    return "hello\n"
}

Tanpa pub, kode di module lain tidak bisa memanggil greet. Runtime perlu memanggil main dari luar module buatan pengguna mana pun, dan itulah sebabnya main selalu pub.

Aturan privat sebagai default ini layak diikuti. Tandai hanya yang memang ingin kamu jadikan antarmuka, dan biarkan sisanya tetap di dalam module.

Tipe Kembalian

Setiap fungsi mendeklarasikan tipe kembaliannya setelah ->. Tipe kembalian yang umum:

fun answer() -> i32 { return 42 }
fun ok()     -> bool { return true }
fun label()  -> String { return "ready\n" }
fun nothing() -> Void { }

Void adalah tipe kembalian untuk fungsi yang bekerja lewat efek samping, bukan dengan menghasilkan nilai. Fungsi Void tidak butuh return yang eksplisit, karena selesai di akhir isi fungsi sudah cukup.

fun log(world: World, message: String) -> Void raises {
    check world.out.write(message)
}

Pemanggilan Fungsi yang Membuang Nilai

Kalau sebuah fungsi mengembalikan nilai dan kamu tidak membutuhkannya, kamu tetap harus mengakui nilai kembalian itu. Caranya, ikat nilainya dengan let:

ignored adalah binding yang tidak pernah dibaca oleh sisa fungsi. Konvensi memakai nama ignored (atau _) menandakan bahwa nilai itu sengaja dibuang. Cara ini memang lebih repot daripada membuang nilai kembalian diam-diam, dan memang itulah tujuannya: di bahasa yang kodenya dibaca dan dibuat oleh agen, nilai yang tidak dibaca sering kali adalah bug yang perlu diungkap.

Peran raises

Fungsi yang mungkin gagal menyatakannya di signature. Kita sudah melihatnya pada main:

pub fun main(world: World) -> Void raises {
    check world.out.write("hello\n")
}

Klausa raises bisa ditulis saja (error apa pun) atau spesifik:

fun validate(ok: Bool) -> i32 raises { InvalidInput } {
    if ok == false {
        raise InvalidInput
    }
    return 42
}

raises { InvalidInput } berarti "fungsi ini bisa gagal dengan InvalidInput, dan tidak dengan error lain." Pemanggil harus memakai check (atau bentuk penanganan yang lebih lengkap) untuk meneruskan atau menangani error tersebut.

Raises dan Check membahas ini secara mendalam, termasuk apa yang terjadi dengan beberapa tipe error dan bagaimana check berinteraksi dengan klausa raises milik pemanggil.

Fungsi Generic

Saat kamu ingin sebuah fungsi bekerja untuk lebih dari satu tipe, deklarasikan parameter tipe di dalam kurung sudut:

fun makePair<T, U>(left: T, right: U) -> Pair<T, U> {
    return Pair { left: left, right: right }
}

T dan U adalah parameter tipe, dan pemanggillah yang menentukan tipenya. Memanggil makePair(40, 2_u8) menghasilkan Pair<i32, u8>. Lihat Generic untuk penjelasan lengkapnya, termasuk shape generic dan batasan tipe.

Di Mana Fungsi Ditulis

Di program kecil, kamu menulis fungsi langsung di file .0. Di dalam package, kamu menyebarkan fungsi ke beberapa file di dalam src/, dan compiler menyelesaikan referensi antar-file untukmu. Dasarnya tetap sama, yaitu fun, parameter, tipe kembalian, dan isi fungsi, di mana pun fungsi itu berada.

Catatan Gaya

Beberapa konvensi yang akan kamu lihat di contoh-contoh resmi:

  • Nama fungsi huruf kecil, dengan kata yang disambung (makePair) atau dipisahkan dengan camelCase. Standard library cenderung memakai camelCase.
  • Satu nilai kembalian per fungsi. Kalau kamu perlu mengembalikan beberapa hal, buat shape kecil untuk menampungnya. Itu lebih jelas daripada mengembalikan tuple berisi pair berisi tuple.
  • Fungsi Void hanya melakukan pemanggilan check, sedangkan fungsi yang menghitung nilai menghindari I/O sebisa mungkin. Pemisahan ini sebagian karena kebiasaan, sebagian lagi dipaksakan oleh bahasa: fungsi perhitungan murni tidak menerima world, jadi memang tidak bisa melakukan I/O.

Poin terakhir itu layak direnungkan. Karena I/O ada di balik capability World dan World dikirim secara eksplisit, signature sebuah fungsi memberi tahu kamu apakah fungsi itu mungkin melakukan I/O. Fungsi yang signature-nya tidak menyebut World bersifat murni terhadap dunia luar. Agen (dan manusia) bisa mengandalkan sifat ini tanpa membaca isi fungsinya.

Berikutnya: If/Else

Kamu sudah melihat if muncul sekilas. Dokumen berikutnya membahas ekspresi if/else secara detail, termasuk interaksinya dengan binding dan apa saja yang sengaja tidak disediakan (tidak ada konversi truthy, tidak ada operator ternary).

Pertanyaan yang sering diajukan

Bagaimana cara mendeklarasikan fungsi di Zero?

Pakai fun: fun name(param: Type) -> ReturnType { body }. Tambahkan pub di depannya agar fungsi terlihat dari luar module-nya. Tambahkan raises setelah tipe kembalian kalau fungsi itu bisa gagal. Contohnya: pub fun double(value: i32) -> i32 { return value * 2 }.

Apa fungsi kata kunci pub?

pub menandai sebuah deklarasi sebagai publik, yaitu terlihat oleh kode di luar module-nya. Tanpa pub, sebuah fungsi bersifat privat di file (atau package) tempat ia dideklarasikan. Titik masuk standar pub fun main harus publik agar runtime bisa menemukan dan memanggilnya.

Bagaimana cara mengembalikan nilai dari fungsi di Zero?

Tulis return value di dalam isi fungsi. Ekspresinya harus cocok dengan tipe kembalian yang dideklarasikan. Fungsi dengan tipe kembalian Void tidak mengembalikan apa pun dan tidak butuh pernyataan return yang eksplisit, jadi fungsi boleh selesai begitu saja di akhir isinya.

Apakah fungsi Zero bisa menerima beberapa parameter?

Bisa. Tulis parameter-parameternya di dalam kurung, dipisahkan koma, masing-masing dengan nama dan tipenya: fun add(a: i32, b: i32) -> i32 { return a + b }. Setiap parameter adalah binding seperti let di dalam isi fungsi. Zero mewajibkan tipe yang eksplisit pada parameter, jadi tidak ada inferensi tipe parameter di deklarasi fungsi.

Apa arti raises pada signature fungsi?

raises menyatakan bahwa fungsi itu bisa gagal. raises saja mengizinkan tipe error apa pun, sedangkan raises { InvalidInput } membatasinya ke error tertentu yang diberi nama. Pemanggil harus memakai check (atau konstruksi lain untuk kegagalan) untuk mengakui kemungkinan gagal itu, dan tidak boleh mengabaikannya diam-diam.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai