Fungsi def

Menulis fungsi def dengan body bergaya Python dan aturan pengetikan yang longgar.

Daftar isi

Fungsi di Mojo dideklarasikan dengan kata kunci def, dan sejak Mojo 1.0 kata kunci tersebut menjadi satu-satunya cara mendefinisikan fungsi. Topik ini membahas anatomi def, aturan pengetikan pada signature atau baris deklarasi fungsi, argumen dengan nilai default dan pemanggilan menurut nama, serta penanda raises untuk fungsi yang dapat menyebarkan error. Setiap contoh disertai berkas utuh yang dijalankan dengan mojo run, sehingga setiap aturan dapat diperiksa langsung pada keluarannya. Setelah topik ini, deklarasi fungsi sederhana dapat ditulis, dijalankan, dan diperbaiki apabila compiler menolaknya.

Anatomi fungsi def#

Deklarasi def terdiri atas nama fungsi, daftar argumen bertipe, tipe kembalian yang bersifat opsional, dan badan fungsi (function body) yang diindentasi seperti Python. Deklarasi dimulai dengan def, diikuti nama fungsi dan tanda kurung yang memuat argumen. Setiap argumen dideklarasikan dengan pola nama: Tipe, sedangkan tipe kembalian ditulis setelah tanda ->. Baris-baris di bawah titik dua membentuk badan fungsi dan wajib diindentasi, sehingga struktur program terbaca sama seperti kode Python.

Berkas tambah.mojo mendefinisikan fungsi tambah yang menerima dua argumen Int dan mengembalikan jumlah keduanya. Kata kunci return mengakhiri fungsi sekaligus mengirim nilai kembali ke pemanggil. Seluruh berkas dapat disalin apa adanya dan dijalankan tanpa perubahan.

mojo tambah.mojo
def tambah(a: Int, b: Int) -> Int:
    return a + b


def main():
    print(tambah(2, 3))

Jalankan berkas tersebut dengan perintah berikut.

bash
# menjalankan program Mojo pertama
mojo run tambah.mojo
text Keluaran
5

Pemanggilan tambah(2, 3) mengirimkan dua nilai posisi sesuai urutan deklarasinya. Nilai yang dikembalikan oleh return langsung diteruskan ke print, sehingga keluarannya adalah 5. Fungsi yang tidak memuat return pun tetap sah dan berhenti pada baris terakhir badannya.

Aturan pengetikan pada def#

Aturan pengetikan def bersifat longgar pada dua titik, yaitu tipe kembalian yang boleh dihilangkan dan penerimaan literal untuk argumen bertipe numerik. Tipe kembalian yang tidak dideklarasikan berarti None, yaitu tipe khusus yang menandakan ketiadaan nilai kembalian. Fungsi sapa pada sapa.mojo hanya mencetak teks dan tidak mengembalikan apa pun, sehingga penulisannya tanpa anotasi kembalian.

mojo sapa.mojo
def sapa(nama: String):
    print("Halo,", nama)


def main():
    sapa("Citra")
bash
# fungsi tanpa tipe kembalian
mojo run sapa.mojo
text Keluaran
Halo, Citra

Kelonggaran yang kedua berlaku pada literal, yaitu nilai yang ditulis langsung di dalam kode. Compiler, yaitu program yang menerjemahkan kode Mojo menjadi program yang dapat dijalankan, menerima literal bilangan bulat seperti 4 untuk argumen bertipe Float64 tanpa konversi manual karena nilainya sudah pasti saat kompilasi. Berkas skala.mojo memanggil fungsi bertipe Float64 dengan literal 4 dan hasilnya dicetak sebagai bilangan pecahan.

mojo skala.mojo
def skala(x: Float64) -> Float64:
    return x * 2.0


def main():
    print(skala(4))
bash
# literal Int diterima untuk argumen Float64
mojo run skala.mojo
text Keluaran
8.0

Kelonggaran tersebut tidak berlaku sebaliknya pada nilai yang sudah bertipe. Konversi antar tipe bernama tetap harus dinyatakan secara eksplisit, misalnya melalui konstruktor seperti Float64(...). Fungsi tanpa anotasi kembalian juga tidak boleh memuat return dengan nilai, sebagaimana diperagakan pada bagian Kesalahan umum.

Argumen default dan pemanggilan kata kunci#

Argumen dapat diberi nilai default sehingga pemanggil boleh mengabaikannya. Nilai default ditulis setelah tanda = pada deklarasi argumen, dan argumen dengan nilai default wajib berada setelah argumen wajib. Selain itu, pemanggilan dapat menyebut nama argumen secara eksplisit dengan pola nama=nilai, sebuah gaya yang diwarisi dari Python. Pemanggilan menurut nama boleh membalik urutan dan boleh dicampur dengan pemanggilan posisi.

Berkas pangkat.mojo mendefinisikan fungsi pangkat dengan nilai default 2 pada argumen p. Tiga pemanggilan pada main menunjukkan penggunaan default, pengiriman posisi lengkap, dan pengiriman menurut nama.

mojo pangkat.mojo
def pangkat(x: Int, p: Int = 2) -> Int:
    return x ** p


def main():
    print(pangkat(3))
    print(pangkat(3, 3))
    print(pangkat(3, p=4))
bash
# tiga bentuk pemanggilan yang setara secara hasil
mojo run pangkat.mojo
text Keluaran
9
27
81

Pemanggilan pertama menggunakan nilai default sehingga menghasilkan 9, sedangkan pemanggilan kedua mengirim 3 sebagai nilai pangkat dan menghasilkan 27. Pemanggilan ketiga menyebut p=4 secara eksplisit dan menghasilkan 81. Nilai default yang tepat membuat API fungsi lebih ringkas tanpa menghilangkan fleksibilitas pengaturannya.

Fungsi yang dapat raise#

Fungsi Mojo secara default bersifat non-raising, artinya fungsi tersebut tidak boleh menyebarkan error ke pemanggilnya. Untuk mengizinkan penyebaran error (error propagation), tambahkan kata kunci raises pada signature. Di dalam fungsi bertanda raises, pernyataan raise Error("pesan") menghentikan fungsi dan mengirim objek error ke atas. Pemanggil dapat menangkap error tersebut dengan blok try dan except, atau membiarkannya menyebar dengan menandai fungsi pemanggil raises pula.

Berkas cek-nilai.mojo memuat fungsi cek yang menolak nilai negatif dengan melempar error, dan fungsi main yang menangkapnya.

mojo cek-nilai.mojo
def cek(nilai: Int) raises:
    if nilai < 0:
        raise Error("nilai tidak boleh negatif")


def main() raises:
    try:
        cek(-1)
    except e:
        print("tertangkap:", e)
    print("program berlanjut")
bash
# melempar dan menangkap error
mojo run cek-nilai.mojo
text Keluaran
tertangkap: nilai tidak boleh negatif
program berlanjut

Blok except menerima objek error sebagai e, dan pencetakan e menampilkan pesannya. Eksekusi berlanjut setelah penangkapan, sehingga baris terakhir tetap tercetak. Penanda raises bersifat eksplisit dan terbaca langsung dari signature, sehingga pembaca mengetahui kemungkinan kegagalan tanpa membaca badan fungsi.

Kesalahan umum#

Kebiasaan menulis Python membuat argumen sering dideklarasikan tanpa tipe. Compiler menolak bentuk tersebut karena tipe argumen wajib dinyatakan pada semua fungsi. Berkas argumen-tanpa-tipe.mojo memuat kesalahan tersebut beserta pesannya.

mojo argumen-tanpa-tipe.mojo
def salam(nama):
    print("Hai", nama)


def main():
    salam("Dewi")
bash
# argumen tanpa tipe ditolak compiler
mojo run argumen-tanpa-tipe.mojo
text Keluaran
/home/kal/my-project/content/mojo/dua-jenis-fungsi/fungsi-def/argumen-tanpa-tipe.mojo:1:11: error: argument type must be specified
def salam(nama):
          ^~~~
/home/kal/.pixi/envs/mojo/bin/mojo: error: failed to parse the provided Mojo source module

Perbaikannya adalah mendeklarasikan tipe argumen, misalnya def salam(nama: String):. Kesalahan kedua terjadi ketika fungsi tanpa raises memanggil fungsi yang dapat melempar error. Compiler meminta penanganan eksplisit, sebagaimana ditunjukkan oleh tanpa-raises.mojo.

mojo tanpa-raises.mojo
def cek(nilai: Int) raises:
    if nilai < 0:
        raise Error("nilai tidak boleh negatif")


def main():
    cek(-1)
bash
# pemanggilan fungsi raising tanpa penanganan
mojo run tanpa-raises.mojo
text Keluaran
/home/kal/my-project/content/mojo/dua-jenis-fungsi/fungsi-def/tanpa-raises.mojo:7:8: error: cannot call function that may raise in a context that cannot raise
    cek(-1)
    ~~~^~~~
/home/kal/my-project/content/mojo/dua-jenis-fungsi/fungsi-def/tanpa-raises.mojo:7:8: note: try surrounding the call in a 'try' block
    cek(-1)
       ^
/home/kal/my-project/content/mojo/dua-jenis-fungsi/fungsi-def/tanpa-raises.mojo:6:5: note: or mark surrounding function as 'raises'
def main():
    ^
/home/kal/.pixi/envs/mojo/bin/mojo: error: failed to parse the provided Mojo source module

Pesan error menyebut dua perbaikan, yaitu membungkus pemanggilan dengan try atau menandai main dengan raises. Pilih penandaan raises apabila error memang hendak disebarkan ke atas, dan pilih try apabila error ditangani di tempat.

Kesalahan ketiga terjadi ketika fungsi tanpa anotasi kembalian memuat return dengan nilai. Tipe kembalian yang tidak dideklarasikan berarti None, sehingga pengembalian nilai apa pun bertabrakan dengan tipe tersebut. Berkas tanpa-kembalian.mojo memuat fungsi angka yang mengembalikan 7 tanpa anotasi kembalian.

mojo tanpa-kembalian.mojo
def angka():
    return 7


def main():
    var x = angka()
bash
# return bernilai pada fungsi tanpa anotasi kembalian
mojo run tanpa-kembalian.mojo
text Keluaran
/home/kal/my-project/content/mojo/dua-jenis-fungsi/fungsi-def/tanpa-kembalian.mojo:2:12: error: cannot implicitly convert 'IntLiteral[7]' value to 'None' in return value
    return 7
           ^
/home/kal/.pixi/envs/mojo/bin/mojo: error: failed to parse the provided Mojo source module

Perbaikannya adalah menambahkan anotasi kembalian, misalnya def angka() -> Int:. Dengan anotasi tersebut, pemanggilan di main juga dapat menyimpan nilai kembalian ke dalam variabel x tanpa penolakan compiler.

Ringkasan#

  • Fungsi Mojo dideklarasikan dengan def yang badannya diindentasi seperti Python, dan def menjadi satu-satunya kata kunci fungsi sejak Mojo 1.0.
  • Tipe setiap argumen wajib dideklarasikan, sedangkan tipe kembalian boleh dihilangkan dan berarti None.
  • Argumen dapat memiliki nilai default, dan pemanggilan dapat dilakukan menurut posisi maupun nama argumen.
  • Fungsi bersifat non-raising secara default, sehingga fungsi yang menyebarkan error wajib ditandai raises dan pemanggilnya wajib menangkap atau menyebarkannya kembali.

Referensi#