Topik ini menerjemahkan program statistik nilai ujian dari C++ ke Carbon, lalu menjalankan hasil terjemahannya pada Carbon Explorer. Pembahasannya bergerak dari pemetaan konstruksi, penulisan program Carbon, hingga perbandingan keluaran dengan program asli. Setelah seluruh bagian dibaca, alur porting yang sama dapat diulang untuk program C++ kecil lain yang kriterianya serupa.
Pemetaan konstruksi dari C++ ke Carbon#
Penerjemahan yang rapi dimulai dari pemetaan konstruksi, yaitu daftar pasangan bentuk C++ dan bentuk Carbon yang setara. Pemetaan dibuat sebelum baris pertama terjemahan ditulis agar keputusan tentang binding, bentuk pemanggilan metode, dan penulisan keluaran tidak diambil secara terpisah-pisah. Tabel berikut merangkum pemetaan yang digunakan, dan setiap bentuk Carbon di dalamnya sudah dijalankan di Explorer.
| Konstruksi pada program C++ | Bentuk Carbon yang digunakan |
|---|---|
int | i32 |
class Statistik dengan anggota publik | class Statistik dengan field var |
metode rata_rata() yang hanya membaca objek | fn RataRata(self: Statistik) -> i32 |
fungsi catat(Statistik, int) dengan nilai kembalian | fn Catat(var s: Statistik, nilai: i32) -> Statistik |
std::cout << ... << "\n" | Print("...: {0}", nilai) |
int main() | fn Main() -> i32 diakhiri return 0; |
Dua hal pada pemetaan memerlukan catatan tambahan. Deklarasi package sample api; wajib ada pada setiap program Carbon, sedangkan program C++ satu berkas ini tidak membutuhkan padanannya. Penulisan keluaran juga berbeda perilaku, karena Print tidak menyisipkan baris baru seperti yang dilakukan "\n" pada program asli, sedangkan panel Compiler Explorer menampilkan hasil setiap pemanggilan sebagai baris yang terpisah. Dengan dua catatan itu, pemetaan pada tabel dapat diterapkan dari atas ke bawah tanpa kejutan.
Alur kerja penerjemahannya sendiri terbagi menjadi empat langkah yang berurutan. Diagram berikut merangkum alur tersebut, dan langkah yang sama dipraktikkan pada bagian-bagian selanjutnya.
Program Carbon hasil terjemahan#
Program berikut merupakan hasil terjemahan lengkap dari statistik.cpp, dengan deklarasi package dan fungsi Main yang mengembalikan i32. Instance Statistik dibangun dari nilai struct yang mengisi kelima field sekaligus, sedangkan fungsi Catat menggunakan parameter var agar field pada salinannya dapat diperbarui. Seluruh keluaran di bawah diperoleh dari eksekusi nyata melalui layanan Compiler Explorer dengan kompiler Explorer (trunk).
package sample api;
class Statistik {
var jumlah: i32;
var total: i32;
var lulus: i32;
var tertinggi: i32;
var terendah: i32;
fn RataRata(self: Statistik) -> i32 {
return self.total / self.jumlah;
}
}
fn Catat(var s: Statistik, nilai: i32) -> Statistik {
s.jumlah += 1;
s.total += nilai;
if (nilai >= 70) {
s.lulus += 1;
}
if (nilai > s.tertinggi) {
s.tertinggi = nilai;
}
if (nilai < s.terendah) {
s.terendah = nilai;
}
return s;
}
fn Main() -> i32 {
var s: Statistik = {.jumlah = 0, .total = 0, .lulus = 0, .tertinggi = 0, .terendah = 100};
s = Catat(s, 78);
s = Catat(s, 92);
s = Catat(s, 65);
s = Catat(s, 88);
s = Catat(s, 71);
Print("Jumlah nilai: {0}", s.jumlah);
Print("Total: {0}", s.total);
Print("Rata-rata: {0}", Statistik.RataRata(s));
Print("Lulus (>= 70): {0}", s.lulus);
Print("Tertinggi: {0}", s.tertinggi);
Print("Terendah: {0}", s.terendah);
return 0;
}Jumlah nilai: 5
Total: 394
Rata-rata: 78
Lulus (>= 70): 4
Tertinggi: 92
Terendah: 65
result: 0Bagian terjemahan yang paling menonjol adalah pemilihan binding. Binding s di dalam Main dideklarasikan dengan var karena isinya ditimpa berulang kali melalui penugasan s = Catat(s, 78); dan seterusnya. Apabila binding ini ditulis dengan let, penugasan yang sama akan ditolak dengan pesan Only a reference expression can be assigned to. Di dalam Catat, parameter s juga diberi awalan var karena field-fieldnya diperbarui melalui += dan penugasan langsung.
Pola pembaruan melalui nilai kembalian lahir dari semantik penyalinan pada Carbon. Setiap pemanggilan Catat bekerja pada salinan sendiri, sehingga perubahan di dalam fungsi tidak pernah merambat ke objek milik pemanggil. Perilaku ini dapat dibuktikan dengan program kecil berikut yang menyalin satu objek Statistik ke binding lain, lalu mengubah salinannya.
package sample api;
class Statistik {
var total: i32;
}
fn Main() -> i32 {
var s: Statistik = {.total = 10};
var t: Statistik = s;
t.total = 99;
Print("s: {0}", s.total);
Print("t: {0}", t.total);
return 0;
}s: 10
t: 99
result: 0Keluarannya menunjukkan bahwa perubahan pada t tidak merambat ke s. Inilah alasan nilai kembalian Catat harus ditampung kembali melalui penugasan, karena tanpanya seluruh pembaruan hanya hidup pada salinan yang segera dibuang.
Pemanggilan metode juga menuntut bentuk tertentu pada interpreter. Panggilan Statistik.RataRata(s) melalui nama tipe dijalankan dengan benar, sedangkan bentuk panggilan titik pada instance seperti s.RataRata() masih ditolak dengan error jumlah argumen. Bentuk eksplisit ini membuat objek yang menjadi self terlihat jelas pada setiap pemanggilan, dan hal yang sama berlaku pada program terjemahan ini.
Membandingkan keluaran C++ dan Carbon#
Perbandingan dilakukan baris demi baris mulai dari Jumlah nilai: 5 hingga Terendah: 65. Keenam baris itu identik pada program C++ dan program Carbon, termasuk urutannya, sehingga perilaku program asli terjaga. Rata-rata 78 pada kedua program berasal dari pembagian bilangan bulat yang sama, yaitu 394 dibagi 5, sehingga tidak ada perbedaan semantik aritmetika yang muncul. Perbandingan semacam ini bermakna karena keluaran program aslinya memang deterministik sejak awal.
Satu-satunya perbedaan berada di luar keluaran program itu sendiri, yaitu baris result: 0 pada panel Explorer. Baris tersebut bukan hasil Print, melainkan catatan Compiler Explorer yang selalu berisi kode kembalian fungsi Main, dan nilainya 0 karena program diakhiri return 0;. Ketika dibandingkan dengan program C++, baris ini diabaikan sebagaimana kode kembalian main() juga tidak tampak pada keluaran terminalnya.
Perbandingan ini sekaligus menandai batas dari terjemahan yang berhasil. Seluruh kebutuhan program asli ternyata dapat diungkapkan pada bentuk Carbon yang tersedia hari ini, dengan dua penyesuaian berupa tipe i32 dan pola nilai kembalian. Penyesuaian itulah yang layak dicatat sebagai temuan, karena program C++ lain belum tentu seberuntung ini dan sebagian kebutuhan mungkin tidak dapat diungkapkan sama sekali.
Ringkasan#
- Terjemahan dimulai dari pemetaan konstruksi yang mencakup tipe, kelas, fungsi, penulisan keluaran, dan bentuk
Main. - Program hasil porting menggunakan binding
varuntuk data yang diperbarui, parametervarpada fungsi pembaruan, dan nilai kembalian untuk membawa statistik baru. - Keluaran enam baris program Carbon identik dengan keluaran program C++ asli, sedangkan baris
result: 0merupakan catatan kode kembalianMaindari Compiler Explorer. - Setiap pemanggilan pembaruan harus menampung nilai kembaliannya, karena fungsi bekerja pada salinan dan semantik penyalinan membuat perubahan tidak merambat ke pemanggil.
Referensi#
- Compiler Explorer — godbolt.org — Layanan daring tempat program terjemahan dan seluruh keluaran Carbon pada topik ini dieksekusi dengan kompiler Explorer (trunk).
- Classes — Carbon Language — Dasar bagi field, instansiasi melalui nilai struct, dan metode dengan parameter self pada program terjemahan.
- Functions — Carbon Language
— Menetapkan bentuk deklarasi fungsi dan klausa kembalian yang dipakai pada fungsi
CatatdanMain. - Values, objects, and expressions — Carbon Language — Menetapkan semantik binding nilai dan kategori ekspresi yang mendasari pola penyalinan pada program ini.