Umumnya jenis yang tidak dapat diubah yang dibuat dalam bahasa yang tidak berputar di sekitar ketidakmampuan akan cenderung menghabiskan lebih banyak waktu pengembang untuk membuat serta berpotensi digunakan jika mereka memerlukan beberapa jenis "pembangun" objek untuk mengekspresikan perubahan yang diinginkan (ini tidak berarti bahwa keseluruhan pekerjaan akan lebih banyak, tetapi ada biaya di muka dalam kasus ini). Terlepas dari apakah bahasa membuatnya sangat mudah untuk membuat tipe yang tidak dapat diubah atau tidak, itu akan cenderung selalu membutuhkan beberapa pemrosesan dan overhead memori untuk tipe data yang tidak sepele.
Membuat Fungsi Tanpa Efek Samping
Jika Anda bekerja dalam bahasa yang tidak berputar di sekitar ketidakberdayaan, maka saya pikir pendekatan pragmatis bukanlah berusaha membuat setiap jenis data tunggal tidak dapat diubah. Pola pikir yang berpotensi jauh lebih produktif yang memberi Anda banyak manfaat yang sama adalah fokus pada memaksimalkan jumlah fungsi dalam sistem Anda yang menyebabkan efek samping nol .
Sebagai contoh sederhana, jika Anda memiliki fungsi yang menyebabkan efek samping seperti ini:
// Make 'x' the absolute value of itself.
void make_abs(int& x);
Maka kita tidak memerlukan tipe data integer yang tidak dapat diubah yang melarang operator seperti penugasan pasca inisialisasi untuk membuat fungsi tersebut menghindari efek samping. Kita bisa melakukan ini:
// Returns the absolute value of 'x'.
int abs(int x);
Sekarang fungsi tidak berantakan dengan x
atau apa pun di luar ruang lingkupnya, dan dalam kasus sepele ini kita bahkan mungkin mencukur beberapa siklus dengan menghindari overhead yang terkait dengan tipuan / aliasing. Paling tidak versi kedua seharusnya tidak lebih mahal secara komputasi daripada yang pertama.
Hal-hal Yang Mahal untuk Menyalin Sepenuhnya
Tentu saja sebagian besar kasus tidak sepele ini jika kita ingin menghindari membuat fungsi menyebabkan efek samping. Kasus penggunaan dunia nyata yang kompleks mungkin lebih seperti ini:
// Transforms the vertices of the specified mesh by
// the specified transformation matrix.
void transform(Mesh& mesh, Matrix4f matrix);
Pada titik mana mesh mungkin memerlukan beberapa ratus megabyte memori dengan lebih dari seratus ribu poligon, bahkan lebih banyak simpul dan tepi, beberapa peta tekstur, target morf, dll. Akan sangat mahal untuk menyalin seluruh mesh hanya untuk membuat ini transform
berfungsi bebas dari efek samping, seperti:
// Returns a new version of the mesh whose vertices been
// transformed by the specified transformation matrix.
Mesh transform(Mesh mesh, Matrix4f matrix);
Dan dalam kasus ini di mana menyalin sesuatu secara keseluruhan biasanya akan menjadi overhead epik di mana saya merasa berguna untuk berubah Mesh
menjadi struktur data yang persisten dan tipe yang tidak dapat diubah dengan "pembangun" analogis untuk membuat versi modifikasi dari itu sehingga dapat dengan mudah menyalin dan menghitung jumlah bagian yang tidak unik. Semuanya dengan fokus untuk dapat menulis fungsi mesh yang bebas dari efek samping.
Struktur Data Persisten
Dan dalam kasus-kasus ini di mana menyalin semuanya sangat mahal, saya menemukan upaya mendesain yang kekal Mesh
untuk benar-benar melunasi walaupun biayanya sedikit curam di muka, karena itu tidak hanya menyederhanakan keamanan thread. Ini juga menyederhanakan pengeditan non-destruktif (memungkinkan pengguna untuk melapisi operasi mesh tanpa memodifikasi salinan aslinya), membatalkan sistem (sekarang sistem undo hanya dapat menyimpan salinan mesh yang tidak berubah sebelum perubahan yang dilakukan oleh operasi tanpa meledakkan memori gunakan), dan pengecualian-keselamatan (sekarang jika pengecualian terjadi pada fungsi di atas, fungsi tidak harus memutar kembali dan membatalkan semua efek sampingnya karena tidak menyebabkan apapun untuk memulai).
Saya yakin dapat mengatakan dalam kasus-kasus ini bahwa waktu yang diperlukan untuk membuat struktur data yang besar dan kekal ini menghemat lebih banyak waktu daripada biayanya, karena saya telah membandingkan biaya perawatan dari desain baru ini dengan yang sebelumnya yang berkisar seputar kemampuan berubah-ubah dan fungsi yang menyebabkan efek samping, dan desain sebelumnya yang bisa berubah biaya jauh lebih banyak waktu dan jauh lebih rentan terhadap kesalahan manusia, terutama di daerah yang benar-benar menggoda bagi pengembang untuk mengabaikan selama waktu krisis, seperti pengecualian-keselamatan.
Jadi saya pikir tipe data yang tidak dapat diubah benar-benar terbayar dalam kasus-kasus ini, tetapi tidak semuanya harus dibuat tidak dapat diubah untuk membuat sebagian besar fungsi dalam sistem Anda bebas dari efek samping. Banyak hal yang cukup murah untuk disalin secara penuh. Juga banyak aplikasi dunia nyata yang perlu menyebabkan beberapa efek samping di sana-sini (paling tidak seperti menyimpan file), tetapi biasanya ada lebih banyak fungsi yang bisa tanpa efek samping.
Maksud dari memiliki beberapa tipe data yang tidak dapat diubah kepada saya adalah untuk memastikan bahwa kita dapat menulis jumlah fungsi maksimum agar bebas dari efek samping tanpa menimbulkan overhead epik dalam bentuk penyalinan mendalam struktur data besar-besaran kiri dan kanan penuh ketika hanya sebagian kecil dari mereka perlu dimodifikasi. Memiliki struktur data yang terus-menerus dalam kasus-kasus itu kemudian berakhir menjadi detail optimisasi untuk memungkinkan kita menulis fungsi kita agar bebas dari efek samping tanpa membayar biaya besar untuk melakukannya.
Overhead Abadi
Sekarang secara konseptual versi yang bisa berubah akan selalu memiliki keunggulan dalam efisiensi. Selalu ada overhead komputasi yang terkait dengan struktur data yang tidak dapat diubah. Tapi saya menganggapnya sebagai pertukaran yang layak dalam kasus-kasus yang saya jelaskan di atas, dan Anda dapat fokus untuk membuat biaya overhead yang minimal. Saya lebih suka jenis pendekatan di mana kebenaran menjadi mudah dan optimasi menjadi lebih sulit daripada optimasi menjadi lebih mudah tetapi kebenaran menjadi lebih sulit. Ini hampir tidak demoralisasi untuk memiliki kode yang berfungsi sempurna dengan benar membutuhkan beberapa tune up lebih dari kode yang tidak berfungsi dengan benar di tempat pertama, tidak peduli seberapa cepat itu mencapai hasil yang salah.