Atribut intent memasangkan setiap argumen dummy dengan kontrak (contract) yang menyatakan arah aliran datanya, yaitu hanya dibaca, hanya ditulis, atau dibaca sekaligus diperbarui. Nilainya ada tiga, yaitu intent(in), intent(out), dan intent(inout). Kontrak ini bukan sekadar komentar, melainkan aturan yang diperiksa compiler, yaitu program yang menerjemahkan kode sumber menjadi program mesin, pada saat kompilasi. Topik ini membahas makna ketiga nilai tersebut, perilaku standar intent(out) saat prosedur dimasuki, dua pesan error nyata dari gfortran 14.2.0, serta syarat antarmuka eksplisit agar penegakan kontrak berlaku penuh.
Tiga arah aliran data#
intent(in) menandai argumen yang hanya boleh dibaca di dalam prosedur, sehingga prosedur menjanjikan argumen aktualnya tidak berubah. intent(out) menandai argumen yang nilainya dihasilkan prosedur dan tidak boleh dibaca sebelum ditugaskan. intent(inout) menandai argumen yang dibaca terlebih dahulu, lalu diperbarui, misalnya saldo yang ditambah setiap pemanggilan. Satu subrutin boleh memuat ketiganya sekaligus, sebagaimana fragmen berikut.
! tidak-dijalankan
subroutine contoh(masukan, keluaran, dua_arah)
implicit none
real, intent(in) :: masukan
real, intent(out) :: keluaran
real, intent(inout) :: dua_arah
keluaran = masukan * 2.0
dua_arah = dua_arah + masukan
end subroutine contohFragmen tersebut membaca masukan untuk menghitung keluaran, lalu menambah dua_arah dengan nilai masukan. Setiap argumen memperoleh satu baris deklarasi tersendiri, sehingga ketiga kontrak dapat diperiksa sekilas tanpa membaca tubuh subrutin. Konvensi yang lazim menempatkan intent(in) lebih dahulu, disusul intent(inout), kemudian intent(out), mengikuti aliran data dari masukan menuju keluaran.
Kontrak ini juga berfungsi sebagai dokumentasi yang selalu mutakhir, karena tertanam pada deklarasi yang diperiksa compiler, bukan pada komentar yang bisa tertinggal. Pembaca kepala prosedur langsung mengetahui argumen mana yang tidak akan berubah dan mana yang akan diisi. Dengan demikian, intent menggabungkan dua peran sekaligus, yaitu penjaga kebenaran di sisi compiler dan petunjuk bagi pembaca kode yang memanggil prosedur tersebut.
Program dengan kontrak lengkap#
Program berikut menggunakan ketiga kontrak dalam satu berkas. Subrutin ke_fahrenheit menerima suhu Celsius dan menghasilkan Fahrenheit, subrutin tambah_bonus memperbarui nilai yang sudah ada, dan subrutin reset mengisi ulang variabel dari nol. Ketiga subrutin ditempatkan sebagai prosedur internal, sehingga pemeriksaan kontraknya berlaku pada setiap pemanggilan.
program konversi
implicit none
real :: c, f
real :: nilai
! intent(in) dan intent(out)
c = 25.0
call ke_fahrenheit(c, f)
print '(a, f6.1)', 'Celsius =', c
print '(a, f6.1)', 'Fahrenheit =', f
! intent(inout) membaca nilai lama dan memperbaruinya
nilai = 80.0
call tambah_bonus(nilai, 2.5)
print '(a, f6.1)', 'Nilai sesudah bonus =', nilai
! intent(out) mengosongkan argumen aktual saat masuk prosedur
nilai = 42.0
call reset(nilai)
print '(a, f6.1)', 'Nilai sesudah reset =', nilai
contains
subroutine ke_fahrenheit(celsius, fahrenheit)
implicit none
real, intent(in) :: celsius
real, intent(out) :: fahrenheit
fahrenheit = celsius * 9.0 / 5.0 + 32.0
end subroutine ke_fahrenheit
subroutine tambah_bonus(nilai, bonus)
implicit none
real, intent(inout) :: nilai
real, intent(in) :: bonus
nilai = nilai + bonus
end subroutine tambah_bonus
subroutine reset(nilai)
implicit none
real, intent(out) :: nilai
! compiler menganggap nilai tak terdefinisi saat prosedur dimasuki
nilai = 0.0
end subroutine reset
end program konversi# mengompilasi program konversi
gfortran konversi.f90 -o konversi
# menjalankan hasil kompilasi
./konversiCelsius = 25.0
Fahrenheit = 77.0
Nilai sesudah bonus = 82.5
Nilai sesudah reset = 0.0Perhatikan subrutin reset pada bagian contains. Standar Fortran menetapkan bahwa argumen aktual yang menyandang intent(out) menjadi tidak terdefinisi pada saat prosedur dimasuki, seolah-olah nilai lamanya dibuang terlebih dahulu. Karena itu reset cukup menugaskan nilai baru dan tidak perlu membaca nilai lama. Keluaran menunjukkan nilai berpindah dari 42.0 ke 0.0, sesuai dengan penugasan di dalam subrutin. Perilaku ini menjadi jebakan bila argumen intent(out) tidak diisi pada semua jalur eksekusi prosedur, karena pemanggil menerima nilai yang tidak terdefinisi.
Compiler menolak penugasan pada intent(in)#
Kontrak intent(in) ditegakkan langsung di dalam tubuh prosedur. Berkas berikut sengaja menugaskan argumen dummy celsius yang menyandang intent(in), sehingga kompilasinya gagal. Pesan errornya ditampilkan persis seperti yang dicetak compiler.
program salah_intent
implicit none
real :: c
c = 25.0
call perbaiki(c)
print '(a, f6.1)', 'Celsius =', c
contains
subroutine perbaiki(celsius)
implicit none
real, intent(in) :: celsius
celsius = celsius + 1.0
print '(a, f6.1)', 'Celsius =', celsius
end subroutine perbaiki
end program salah_intent# mencoba mengompilasi berkas yang menugaskan argumen intent(in)
gfortran salah-intent.f90 -o salah-intentsalah-intent.f90:14:4:
14 | celsius = celsius + 1.0
| 1
Error: Dummy argument ‘celsius’ with INTENT(IN) in variable definition context (assignment) at (1)Compiler menunjuk baris 14 kolom 4, tepat pada penugasan celsius = celsius + 1.0, dengan pesan bahwa argumen dummy yang menyandang INTENT(IN) berada dalam konteks definisi variabel. Kesalahan ini tertangkap sebelum program dijalankan, sehingga pelanggaran kontrak tidak pernah sampai ke pengguna program. Perbaikannya menyesuaikan maksud prosedur, yaitu mengubah kontrak menjadi intent(inout) bila argumen memang harus diperbarui, atau menghitung hasil tanpa mengubah argumen masukan.
Penegakan lewat antarmuka eksplisit#
Kontrak argumen baru diperiksa penuh ketika compiler mengenali antarmuka (interface) prosedur pada titik pemanggilannya, misalnya karena prosedur tersebut internal atau terletak di dalam modul. Berkas berikut meneruskan konstanta 42.0 ke argumen intent(out), dan pelanggarannya tertangkap saat kompilasi karena reset adalah prosedur internal. Tanpa antarmuka eksplisit, konstanta yang sama baru bermasalah pada saat program berjalan.
program salah_panggil
implicit none
call reset(42.0)
contains
subroutine reset(nilai)
implicit none
real, intent(out) :: nilai
nilai = 0.0
end subroutine reset
end program salah_panggil# mencoba meneruskan konstanta ke argumen intent(out)
gfortran salah-panggil.f90 -o salah-panggilsalah-panggil.f90:4:13:
4 | call reset(42.0)
| 1
Error: Non-variable expression in variable definition context (actual argument to INTENT = OUT/INOUT) at (1)Pesan error menyatakan bahwa ekspresi bukan variabel berada dalam konteks definisi variabel karena menjadi argumen aktual untuk INTENT = OUT atau INOUT. Pemeriksaan semacam ini mustahil dilakukan tanpa antarmuka eksplisit. Prosedur eksternal yang dikompilasi terpisah menyembunyikan kontraknya dari pemanggil, sehingga konstanta yang diteruskan ke argumen yang akan diubah lolos kompilasi dan baru terasa sebagai perilaku tidak terdefinisi saat program berjalan. Kebiasaan yang aman adalah memberi intent pada setiap argumen dummy dan menempatkan prosedur pada posisi yang antarmukanya eksplisit, misalnya di dalam modul atau setelah pernyataan contains.
Kesalahan umum#
Kesalahan pertama adalah menganggap argumen intent(out) membawa nilai lama ke dalam prosedur. Asumsi ini muncul karena argumen aktual memang berisi nilai sebelum pemanggilan, padahal standar menetapkan nilai itu tidak terdefinisi pada saat prosedur dimasuki. Bila nilai lama memang diperlukan untuk perhitungan, kontraknya adalah intent(inout), bukan intent(out).
Kesalahan kedua adalah tidak menugaskan argumen intent(out) pada semua jalur eksekusi, misalnya nilainya hanya diisi di dalam salah satu cabang if. Prosedur semacam itu lolos kompilasi, tetapi pemanggil menerima nilai yang tidak terdefinisi apabila eksekusi berjalan pada cabang yang tidak menugaskannya. Kebiasaan yang aman adalah menugaskan nilai bawaan di awal prosedur, lalu menimpanya pada cabang yang relevan.
Kesalahan ketiga adalah memberi intent pada prosedur eksternal lalu mengira compiler ikut memeriksa setiap pemanggilannya. Kontrak tetap tercatat di dalam prosedur, tetapi pemanggil yang tidak melihat antarmuka eksplisit tidak diperiksa sama sekali. Penempatan prosedur di dalam modul atau sebagai prosedur internal menutup celah ini.
Ringkasan#
intent(in),intent(out), danintent(inout)adalah kontrak arah aliran data pada argumen dummy yang diperiksa compiler pada saat kompilasi.- Argumen aktual yang menyandang
intent(out)menjadi tidak terdefinisi pada saat prosedur dimasuki, sehingga prosedur wajib menugaskannya pada semua jalur eksekusi. - Pelanggaran kontrak tertangkap saat kompilasi hanya bila prosedur memiliki antarmuka eksplisit, misalnya prosedur internal atau prosedur modul.
Referensi#
- Learn — Fortran Programming Language — Halaman belajar resmi komunitas Fortran yang memuat tutorial quickstart dan praktik terbaik.
- The GNU Fortran Compiler — GNU Project — Manual compiler gfortran yang memuat opsi kompilasi dan karakteristik bahasa yang didukung.