Bahasa utama saya diketik secara statis (Jawa). Di Jawa, Anda harus mengembalikan satu jenis dari setiap metode. Misalnya, Anda tidak dapat memiliki metode yang mengembalikan secara String
kondisional atau mengembalikan secara kondisional Integer
. Tetapi dalam JavaScript, misalnya, ini sangat mungkin.
Dalam bahasa yang diketik secara statis saya mengerti mengapa ini adalah ide yang buruk. Jika setiap metode dikembalikan Object
(orang tua umum semua kelas mewarisi dari) maka Anda dan kompiler tidak tahu apa yang Anda hadapi. Anda harus menemukan semua kesalahan saat dijalankan.
Tetapi dalam bahasa yang diketik secara dinamis, bahkan mungkin tidak ada kompiler. Dalam bahasa yang diketik secara dinamis, tidak jelas bagi saya mengapa fungsi yang mengembalikan beberapa jenis adalah ide yang buruk. Latar belakang saya dalam bahasa statis membuat saya menghindari penulisan fungsi-fungsi seperti itu, tetapi saya khawatir saya terlalu dekat dengan fitur yang bisa membuat kode saya lebih bersih dengan cara yang tidak bisa saya lihat.
Sunting : Saya akan menghapus contoh saya (sampai saya bisa memikirkan yang lebih baik). Saya pikir itu mengarahkan orang untuk menjawab suatu hal yang tidak saya coba sampaikan.
sumber
(coerce var 'string)
hasilstring
atau(concatenate 'string this that the-other-thing)
juga. Saya sudah menulis hal-hal sepertiThingLoader.getThingById (Class<extends FindableThing> klass, long id)
juga. Dan, di sana, saya mungkin hanya mengembalikan sesuatu yang mensubclasskan apa yang Anda minta:loader.getThingById (SubclassA.class, 14)
mungkin mengembalikanSubclassB
yang meluasSubclassA
...Jawaban:
Berbeda dengan jawaban lain, ada kasus di mana mengembalikan berbagai jenis dapat diterima.
Contoh 1
Dalam beberapa bahasa yang diketik secara statis, ini melibatkan kelebihan beban, jadi kami dapat mempertimbangkan bahwa ada beberapa metode, masing-masing mengembalikan jenis yang telah ditentukan sebelumnya, jenis tetap. Dalam bahasa dinamis, ini mungkin fungsi yang sama, diimplementasikan sebagai:
Fungsi yang sama, berbagai jenis nilai pengembalian.
Contoh 2
Bayangkan Anda mendapat respons dari komponen OpenID / OAuth. Beberapa penyedia OpenID / OAuth mungkin berisi lebih banyak informasi, seperti usia orang tersebut.
Orang lain akan memiliki minimum, apakah itu alamat email atau nama samaran.
Sekali lagi, fungsi yang sama, hasil yang berbeda.
Di sini, manfaat mengembalikan jenis yang berbeda sangat penting dalam konteks di mana Anda tidak peduli dengan jenis dan antarmuka, tetapi benda apa yang sebenarnya berisi. Misalnya, mari kita bayangkan sebuah situs web berisi bahasa dewasa. Maka
findCurrent()
dapat digunakan seperti ini:Refactoring ini ke dalam kode di mana setiap penyedia akan memiliki fungsinya sendiri yang akan mengembalikan tipe yang terdefinisi dengan baik dan tidak hanya akan menurunkan basis kode dan menyebabkan duplikasi kode, tetapi juga tidak akan membawa manfaat apa pun. Seseorang mungkin akhirnya melakukan kengerian seperti:
sumber
sum
contoh Anda .sum :: Num a => [a] -> a
. Anda dapat menjumlahkan daftar apa pun yang merupakan angka. Tidak seperti javascript, jika Anda mencoba menjumlahkan sesuatu yang bukan angka, kesalahan akan ditangkap pada waktu kompilasi.Iterator[A]
memiliki metodedef sum[B >: A](implicit num: Numeric[B]): B
, yang sekali lagi memungkinkan untuk menjumlahkan semua jenis angka dan diperiksa pada waktu kompilasi.+
untuk string (dengan mengimplementasikanNum
, yang merupakan ide yang buruk, desain bijaksana tetapi legal) atau menciptakan operator / fungsi yang berbeda yang kelebihan beban oleh bilangan bulat dan string masing-masing. Haskell memiliki polimorfisme ad-hoc melalui kelas tipe. Daftar yang berisi bilangan bulat dan string jauh lebih sulit (mungkin tidak mungkin tanpa ekstensi bahasa) tetapi masalah yang agak berbeda.null
nilai.Secara umum, ini adalah ide yang buruk karena alasan yang sama dengan persamaan moral dalam bahasa yang diketik secara statis adalah ide yang buruk: Anda tidak tahu tipe konkret dikembalikan, jadi Anda tidak tahu apa yang dapat Anda lakukan dengan hasilnya (selain dari beberapa hal yang dapat dilakukan dengan nilai apa pun). Dalam sistem tipe statis, Anda memiliki anotasi yang diperiksa kompiler untuk jenis kembali dan semacamnya, tetapi dalam bahasa dinamis pengetahuan yang sama masih ada - itu hanya informal dan disimpan dalam otak dan dokumentasi daripada dalam kode sumber.
Dalam banyak kasus, bagaimanapun, ada sajak dan alasan yang jenis dikembalikan dan efeknya mirip dengan kelebihan beban atau polimorfisme parametrik dalam sistem tipe statis. Dengan kata lain, jenis hasil yang diprediksi, itu hanya tidak cukup sederhana untuk mengekspresikan.
Tetapi perhatikan bahwa mungkin ada alasan lain mengapa fungsi tertentu dirancang dengan buruk: Misalnya,
sum
fungsi mengembalikan false pada input yang tidak valid adalah ide yang buruk terutama karena nilai pengembalian tidak berguna dan rentan kesalahan (0 <-> kebingungan salah).sumber
NaN
"tandingan" sama sekali. NaN sebenarnya adalah input yang valid untuk setiap perhitungan floating-point. Anda dapat melakukan apa saja denganNaN
yang dapat Anda lakukan dengan angka aktual. Benar, hasil akhir dari perhitungan tersebut mungkin tidak terlalu berguna bagi Anda, tetapi itu tidak akan menyebabkan kesalahan runtime yang aneh dan Anda tidak perlu memeriksanya setiap saat - Anda hanya dapat memeriksa sekali di akhir serangkaian perhitungan. NaN bukan tipe yang berbeda , hanya nilai khusus , semacam Null Object .NaN
,, seperti objek nol, cenderung bersembunyi ketika kesalahan terjadi, hanya untuk mereka muncul nanti. Misalnya, program Anda kemungkinan besar akan meledak jikaNaN
masuk ke loop kondisional.NaN
nilainya adalah (a) sebenarnya bukan tipe pengembalian yang berbeda dan (b) memiliki semantik floating-point yang terdefinisi dengan baik, dan karena itu benar-benar tidak memenuhi syarat sebagai analog dengan nilai pengembalian yang diketik secara bervariasi. Tidak semua yang berbeda dari nol dalam aritmatika integer, sungguh; jika nol "tanpa sengaja" masuk ke dalam perhitungan Anda, maka Anda sering berakhir dengan nol sebagai akibat atau kesalahan divide-by-zero. Apakah nilai "infinity" yang didefinisikan oleh IEEE floating point juga jahat?Dalam bahasa dinamis, Anda tidak boleh bertanya apakah mengembalikan tipe berbeda tetapi objek dengan API berbeda . Karena sebagian besar bahasa dinamis tidak terlalu peduli tentang jenis, tetapi gunakan berbagai versi pengetikan bebek .
Ketika kembali berbagai jenis masuk akal
Misalnya metode ini masuk akal:
Karena kedua file, dan daftar string adalah (dengan Python) iterables yang mengembalikan string. Tipe yang sangat berbeda, API yang sama (kecuali seseorang mencoba memanggil metode file pada daftar, tetapi ini adalah cerita yang berbeda).
Anda dapat kembali secara kondisional
list
atautuple
(tuple
adalah daftar yang tidak dapat diubah dengan python).Bahkan secara formal melakukan:
atau:
mengembalikan tipe yang berbeda, karena kedua python
None
dan Javascriptnull
adalah tipe di kanan mereka sendiri.Semua kasus penggunaan ini akan memiliki rekan mereka dalam bahasa statis, fungsinya hanya akan mengembalikan antarmuka yang tepat.
Ketika mengembalikan objek dengan API yang berbeda secara kondisional adalah ide yang bagus
Mengenai apakah mengembalikan API yang berbeda adalah ide yang baik, IMO dalam banyak kasus itu tidak masuk akal. Hanya contoh masuk akal yang muncul di pikiran adalah sesuatu yang dekat dengan apa yang dikatakan @MainMa : ketika API Anda dapat memberikan jumlah detail yang bervariasi, mungkin masuk akal untuk mengembalikan lebih detail ketika tersedia.
sumber
do_file
mengembalikan sebuah string yang dapat diubah dengan cara apa pun.iter()
daftar dan file untuk memastikan hasilnya hanya dapat digunakan sebagai iterator dalam kedua kasus.None or something
adalah pembunuh untuk optimasi kinerja yang dilakukan oleh alat-alat seperti PyPy, Numba, Pyston dan lainnya. Ini adalah salah satu dari Python-isme yang membuat python tidak terlalu cepat.Pertanyaan Anda membuat saya ingin sedikit menangis. Bukan untuk penggunaan contoh yang Anda berikan, tetapi karena seseorang tanpa disadari akan mengambil pendekatan ini terlalu jauh. Ini adalah langkah singkat dari kode yang sangat sulit dipelihara.
Kondisi kesalahan menggunakan jenis kasus masuk akal, dan pola nol (semuanya harus menjadi pola) dalam bahasa yang diketik secara statis melakukan hal yang sama. Panggilan fungsi Anda mengembalikan
object
atau mengembalikannyanull
.Tapi itu langkah singkat untuk mengatakan "Aku akan menggunakan ini untuk membuat pola pabrik " dan kembali baik
foo
ataubar
ataubaz
tergantung suasana hati fungsi ini. Debugging ini akan menjadi mimpi buruk ketika penelepon mengharapkanfoo
tetapi diberikanbar
.Jadi saya tidak berpikir Anda sedang berpikiran tertutup. Anda berhati-hati dalam menggunakan fitur bahasa.
Pengungkapan: latar belakang saya dalam bahasa yang diketik secara statis, dan saya umumnya bekerja pada tim yang lebih besar dan beragam di mana kebutuhan akan kode yang dapat dipelihara cukup tinggi. Jadi perspektif saya mungkin miring juga.
sumber
Menggunakan Generics in Java memungkinkan Anda mengembalikan tipe yang berbeda, namun tetap mempertahankan keamanan tipe statis. Anda cukup menentukan tipe yang ingin Anda kembalikan dalam parameter tipe generik dari panggilan fungsi.
Apakah Anda bisa menggunakan pendekatan serupa di Javascript, tentu saja, merupakan pertanyaan terbuka. Karena Javascript adalah bahasa yang diketik secara dinamis, mengembalikan sebuah
object
sepertinya pilihan yang jelas.Jika Anda ingin tahu di mana skenario pengembalian dinamis dapat berfungsi saat Anda terbiasa bekerja dalam bahasa yang diketik secara statis, pertimbangkan untuk melihat
dynamic
kata kunci dalam C #. Rob Conery berhasil menulis Object-Relational Mapper dalam 400 baris kode menggunakandynamic
kata kunci.Tentu saja, semua
dynamic
sebenarnya adalah membungkusobject
variabel dengan beberapa jenis keamanan runtime.sumber
Itu ide yang buruk, saya pikir, untuk mengembalikan tipe yang berbeda secara kondisional. Salah satu cara ini sering muncul bagi saya adalah jika fungsinya dapat mengembalikan satu atau lebih nilai. Jika hanya satu nilai yang perlu dikembalikan, mungkin masuk akal untuk mengembalikan nilai, daripada mengemasnya dalam Array, untuk menghindari keharusan membongkar dalam fungsi panggilan. Namun, ini (dan sebagian besar contoh lain dari ini) menempatkan kewajiban pada penelepon untuk membedakan antara dan menangani kedua jenis. Fungsi akan lebih mudah untuk dipikirkan jika selalu mengembalikan jenis yang sama.
sumber
"Praktek buruk" ada terlepas dari apakah bahasa Anda diketik secara statis. Bahasa statis lebih banyak menjauhkan Anda dari praktik-praktik ini, dan Anda mungkin menemukan lebih banyak pengguna yang mengeluh tentang "praktik buruk" dalam bahasa statis karena itu adalah bahasa yang lebih formal. Namun, masalah yang mendasarinya ada dalam bahasa yang dinamis, dan Anda dapat menentukan apakah mereka dibenarkan.
Inilah bagian yang tidak menyenangkan dari apa yang Anda usulkan. Jika saya tidak tahu jenis apa yang dikembalikan, maka saya tidak dapat langsung menggunakan nilai kembali. Saya harus "menemukan" sesuatu tentang itu.
Seringkali jenis pengalihan kode ini merupakan praktik yang buruk. Itu membuat kode lebih sulit dibaca. Dalam contoh ini, Anda melihat mengapa melempar dan menangkap kasus pengecualian sangat populer. Dengan kata lain: jika fungsi Anda tidak dapat melakukan apa yang dikatakannya, itu tidak seharusnya kembali dengan sukses . Jika saya memanggil fungsi Anda, saya ingin melakukan ini:
Karena baris pertama kembali dengan sukses, saya berasumsi bahwa
total
sebenarnya berisi jumlah array. Jika tidak berhasil kembali, kami beralih ke halaman lain dari program saya.Mari kita gunakan contoh lain yang bukan hanya tentang menyebarkan kesalahan. Mungkin sum_of_array mencoba menjadi pintar dan mengembalikan string yang dapat dibaca manusia dalam beberapa kasus, seperti "Itu kombinasi loker saya!" jika dan hanya jika arraynya adalah [11,7,19]. Saya mengalami kesulitan memikirkan contoh yang baik. Bagaimanapun, masalah yang sama berlaku. Anda harus memeriksa nilai pengembalian sebelum Anda dapat melakukan apa pun dengan itu:
Anda mungkin berpendapat bahwa akan berguna untuk fungsi mengembalikan integer atau float, misalnya:
Tetapi hasil itu bukan tipe yang berbeda, sejauh yang Anda ketahui. Anda akan memperlakukan mereka berdua sebagai angka, dan hanya itu yang Anda pedulikan. Jadi sum_of_array mengembalikan tipe angka. Inilah yang dimaksud dengan polimorfisme.
Jadi ada beberapa praktik yang mungkin Anda langgar jika fungsi Anda dapat mengembalikan beberapa tipe. Mengetahui mereka akan membantu Anda menentukan apakah fungsi khusus Anda akan mengembalikan beberapa jenis.
sumber
Sebenarnya, tidak jarang sama sekali untuk mengembalikan tipe yang berbeda bahkan dalam bahasa yang diketik secara statis. Itu sebabnya kami memiliki jenis serikat pekerja, misalnya.
Bahkan, metode di Jawa hampir selalu mengembalikan satu dari empat jenis: beberapa jenis objek atau
null
pengecualian atau mereka tidak pernah kembali sama sekali.Dalam banyak bahasa, kondisi kesalahan dimodelkan sebagai subrutin yang mengembalikan tipe hasil atau tipe kesalahan. Misalnya, dalam Scala:
Ini adalah contoh yang bodoh, tentu saja. Jenis kembali berarti "mengembalikan string atau desimal". Secara konvensi, tipe kiri adalah tipe kesalahan (dalam hal ini, string dengan pesan kesalahan) dan jenis yang tepat adalah jenis hasil.
Ini mirip dengan pengecualian, kecuali kenyataan bahwa pengecualian juga merupakan konstruk aliran kontrol. Mereka, pada kenyataannya, setara dalam kekuatan ekspresif
GOTO
.sumber
Unit
(metode tidak diperlukan untuk kembali sama sekali). Benar, Anda tidak benar-benar menggunakanreturn
kata kunci, tetapi hasilnya adalah kembali dari metode. Dengan pengecualian yang dicentang, itu bahkan secara eksplisit disebutkan dalam tipe tanda tangan.GOTO
, sebenarnya).Belum ada jawaban yang menyebutkan prinsip SOLID. Secara khusus, Anda harus mengikuti prinsip substitusi Liskov, bahwa setiap kelas yang menerima tipe selain tipe yang diharapkan masih dapat bekerja dengan apa pun yang mereka dapatkan, tanpa melakukan apa pun untuk menguji jenis apa yang dikembalikan.
Jadi, jika Anda melempar beberapa properti tambahan ke objek, atau membungkus fungsi yang dikembalikan dengan semacam dekorator yang masih memenuhi apa yang ingin dicapai fungsi asli, Anda baik, selama tidak ada kode yang memanggil fungsi Anda pernah bergantung pada ini perilaku, di jalur kode apa pun.
Alih-alih mengembalikan string atau integer, contoh yang lebih baik mungkin mengembalikan sistem sprinkler atau kucing. Ini bagus jika semua kode panggilan yang akan dilakukan adalah memanggil functionInQuestion.hiss (). Secara efektif Anda memiliki antarmuka tersirat yang diharapkan oleh kode panggilan, dan bahasa yang diketik secara dinamis tidak akan memaksa Anda untuk membuat antarmuka menjadi eksplisit.
Sedihnya, rekan kerja Anda mungkin akan melakukannya, jadi Anda mungkin harus melakukan pekerjaan yang sama dalam dokumentasi Anda, kecuali tidak ada cara yang dapat diterima secara universal, singkat, dan dapat dianalisis dengan mesin untuk melakukannya - seperti saat Anda mendefinisikan antarmuka dalam bahasa yang memilikinya.
sumber
Satu tempat di mana saya melihat diri saya mengirim berbagai jenis adalah untuk input yang tidak valid atau "pengecualian orang miskin" di mana kondisi "luar biasa" tidak terlalu luar biasa. Sebagai contoh, dari repositori fungsi-fungsi utilitas PHP contoh singkat ini:
Fungsi secara nominal mengembalikan BOOLEAN, namun mengembalikan NULL pada input yang tidak valid. Perhatikan bahwa sejak PHP 5.3, semua fungsi PHP internal juga berperilaku seperti ini . Selain itu, beberapa fungsi PHP internal mengembalikan FALSE atau INT pada input nominal, lihat:
sumber
null
, karena (a) gagal keras , jadi lebih mungkin diperbaiki pada fase pengembangan / pengujian, (b) mudah untuk ditangani, karena saya bisa menangkap semua pengecualian langka / tidak terduga satu kali untuk seluruh aplikasi saya (di front controller saya) dan hanya login (saya biasanya mengirim email ke tim dev), sehingga bisa diperbaiki nanti. Dan saya benar-benar benci bahwa pustaka PHP standar kebanyakan menggunakan pendekatan "kembali null" - itu benar-benar hanya membuat kode PHP lebih rentan kesalahan, kecuali Anda memeriksa semuanya denganisset()
, yang terlalu banyak beban.null
karena kesalahan, tolong harap jelaskan dalam docblock (mis.@return boolean|null
), Jadi jika saya menemukan kode Anda suatu hari nanti, saya tidak perlu memeriksa fungsi / metode body.Saya rasa ini bukan ide yang buruk! Berbeda dengan pendapat yang paling umum ini, dan seperti yang telah ditunjukkan oleh Robert Harvey, bahasa yang diketik secara statis seperti Java memperkenalkan Generics persis untuk situasi seperti yang Anda tanyakan. Sebenarnya Java mencoba untuk menjaga (sedapat mungkin) keamanan ketik pada waktu kompilasi, dan terkadang Generics menghindari duplikasi kode, mengapa? Karena Anda dapat menulis metode yang sama atau kelas yang sama yang menangani / mengembalikan tipe yang berbeda . Saya akan membuat contoh yang sangat singkat untuk menunjukkan ide ini:
Jawa 1.4
Java 1.5+
Dalam bahasa yang diketik dinamis, karena Anda tidak memiliki ketelitian pada waktu kompilasi, Anda benar-benar bebas untuk menulis kode yang sama yang bekerja untuk banyak jenis. Karena bahkan dalam bahasa yang diketik secara statis, Generik diperkenalkan untuk menyelesaikan masalah ini, jelas merupakan petunjuk bahwa menulis fungsi yang mengembalikan berbagai jenis dalam bahasa dinamis bukanlah ide yang buruk.
sumber
Mengembangkan perangkat lunak pada dasarnya adalah seni dan keterampilan mengelola kompleksitas. Anda mencoba mempersempit sistem pada titik yang Anda mampu, dan membatasi opsi di titik lain. Antarmuka fungsi adalah kontrak yang membantu mengelola kompleksitas kode dengan membatasi pengetahuan yang diperlukan untuk bekerja dengan bagian kode apa pun. Dengan mengembalikan jenis yang berbeda, Anda secara signifikan memperluas antarmuka fungsi dengan menambahkan semua antarmuka dari jenis yang berbeda yang Anda kembalikan DAN menambahkan aturan yang tidak jelas tentang antarmuka yang dikembalikan.
sumber
Perl banyak menggunakan ini karena apa fungsi tidak tergantung pada konteksnya . Misalnya, suatu fungsi dapat mengembalikan array jika digunakan dalam konteks daftar, atau panjang array jika digunakan di tempat di mana nilai skalar diharapkan. Dari tutorial itu adalah hit pertama untuk "konteks perl" , jika Anda melakukannya:
Kemudian @now adalah variabel array (itulah arti dari @), dan akan berisi array seperti (40, 51, 20, 9, 0, 109, 5, 8, 0).
Jika sebaliknya, Anda memanggil fungsi dengan cara di mana hasilnya harus berupa skalar, dengan ($ variabel skalar):
maka ia melakukan sesuatu yang sama sekali berbeda: $ sekarang akan menjadi sesuatu seperti "Jumat 9 Januari 20:51:40 2009".
Contoh lain yang dapat saya pikirkan adalah dalam mengimplementasikan REST API, di mana format apa yang dikembalikan tergantung pada apa yang diinginkan klien. Misalnya, HTML, atau JSON, atau XML. Meskipun secara teknis itu semua aliran byte, idenya serupa.
sumber
String
. Sekarang orang-orang meminta dokumentasi tentang tipe pengembalian. Alat java dapat menghasilkan secara otomatis jika saya mengembalikan kelas, tetapi karena saya memilihString
saya tidak dapat menghasilkan dokumentasi secara otomatis. Di belakang / moral cerita, saya berharap saya mengembalikan satu jenis per metode.Di tanah yang dinamis itu semua tentang mengetik bebek. Hal yang paling bertanggung jawab untuk diekspos / menghadap publik adalah untuk membungkus tipe yang berpotensi berbeda dalam pembungkus yang memberi mereka antarmuka yang sama.
Kadang-kadang mungkin masuk akal untuk menghapus berbagai jenis sama sekali tanpa pembungkus generik tetapi jujur dalam 7 tahun tahun menulis JS, itu bukan sesuatu yang saya temukan itu masuk akal atau nyaman untuk dilakukan sangat sering. Sebagian besar itu adalah sesuatu yang akan saya lakukan dalam konteks lingkungan tertutup seperti interior objek di mana segala sesuatunya disatukan. Tapi itu bukan sesuatu yang sudah saya lakukan cukup sering sehingga setiap contoh terlintas dalam pikiran.
Sebagian besar, saya akan menyarankan Anda berhenti memikirkan jenis sama sekali. Anda berurusan dengan tipe saat Anda membutuhkannya dalam bahasa yang dinamis. Tidak lagi. Itu intinya. Jangan periksa jenis setiap argumen tunggal. Itu adalah sesuatu yang saya hanya akan tergoda untuk melakukannya di lingkungan di mana metode yang sama dapat memberikan hasil yang tidak konsisten dengan cara yang tidak jelas (jadi pasti tidak melakukan hal seperti itu). Tapi itu bukan tipe yang penting, tapi bagaimana pun yang kau berikan padaku, bekerja.
sumber