Fungsi pada Mojo dapat memuat parameter yang nilainya ditetapkan saat kompilasi di dalam kurung siku, terpisah dari argumen runtime yang berada di dalam kurung biasa. Topik ini membahas deklarasi parameter lebar dan dtype pada fungsi yang bekerja dengan SIMD, bentuk pemanggilan eksplisitnya, serta parameter tipe generik yang melayani banyak tipe data. Setelah topik ini dibaca, satu fungsi dapat ditulis untuk lebar atau tipe mana pun yang dibutuhkan pemanggilnya.
Parameter compile-time pada kurung siku#
Kurung siku pada deklarasi fungsi menampung parameter compile-time, yaitu nama yang nilainya wajib diketahui compiler sebelum program dijalankan. Kurung biasa setelahnya menampung argumen runtime sebagaimana fungsi pada umumnya. Bentuk def jumlahkan[lebar: Int](v: SIMD[DType.float64, lebar]) -> Float64: memuat satu parameter compile-time bernama lebar bertipe Int dan satu argumen runtime bernama v. Parameter lebar dipakai di dalam tipe argumen tersebut, sehingga setiap nilai lebar menghasilkan argumen dengan tipe yang berbeda.
Compiler menyiapkan spesialisasi, yaitu versi fungsi yang dikompilasi untuk satu kombinasi parameter. Pemanggilan jumlahkan[4] menghasilkan versi dengan v bertipe SIMD[DType.float64, 4], sedangkan pemanggilan jumlahkan[8] menghasilkan versi lain dengan lebar 8. Kedua versi dibangun dari satu body yang sama tanpa duplikasi sumber. Tipe SIMD itu sendiri diparametrisasi dengan cara yang sama, sehingga bentuk kurung siku ini sudah dikenal dari penulisan tipe seperti SIMD[DType.float64, 4].
Parameter compile-time tidak terbatas pada lebar vektor. Contoh pada topik ini memakai Int untuk lebar, DType untuk jenis tipe vektor, dan trait sebagai batasan parameter tipe. Nilai parameternya ditulis pada urutan yang sama dengan deklarasi, sehingga fungsi dengan dua parameter menerima nilai dtype lebih dahulu dan lebar kemudian. Kesalahan urutan maupun jenis nilai diperiksa compiler sebelum program dijalankan, karena daftar parameter selalu diselesaikan pada fase kompilasi.
Memparametrisasi lebar dan dtype#
Program berikut memuat dua fungsi berparameter. Fungsi jumlahkan hanya berparameter lebar untuk tipe float64, sedangkan fungsi identitas berparameter dtype dan lebar sekaligus sehingga dapat menerima vektor tipe apa pun.
def jumlahkan[lebar: Int](v: SIMD[DType.float64, lebar]) -> Float64:
return v.reduce_add()
def identitas[dtype: DType, lebar: Int](v: SIMD[dtype, lebar]) -> SIMD[dtype, lebar]:
return v
def main():
var v4 = SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0)
var v8 = SIMD[DType.float64, 8](0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5)
print(jumlahkan[4](v4))
print(jumlahkan[8](v8))
var g = identitas[DType.float32, 2](SIMD[DType.float32, 2](1.5, 2.5))
print(g.reduce_add())# jalankan program parameter lebar dan dtype
mojo run parameter.mojo10.0
32.0
4.0Pemanggilan jumlahkan[4](v4) dan jumlahkan[8](v8) menetapkan lebar pada kurung siku, dan kedua panggilan itu membangun dua spesialisasi dari fungsi yang sama. Keluaran 10.0 dan 32.0 masing-masing berasal dari penjumlahan seluruh lane kedua vektor. Fungsi identitas menuliskan parameter dtype sebelum lebar, dan urutan inilah yang diikuti saat pemanggilan eksplisitnya, yaitu identitas[DType.float32, 2]. Nilai kembaliannya bertipe SIMD[dtype, lebar], sehingga penjumlahan yang menghasilkan keluaran 4.0 dihitung dalam tipe float32.
Parameter tipe generik#
Parameter kurung siku juga dapat menampung tipe secara langsung, bukan hanya angka. Deklarasi def pertama[T: ImplicitlyCopyable](v: T) -> T: membuat T menjadi parameter tipe, yaitu nama tipe yang ditetapkan pemanggil. Batasan ImplicitlyCopyable menuntut tipe yang mengisi T dapat disalin secara implisit, dan batasan itu wajib karena body fungsi mengembalikan v, sebuah tindakan yang menyalin nilai parameternya. Program berikut memanggil fungsi tersebut pada empat tipe berbeda.
def pertama[T: ImplicitlyCopyable](v: T) -> T:
return v
def main():
print(pertama(42))
print(pertama[Int](7))
print(pertama("teks"))
print(pertama[Float64](2.5))# jalankan program parameter tipe generik
mojo run tipe-generik.mojo42
7
teks
2.5Pemanggilan pertama(42) menyimpulkan T sebagai Int langsung dari argumennya, sedangkan pertama[Int](7) menuliskan tipe itu secara eksplisit. Kedua bentuk sah dan menghasilkan keluaran yang sama. Penyimpulan ini berhasil karena T muncul langsung sebagai tipe argumen, sedangkan lebar pada fungsi sebelumnya tersembunyi di dalam SIMD[DType.float64, lebar] sehingga wajib ditulis pada kurung siku. Keluaran teks menunjukkan String juga dapat mengisi T, dan 2.5 menunjukkan hal yang sama untuk Float64.
Setiap pemanggilan dengan tipe yang berbeda juga membangun spesialisasi tersendiri. Fungsi pertama pada program di atas dikompilasi untuk Int, untuk String, dan untuk Float64, dan ketiganya berbagi satu body sumber. Batasan pada parameter tipe menentukan operasi yang sah di dalam body, sehingga body yang menyalin nilai menuntut tipe yang dapat disalin. Pemanggilan dengan tipe yang melanggar batasan tersebut ditolak compiler pada waktu kompilasi.
Kesalahan umum#
- Lupa kurung siku saat memanggil fungsi berparameter lebar. Pemanggilan
jumlahkan(v4)ditolak dengan pesaninvalid call to 'jumlahkan': value passed to 'v' cannot be converted from 'SIMD[.float64, 4]' to 'SIMD[.float64, lebar]', it depends on an unresolved parameter 'lebar'. Perbaikannya adalah menuliskan nilai parameternya, yaitujumlahkan[4](v4). - Membatasi parameter tipe dengan
AnyTypepada fungsi yang mengembalikan nilai parameternya. Definisidef pertama[T: AnyType](v: T) -> Tditolak dengan pesanvalue of type 'T' cannot be implicitly copied, it does not conform to 'ImplicitlyCopyable'karena pengembalian nilai menyalin nilai itu. Perbaikannya adalah batasanImplicitlyCopyableyang menjamin salinan tersebut sah. - Menaruh parameter lebar di dalam kurung biasa. Definisi
def jumlahkan(lebar: Int)yang kemudian menuliskanSIMD[DType.float64, lebar]ditolak dengan pesancannot use a dynamic value in a parameter listkarenalebardianggap nilai runtime. Perbaikannya adalah memindahkan parameternya ke kurung siku pada deklarasi fungsi.
Ringkasan#
- Kurung siku pada deklarasi fungsi menampung parameter compile-time, sedangkan kurung biasa menampung argumen runtime.
- Parameter lebar dan dtype dipanggil secara eksplisit, misalnya
jumlahkan[4](v4), dan setiap nilai menghasilkan spesialisasi tersendiri. - Parameter tipe
Tdengan batasan trait dapat disimpulkan dari argumen atau ditulis eksplisit pada kurung siku. - Batasan
ImplicitlyCopyabledibutuhkan ketika body fungsi menyalin nilai parameternya.
Referensi#
- Parameterized declarations — Mojo Manual — Dokumentasi resmi deklarasi berparameter pada kurung siku.
- Mojo Manual — Modular — Panduan resmi bahasa Mojo yang menjadi rujukan umum topik ini.