Topik ini membahas pemisahan antarmuka dan implementasi sebuah library Carbon melalui dua jenis berkas, yaitu berkas api yang menyatakan kontrak publik dan berkas impl yang mengisi definisinya. Pembahasan bergerak dari aturan kedua jenis berkas, contoh pasangan berkas konversi suhu, hingga pemeriksaan yang dilakukan toolchain atas kecocokan keduanya. Setelah membaca topik ini, sebuah library kecil dapat dipecah menjadi pasangan api dan impl beserta alasan desainnya.
Dua jenis berkas dalam satu library#
Setiap library Carbon terdiri atas tepat satu berkas api dan nol atau lebih berkas impl. Berkas api mendeskripsikan antarmuka (interface) library, yaitu kumpulan deklarasi seluruh nama publik yang dapat digunakan pengguna library, sedangkan berkas impl memuat implementasi dari sebagian atau seluruh deklarasi itu. Dokumen desain menyebut pasangan ini mirip pasangan berkas header dan berkas sumber pada C++, tetapi tanpa kewajiban memecah setiap library, karena library kecil boleh berhenti pada satu berkas api saja.
Jenis berkasnya ditandai langsung pada deklarasi package. Deklarasi package Suhu api; menandai berkas api, sedangkan package Suhu impl; menandai berkas impl dengan nama package yang sama, sehingga keduanya dihitung sebagai satu library. Dokumen desain Carbon menandai dirinya sebagai provisional, yaitu memuat bagian yang belum diputuskan melalui proses Carbon, sehingga ejaan deklarasi ini masih dapat berubah dari versi alat ke versi alat berikutnya. Konvensi penamaan yang direkomendasikan dokumen desain menambahkan akhiran .impl.carbon pada nama berkas impl, misalnya suhu.impl.carbon untuk berkas api bernama suhu.carbon.
Pasangan berkas konversi suhu#
Kontrak dan implementasi menjadi lebih mudah diamati pada pasangan berkas nyata. Contoh berikut memecah fungsi konversi suhu menjadi dua berkas. Berkas pertama, suhu.carbon, adalah berkas api yang hanya memuat deklarasi fungsi konversi.
// tidak-dijalankan
package Suhu api;
fn KeFahrenheit(celsius: i32) -> i32;Berkas kedua, suhu.impl.carbon, menyatakan package yang sama dengan kata impl, lalu mengisi definisi fungsi yang dideklarasikan berkas api.
// tidak-dijalankan
package Suhu impl;
fn KeFahrenheit(celsius: i32) -> i32 {
return celsius * 9 / 5 + 32;
}Titik koma pada akhir deklarasi di berkas api penting untuk diamati, karena fn KeFahrenheit(celsius: i32) -> i32; adalah deklarasi tanpa tubuh, sehingga berkas itu hanya memperkenalkan nama beserta tipenya. Berkas impl mengulang deklarasi yang sama, kali ini dengan tubuh fungsi berisi rumus konversi. Pengguna library cukup membaca berkas api untuk mengetahui apa saja yang tersedia, sedangkan isi berkas impl tidak perlu dibaca sama sekali untuk menggunakan fungsi tersebut.
Kedua berkas di atas tetap ditulis sebagaimana desainnya, dan pasangan api dan impl belum dapat dieksekusi sebagai satu kesatuan karena Compiler Explorer mengeksekusi satu berkas sumber per program. Jalur pengeksekusian yang tersedia di sana adalah menyatukan sisi impl dengan sebuah fungsi Main pada satu berkas, sebagaimana program berikut.
package Suhu impl;
fn KeFahrenheit(celsius: i32) -> i32 {
return celsius * 9 / 5 + 32;
}
fn Main() -> i32 {
Print("100 C = {0} F", KeFahrenheit(100));
return 0;
}100 C = 212 F
result: 0Program ini membuktikan bahwa bentuk berkas impl yang sama, yaitu deklarasi package Suhu impl; beserta definisi fungsinya, diterima dan dijalankan oleh interpreter begitu titik masuknya disediakan. Struktur dua berkas tetap menjadi bentuk yang sahih, sebab bentuknya diambil dari dokumen desain resmi yang menjadi rujukan topik ini, dan pemeriksaan yang menyertainya dijelaskan pada bagian berikutnya dengan sumber yang sama.
Pemeriksaan antarmuka oleh toolchain#
Toolchain memeriksa kecocokan api dan impl melalui beberapa aturan yang ditetapkan dokumen desain. Pertama, setiap deklarasi pada berkas api wajib memiliki definisi di suatu tempat dalam library yang sama, baik di berkas api itu sendiri maupun di salah satu berkas impl. Kedua, berkas impl secara otomatis mengimpor berkas api library-nya, sehingga seluruh nama berkas api sudah terlihat di dalam berkas impl tanpa pernyataan import. Ketiga, deklarasi pada berkas api bersifat publik secara bawaan, sedangkan nama yang hanya muncul di berkas impl bersifat privat dan hanya terlihat di dalam library.
Aturan pertama membuat berkas api berperan sebagai kontrak yang selalu terpenuhi, karena tidak ada deklarasi yang menggantung. Aturan kedua menyederhanakan penulisan berkas impl, karena rumus pada suhu.impl.carbon dapat langsung merujuk tipe dan nama lain yang dideklarasikan berkas api. Aturan ketiga menutup kemungkinan nama internal bocor keluar library, sehingga detail seperti fungsi bantu di berkas impl tidak akan memengaruhi pengguna library mana pun.
Manfaat pemisahan bagi proyek besar#
Manfaat utama pemisahan api dan impl terletak pada pengendalian ketergantungan. Library adalah satuan ketergantungan dalam build Carbon, dan pemisahan ini memungkinkan sistem build membedakan ketergantungan pada antarmuka dari ketergantungan pada implementasi. Apabila rumus pada suhu.impl.carbon diperbaiki, berkas lain yang hanya membaca suhu.carbon tidak perlu dikompilasi ulang sejauh antarmukanya tidak berubah. Pada proyek besar dengan ratusan library, pembedaan ini menekan waktu build dan memperjelas bagian yang benar-benar terdampak sebuah perubahan.
Pemisahan juga berfungsi sebagai dokumentasi yang selalu benar. Berkas api yang ramping memuat semua nama publik beserta tipenya, sehingga kemampuan sebuah library dapat dipahami tanpa membaca implementasinya. Dokumen desain menganjurkan library berukuran kecil, dan pemisahan api-impl membuat library kecil itu tetap mudah dibaca sekaligus tetap dapat tumbuh melalui penambahan berkas impl. Satu hal yang perlu dijaga adalah konsistensi nama package, karena deklarasi package Suhu api; dan package Suhu impl; harus menunjuk package yang sama, dan nama yang berbeda membuat pemeriksa memperlakukannya sebagai dua library yang tidak berkaitan.
Ringkasan#
- Library Carbon terdiri atas tepat satu berkas api untuk antarmuka dan nol atau lebih berkas impl untuk implementasi.
- Deklarasi pada berkas api wajib memiliki definisi di dalam library yang sama, dan berkas impl otomatis mengimpor api library-nya.
- Nama pada berkas api bersifat publik secara bawaan, sedangkan nama yang hanya ada di berkas impl bersifat privat.
- Compiler Explorer saat ini mengeksekusi satu berkas sumber, sehingga pasangan api dan impl belum dapat diuji sebagai satu kesatuan di sana, tetapi berkas impl yang disatukan dengan fungsi
Mainpada satu berkas tetap dapat dieksekusi.
Referensi#
- Code and name organization — Carbon Language
— Dokumen desain yang menetapkan satu berkas api per library, kata kunci impl, konvensi akhiran
.impl.carbon, serta peran api sebagai antarmuka. - Design README — Carbon Language — Bagian Files, libraries, packages dan Name visibility menetapkan aturan definisi, import otomatis pada berkas impl, serta keterbukaan nama.
- Compiler Explorer — Godbolt Compiler Explorer — Layanan tempat program Carbon satu berkas dijalankan, sebagaimana disebut pada batasan topik ini.