Mengapa banyak bahasa tidak mendukung parameter bernama? [Tutup]

14

Saya hanya berpikir betapa lebih mudahnya membaca kode jika, ketika memanggil suatu fungsi, Anda dapat menulis:

doFunction(param1=something, param2=somethingElse);

Saya tidak bisa memikirkan kekurangan dan itu akan membuat kode lebih mudah dibaca. Saya tahu Anda bisa melewatkan array sebagai satu-satunya argumen dan memiliki kunci array sebagai nama parameter, namun itu masih belum bisa dibaca.

Apakah ada kerugian dari ini yang saya lewatkan? Jika tidak, mengapa banyak bahasa tidak mengizinkan ini?


sumber
1
dapatkah Anda memberikan bahasa yang seharusnya (dan dapat) menamai parameter tetapi tidak memilikinya?
Bryan Chen
1
Saya pikir itu terlalu bertele-tele dalam banyak kasus. Sejauh yang saya lihat, apakah bahasa menggunakan ini atau tidak cenderung bergantung pada apakah itu akan berdampak positif atau negatif terhadap bahasa. Sebagian besar bahasa C-style berjalan dengan baik tanpa itu. Dalam kasus mereka, seringkali cukup jelas apa yang terjadi, dan ketidakhadirannya benar-benar membantu mengurangi kekacauan pada umumnya.
Panzercrisis
2
@SteveFallows: "Named Parameter Idiom" tampaknya seperti nama aneh untuk definisi API yang lancar (seperti yang dinamai oleh Martin Fowler) pada kelas pembangun. Anda dapat menemukan contoh pola yang sama di salah satu item pertama Java Efektif Joshua Bloch .
jhominal
3
Ini belum tentu pertanyaan berbasis opini
Catskul
2
Sepakat. "Mengapa banyak bahasa tidak mendukung parameter bernama?" lebih merupakan masalah sejarah bahasa daripada pendapat. Akan menjadi pendapat jika seseorang bertanya sebagai "Bukankah parameter bernama lebih baik?".
Andy Fleming

Jawaban:

14

Menentukan nama parameter tidak selalu membuat kode lebih mudah dibaca: sebagai contoh, apakah Anda lebih suka membaca min(first=0, second=1)atau min(0, 1)?

Jika Anda menerima argumen paragraf sebelumnya, maka cukup jelas bahwa menentukan nama parameter tidak harus wajib. Mengapa tidak semua bahasa membuat menentukan nama parameter sebagai opsi yang valid?

Saya dapat memikirkan setidaknya dua alasan:

  • Secara umum, memperkenalkan sintaksis kedua untuk kebutuhan yang sudah tercakup (di sini, melewati parameter) adalah sesuatu yang manfaatnya harus seimbang terhadap biaya inheren dari kompleksitas bahasa yang diperkenalkan (baik dalam implementasi bahasa dan dalam model pikiran pemrogram bahasa) ;
  • Itu membuat mengubah nama parameter perubahan API melanggar, yang mungkin tidak sesuai dengan harapan pengembang bahwa mengubah nama parameter adalah perubahan sepele, tidak melanggar;

Perhatikan bahwa saya tidak tahu bahasa apa pun yang mengimplementasikan parameter bernama tanpa juga menerapkan parameter opsional (yang memerlukan parameter bernama) - jadi Anda harus waspada terhadap terlalu tinggi manfaat keterbacaannya, yang dapat lebih konsisten diperoleh dengan definisi Antarmuka Fasih .

jhominal
sumber
6
Saya secara teratur kode dalam beberapa bahasa yang berbeda, saya hampir selalu harus mencari parameter untuk substring sederhana dalam bahasa apa pun. Apakah substring (mulai, panjang) atau substring (mulai, akhir) dan apakah akhir termasuk dalam string?
James Anderson
1
@JamesAnderson - Bagaimana substring parameter bernama memecahkan masalah Anda? Anda masih harus mencari parameter untuk mengetahui apa nama parameter kedua (kecuali kedua fungsi ada dan fitur polimorfisme bahasa memungkinkan parameter tipe yang sama dengan nama yang berbeda).
mouviciel
6
@mouviciel: Parameter bernama tidak banyak digunakan sama sekali untuk penulis , tetapi sangat bagus untuk pembaca . Kita semua tahu betapa pentingnya nama variabel untuk membuat kode yang dapat dibaca, dan parameter bernama memungkinkan pencipta antarmuka untuk memberikan nama parameter yang baik serta nama fungsi yang baik, sehingga memudahkan orang yang menggunakan antarmuka itu untuk menulis kode yang dapat dibaca.
Phoshi
@ Phoshi - Anda benar. Saya baru saja lupa pentingnya membaca kode ketika saya menulis komentar saya sebelumnya.
mouviciel
14

Parameter Yang Dinamai Membuat Kode Lebih Mudah Dibaca, Lebih Keras Untuk Menulis

Ketika saya membaca sepotong kode, parameter bernama dapat memperkenalkan konteks yang membuat kode lebih mudah dimengerti. Perhatikan misalnya konstruktor ini: Color(1, 102, 205, 170). Apakah maksudnya itu? Memang, Color(alpha: 1, red: 102, green: 205, blue: 170)akan jauh lebih mudah dibaca. Tapi sayangnya, Compiler mengatakan "tidak" - ia ingin Color(a: 1, r: 102, g: 205, b: 170). Saat menulis kode menggunakan parameter bernama, Anda menghabiskan waktu yang tidak perlu mencari nama yang tepat - lebih mudah untuk melupakan nama-nama persis dari beberapa parameter daripada melupakan urutannya.

Ini sekali menggigit saya ketika menggunakan DateTimeAPI yang memiliki dua kelas saudara untuk poin dan durasi dengan antarmuka yang hampir sama. Sementara DateTime->new(...)menerima second => 30argumen, yang DateTime::Duration->new(...)diinginkan seconds => 30, dan serupa untuk unit lain. Ya, itu benar-benar masuk akal, tetapi ini menunjukkan kepada saya bahwa parameter bernama ≠ kemudahan penggunaan.

Nama Buruk Bahkan Tidak Membuatnya Lebih Mudah Dibaca

Contoh lain bagaimana parameter bernama bisa jelek mungkin bahasa R. Sepotong kode ini menciptakan plot data:

plot(plotdata$n, plotdata$mu, type="p", pch=17,  lty=1, bty="n", ann=FALSE, axes=FALSE)

Anda melihat dua argumen posisi untuk baris data x dan y , dan kemudian daftar parameter bernama. Ada banyak lagi opsi dengan standar, dan hanya yang terdaftar yang standarnya ingin saya ubah atau tetapkan secara eksplisit. Setelah kami mengabaikan bahwa kode ini menggunakan angka ajaib, dan dapat mengambil manfaat dari menggunakan enum (jika R punya!), Masalahnya adalah bahwa banyak dari nama parameter ini agak tidak dapat diuraikan.

  • pchsebenarnya karakter plot, mesin terbang yang akan ditarik untuk setiap titik data. 17adalah lingkaran kosong, atau sesuatu seperti itu.
  • ltyadalah tipe garis. Inilah 1garis yang kuat.
  • btyadalah tipe kotak. Mengaturnya untuk "n"menghindari kotak yang ditarik di sekitar plot.
  • ann mengontrol tampilan anotasi sumbu.

Untuk seseorang yang tidak tahu arti setiap singkatan, opsi ini agak membingungkan. Ini juga mengungkapkan mengapa R menggunakan label ini: Bukan sebagai kode yang mendokumentasikan diri, tetapi (menjadi bahasa yang diketik secara dinamis) sebagai kunci untuk memetakan nilai ke variabel yang benar.

Sifat Parameter Dan Tanda Tangan

Tanda tangan fungsi mungkin memiliki properti berikut:

  • Argumen dapat dipesan atau tidak disusun,
  • dinamai atau tidak disebutkan namanya,
  • diperlukan atau opsional.
  • Tanda tangan juga bisa kelebihan beban berdasarkan ukuran atau jenis,
  • dan dapat memiliki ukuran yang tidak ditentukan dengan varargs.

Bahasa yang berbeda mendarat pada koordinat yang berbeda dari sistem ini. Dalam C, argumen diperintahkan, tidak disebutkan namanya, selalu diperlukan, dan dapat menjadi varargs. Di Jawa situasinya serupa, kecuali bahwa tanda tangan dapat kelebihan beban. Di Objective C, tanda tangan diperintahkan, dinamai, diperlukan, dan tidak dapat kelebihan karena itu hanya gula sintaksis sekitar C.

Bahasa yang diketik secara dinamis dengan varargs (antarmuka baris perintah, Perl, ...) dapat meniru parameter bernama opsional. Bahasa dengan ukuran tanda tangan berlebihan memiliki sesuatu seperti parameter opsional posisi.

Bagaimana Tidak Untuk Menerapkan Parameter Yang Bernama

Ketika memikirkan parameter bernama, kita biasanya mengasumsikan parameter bernama, opsional, tidak berurutan. Menerapkan ini sulit.

Parameter opsional dapat memiliki nilai default. Ini harus ditentukan oleh fungsi yang dipanggil dan tidak boleh dikompilasi ke dalam kode panggilan. Jika tidak, default tidak dapat diperbarui tanpa mengkompilasi ulang semua kode dependen.

Sekarang pertanyaan penting adalah bagaimana argumen sebenarnya diteruskan ke fungsi. Dengan parameter yang dipesan, args dapat dikirimkan dalam register, atau dalam urutan yang melekat pada stack. Ketika kami mengecualikan register untuk sesaat, masalahnya adalah bagaimana cara menempatkan argumen opsional yang tidak diurutkan ke dalam tumpukan.

Untuk itu, kita perlu beberapa urutan atas argumen opsional. Bagaimana jika kode deklarasi diubah? Karena urutannya tidak relevan, pemesanan ulang dalam deklarasi fungsi tidak boleh mengubah posisi nilai pada stack. Kami juga harus mempertimbangkan jika menambahkan parameter opsional baru dimungkinkan. Dari perspektif pengguna, ini kelihatannya demikian, karena kode yang tidak menggunakan parameter sebelumnya harus tetap bekerja dengan parameter baru. Jadi ini tidak termasuk pemesanan seperti menggunakan urutan dalam deklarasi atau menggunakan urutan alfabet.

Pertimbangkan ini juga dalam terang subtyping dan Prinsip Pergantian Liskov - dalam output yang dikompilasi, instruksi yang sama harus dapat memanggil metode pada subtipe dengan parameter yang mungkin baru bernama, dan pada supertipe.

Kemungkinan Implementasi

Jika kita tidak dapat memiliki urutan yang pasti, maka kita memerlukan beberapa struktur data yang tidak teratur.

  • Implementasi yang paling sederhana adalah dengan hanya memberikan nama parameter beserta nilainya. Ini adalah bagaimana params yang dinamai diemulasi dalam Perl atau dengan alat baris perintah. Ini menyelesaikan semua masalah ekstensi yang disebutkan di atas, tetapi bisa menjadi pemborosan ruang - bukan pilihan dalam kode kinerja-kritis. Juga, memproses params yang dinamai ini sekarang jauh lebih rumit daripada sekadar mengeluarkan nilai dari stack.

    Sebenarnya, persyaratan ruang dapat dikurangi dengan menggunakan penggabungan string, yang dapat mengurangi perbandingan string di kemudian hari menjadi perbandingan pointer (kecuali jika tidak dapat dijamin bahwa string statis sebenarnya dikumpulkan, dalam hal ini kedua string harus dibandingkan dalam detail).

  • Sebagai gantinya, kami juga bisa meneruskan struktur data pintar yang berfungsi sebagai kamus argumen bernama. Ini murah di sisi penelepon, karena set kunci secara statis dikenal di lokasi panggilan. Ini akan memungkinkan untuk membuat fungsi hash yang sempurna atau untuk menghitung ulang sebuah trie. Callee masih harus menguji keberadaan semua nama parameter yang mungkin agak mahal. Sesuatu seperti ini digunakan oleh Python.

Jadi itu terlalu mahal dalam banyak kasus

Jika suatu fungsi dengan parameter bernama harus dapat diperluas dengan benar, pemesanan definitif tidak dapat diasumsikan. Jadi hanya ada dua solusi:

  • Buat urutan params bernama bagian dari tanda tangan, dan larang perubahan nanti. Ini berguna untuk mendokumentasikan kode diri, tetapi tidak membantu dengan argumen opsional.
  • Berikan struktur data nilai kunci ke callee, yang kemudian harus mengekstrak informasi yang berguna. Ini sangat mahal sebagai perbandingan, dan biasanya hanya terlihat dalam bahasa scripting tanpa penekanan pada kinerja.

Perangkap Lainnya

Nama-nama variabel dalam deklarasi fungsi biasanya memiliki beberapa makna internal dan bukan bagian dari antarmuka - bahkan jika banyak alat dokumentasi masih akan menunjukkannya. Dalam banyak kasus Anda ingin nama yang berbeda untuk variabel internal dan argumen bernama yang sesuai. Bahasa yang tidak memungkinkan memilih nama yang terlihat secara eksternal dari parameter bernama tidak mendapatkan banyak dari mereka jika nama variabel tidak digunakan dengan konteks panggilan dalam pikiran.

Masalah dengan emulasi argumen bernama adalah kurangnya pemeriksaan statis pada sisi pemanggil. Ini sangat mudah untuk dilupakan ketika melewati kamus argumen (melihat Anda, Python). Hal ini penting karena lewat kamus adalah solusi umum, misalnya dalam JavaScript: foo({bar: "baz", qux: 42}). Di sini, baik jenis nilai maupun keberadaan atau ketiadaan nama-nama tertentu dapat diperiksa secara statis.

Mengemulasi Parameter Bernama (Dalam Bahasa yang Diketik Secara Statis)

Cukup menggunakan string sebagai kunci, dan objek apa pun sebagai nilai tidak terlalu berguna di hadapan pemeriksa tipe statis. Namun, argumen bernama dapat ditiru dengan struct, atau objek literal:

// Java

static abstract class Arguments {
  public String bar = "default";
  public int    qux = 0;
}

void foo(Arguments args) {
  ...
}

/* using an initializer block */
foo(new Arguments(){{ bar = "baz"; qux = 42; }});
amon
sumber
3
" it is easier to forget the exact names of some parameters than it is to forget their order" ... itu pernyataan yang menarik.
Catskul
1
Contoh tandingan pertama bukanlah masalah dengan parameter bernama, dan lebih merupakan kekurangan dengan bagaimana bentuk jamak bahasa Inggris memetakan nama bidang dalam antarmuka. Contoh tandingan kedua juga merupakan masalah dengan pengembang membuat pilihan penamaan yang buruk. (Tidak ada pilihan antarmuka parameter kelulusan adalah dengan sendirinya akan menjadi cukup kondisi untuk dibaca - pertanyaannya adalah apakah atau tidak itu adalah diperlukan kondisi atau bantuan ke arah itu dalam beberapa kasus).
mtraceur
1
+1 untuk poin keseluruhan tentang biaya implementasi dan pengorbanan untuk melakukan parameter bernama. Saya hanya berpikir awal akan kehilangan orang.
mtraceur
@ traceur Anda benar. Sekarang, 5 tahun kemudian, saya mungkin menulis jawabannya dengan sangat berbeda. Jika mau, Anda dapat mengedit jawaban saya untuk menghapus hal-hal yang kurang bermanfaat. (Biasanya suntingan seperti itu akan ditolak karena bertentangan dengan maksud penulis, tetapi di sini saya setuju dengan itu). Misalnya saya sekarang percaya bahwa banyak masalah parameter bernama hanya pembatasan bahasa dinamis, dan mereka hanya perlu mendapatkan sistem tipe. Misalnya JavaScript memiliki Flow dan TypeScript, Python memiliki MyPy. Dengan integrasi IDE yang tepat yang menghilangkan sebagian besar masalah kegunaan. Saya masih menunggu sesuatu di Perl.
amon
7

Untuk alasan yang sama bahwa Notasi Hongaria tidak lagi digunakan secara luas; Intellisense (atau yang setara secara moral dalam IDE non-Microsoft). Sebagian besar IDE modern akan memberi tahu Anda segala sesuatu yang perlu Anda ketahui tentang parameter dengan hanya mengarahkan referensi parameter.

Yang mengatakan, sejumlah bahasa mendukung parameter bernama, termasuk C # dan Delphi. Dalam C #, ini opsional, jadi Anda tidak perlu menggunakannya jika tidak mau, dan ada cara lain untuk secara khusus mendeklarasikan anggota, seperti inisialisasi objek.

Parameter Bernama sebagian besar berguna ketika Anda hanya ingin menentukan subset parameter opsional, dan tidak semuanya. Dalam C #, ini sangat berguna karena Anda tidak lagi memerlukan banyak metode kelebihan beban untuk memberikan fleksibilitas kepada programmer ini.

Robert Harvey
sumber
4
Python menggunakan parameter bernama, dan itu adalah fitur luar biasa dari bahasa tersebut. Saya berharap lebih banyak bahasa menggunakannya. Itu membuat argumen default lebih mudah karena Anda tidak lagi dipaksa, seperti dalam C ++, untuk secara eksplisit menentukan argumen "sebelum" default. (Seperti yang Anda miliki di paragraf terakhir, itu adalah bagaimana python lolos tanpa memiliki kelebihan ... argumen default dan argumen nama memungkinkan Anda melakukan semua hal yang sama.) Argumen yang dinamai juga membuat kode lebih mudah dibaca. Ketika Anda memiliki sesuatu seperti sin(radians=2), Anda tidak perlu "Intellisense".
Gort the Robot
2

Bash tentu saja mendukung gaya pemrograman ini, dan baik Perl maupun Ruby mendukung lewatnya daftar parameter nama / nilai (seperti halnya bahasa apa pun dengan dukungan hash / peta asli). Tidak ada yang menghentikan Anda dari memilih untuk mendefinisikan struct / record atau hash / map yang melampirkan nama ke parameter.

Untuk bahasa yang menyertakan toko hash / map / keyvalue secara asli, Anda bisa mendapatkan fungsionalitas yang Anda inginkan, cukup dengan mengadopsi idiom untuk meneruskan hash / nilai hash ke fungsi / metode Anda. Setelah Anda mencobanya pada suatu proyek, Anda akan memiliki wawasan yang lebih baik tentang apakah Anda telah mendapatkan sesuatu, baik dari peningkatan produktivitas yang berasal dari kemudahan penggunaan, atau peningkatan kualitas melalui pengurangan cacat.

Perhatikan bahwa programmer berpengalaman telah merasa nyaman dengan melewati parameter berdasarkan urutan / slot. Anda juga dapat menemukan bahwa idiom ini hanya bernilai ketika Anda memiliki lebih dari beberapa argumen (katakan> 5/6). Dan karena sejumlah besar argumen sering menunjukkan metode yang terlalu kompleks, Anda mungkin menemukan bahwa ungkapan ini hanya bermanfaat untuk metode yang paling kompleks.

ChuckCottrill
sumber
2

Saya pikir itu alasan yang sama mengapa c # lebih populer daripada VB.net. Sementara VB.NET lebih "dapat dibaca" misalnya Anda mengetik "end if" daripada braket penutup, itu hanya berakhir menjadi lebih banyak hal yang melengkapi kode dan akhirnya membuatnya lebih sulit untuk dipahami.

Ternyata apa yang membuat kode lebih mudah dipahami adalah keringkasan. Semakin sedikit semakin baik. Nama parameter fungsi biasanya cukup jelas, dan jangan terlalu jauh untuk membantu Anda memahami kode.

Rocklan
sumber
1
Saya sangat tidak setuju bahwa "semakin sedikit semakin baik". Dibawa ke ekstrem yang logis, pemenang golf kode akan menjadi program "terbaik".
Riley Major
2

Parameter bernama adalah solusi untuk masalah dalam refactoring kode sumber dan tidak dimaksudkan untuk membuat kode sumber lebih mudah dibaca . Parameter bernama digunakan untuk membantu kompiler / pengurai dalam menyelesaikan nilai default untuk panggilan fungsi. Jika Anda ingin membuat kode lebih mudah dibaca, daripada menambahkan komentar yang bermakna.

Bahasa yang sulit untuk refactor sering mendukung parameter bernama, karena foo(1)akan rusak ketika tanda tangan foo()perubahan, tetapi foo(name:1)lebih kecil kemungkinannya untuk istirahat yang membutuhkan lebih sedikit usaha dari programmer untuk melakukan perubahan foo.

Apa yang Anda lakukan ketika Anda harus memperkenalkan parameter baru ke fungsi berikut, dan ada ratusan atau ribuan baris kode yang memanggil fungsi itu?

foo(int weight, int height, int age = 0);

Sebagian besar programmer akan melakukan hal berikut.

foo(int weight, int height, int age = 0, string gender = null);

Sekarang, tidak ada refactoring yang diperlukan dan fungsi dapat dijalankan dalam mode lawas saat gendernol.

Untuk spesifik gendernilai sekarang HARDCODED menjadi panggilan untuk age. Seperti contoh ini:

 foo(10,10,0,"female");

Programmer melihat definisi fungsi, melihat yang agememiliki nilai default 0dan menggunakan nilai itu.

Sekarang nilai default untuk agedefinisi fungsi sama sekali tidak berguna.

Parameter bernama memungkinkan Anda untuk menghindari masalah ini.

foo(weight: 10, height: 10, gender: "female");

Parameter baru dapat ditambahkan ke foodan Anda tidak perlu khawatir tentang urutan mereka ditambahkan dan Anda dapat mengubah nilai default mengetahui bahwa default yang Anda atur adalah benar-benar nilai default yang sebenarnya.

Reactgular
sumber