Apakah ada keuntungan nyata untuk bahasa dinamis? [Tutup]

29

Pertama saya ingin mengatakan Java adalah satu-satunya bahasa yang pernah saya gunakan, jadi mohon maafkan ketidaktahuan saya tentang hal ini.

Bahasa yang diketik secara dinamis memungkinkan Anda untuk memberikan nilai apa pun dalam variabel apa pun. Jadi misalnya Anda bisa menulis fungsi berikut (psuedocode):

void makeItBark(dog){
    dog.bark();
}

Dan Anda dapat memberikan nilai apa pun di dalamnya. Selama nilainya memiliki bark()metode, kode akan berjalan. Jika tidak, pengecualian runtime atau sesuatu yang serupa dilemparkan. (Tolong perbaiki saya jika saya salah tentang ini).

Tampaknya, ini memberi Anda fleksibilitas.

Namun, saya melakukan beberapa bacaan tentang bahasa dinamis, dan apa yang orang katakan adalah bahwa ketika merancang atau menulis kode dalam bahasa dinamis, Anda berpikir tentang jenis dan memperhitungkannya, seperti halnya Anda menggunakan bahasa yang diketik secara statis.

Jadi misalnya ketika menulis makeItBark()fungsi, Anda bermaksud untuk hanya menerima 'hal-hal yang dapat menggonggong', dan Anda masih perlu memastikan Anda hanya memasukkan hal-hal semacam itu ke dalamnya. Satu-satunya perbedaan adalah bahwa sekarang kompiler tidak akan memberi tahu Anda ketika Anda melakukan kesalahan.

Tentu, ada satu keuntungan dari pendekatan ini yaitu dalam bahasa statis, untuk mencapai 'fungsi ini menerima apa pun yang bisa menggonggong', Anda harus mengimplementasikan Barkerantarmuka eksplisit . Namun, ini sepertinya keuntungan kecil.

Apakah saya melewatkan sesuatu? Apa yang sebenarnya saya peroleh dengan menggunakan bahasa yang diketik secara dinamis?

Aviv Cohn
sumber
6
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof")). Argumen itu bahkan bukan kelas , itu adalah anonim bernama tuple. Mengetik bebek ("jika dikuak seperti ...") memungkinkan Anda melakukan antarmuka ad hoc dengan dasarnya tidak ada batasan dan tidak ada overhead sintaksis. Anda dapat melakukan ini dalam bahasa seperti Java, tetapi Anda berakhir dengan banyak refleksi yang berantakan. Jika fungsi di Java memerlukan ArrayList dan Anda ingin memberikannya jenis koleksi lain, Anda SOL. Dengan python yang bahkan tidak bisa muncul.
Phoshi
2
Pertanyaan semacam ini telah diajukan sebelumnya: di sini , di sini , dan di sini . Secara khusus contoh pertama tampaknya menjawab pertanyaan Anda. Mungkin Anda dapat mengubah kata-kata Anda untuk membuatnya berbeda?
logc
3
Perhatikan bahwa misalnya dalam C ++, Anda dapat memiliki fungsi templat yang bekerja dengan semua tipe T yang memiliki bark()metode, dengan kompiler mengeluh ketika Anda memasukkan sesuatu yang salah tetapi tanpa harus benar-benar mendeklarasikan antarmuka yang berisi kulit kayu ().
Wilbert
2
@ Phoshi Argumen dalam Python masih harus dari tipe tertentu - misalnya, itu tidak bisa berupa angka. Jika Anda memiliki objek implementasi ad-hoc Anda sendiri, yang mengambil anggotanya melalui beberapa getMemberfungsi kustom , makeItBarkmeledak karena Anda memanggil dog.barkalih-alih dog.getMember("bark"). Apa yang membuat kode berfungsi adalah bahwa setiap orang secara implisit setuju untuk menggunakan tipe objek asli Python.
Doval
2
@ Phoshi Just because I wrote makeItBark with my own types in mind doesn't mean you can't use yours, wheras in a static language it probably /does/ mean that.Seperti yang ditunjukkan dalam jawaban saya, ini tidak terjadi secara umum . Itulah yang terjadi pada Java dan C #, tetapi bahasa-bahasa tersebut memiliki sistem tipe dan modul yang lumpuh sehingga mereka tidak mewakili apa yang dapat dilakukan pengetikan statis. Saya dapat menulis generik sempurna makeItBarkdalam beberapa bahasa yang diketik secara statis, bahkan yang non-fungsional seperti C ++ atau D.
Doval

Jawaban:

35

Bahasa yang diketik secara dinamis tidak diketik

Membandingkan sistem tipe , tidak ada keuntungan dalam pengetikan dinamis. Pengetikan dinamis adalah kasus khusus pengetikan statis - pengetikan statis di mana setiap variabel memiliki tipe yang sama. Anda bisa mencapai hal yang sama di Java (minus conciseness) dengan membuat setiap variabel bertipe Object, dan memiliki nilai "objek" bertipe Map<String, Object>:

void makeItBark(Object dog) {
    Map<String, Object> dogMap = (Map<String, Object>) dog;
    Runnable bark = (Runnable) dogMap.get("bark");
    bark.run();
}

Jadi, bahkan tanpa refleksi, Anda dapat mencapai efek yang sama di hampir semua bahasa yang diketik secara statis, selain kemudahan sintaksis. Anda tidak mendapatkan kekuatan ekspresif tambahan apa pun; sebaliknya, Anda memiliki kekuatan yang kurang ekspresif karena dalam bahasa yang diketik secara dinamis, Anda ditolak kemampuan untuk membatasi variabel ke jenis tertentu.

Membuat kulit bebek dalam bahasa yang diketik secara statis

Selain itu, bahasa yang diketik secara statis yang baik akan memungkinkan Anda untuk menulis kode yang berfungsi dengan semua jenis yang memiliki barkoperasi. Di Haskell, ini adalah kelas tipe:

class Barkable a where
    bark :: a -> unit

Ini menyatakan kendala bahwa untuk beberapa jenis adianggap Barkable, harus ada barkfungsi yang mengambil nilai jenis itu dan tidak mengembalikan apa pun.

Anda kemudian dapat menulis fungsi umum dalam Barkablebatasan:

makeItBark :: Barkable a => a -> unit
makeItBark barker = bark (barker)

Ini mengatakan bahwa makeItBarkakan bekerja untuk semua jenis Barkablepersyaratan yang memuaskan . Ini mungkin tampak mirip dengan interfacedi Java atau C # tetapi memiliki satu keuntungan besar - tipe tidak harus menentukan di depan kelas tipe apa yang mereka puaskan. Saya dapat mengatakan bahwa tipe Duckitu Barkablekapan saja, bahkan jika Ducktipe pihak ketiga saya tidak menulis. Sebenarnya, tidak masalah jika penulis Ducktidak menulis suatu barkfungsi - saya bisa menyediakannya setelah fakta ketika saya memberi tahu bahasa yang Duckmemuaskan Barkable:

instance Barkable Duck where
    bark d = quack (punch (d))

makeItBark (aDuck)

Ini mengatakan bahwa Ducks dapat menggonggong, dan fungsi kulit mereka diimplementasikan dengan meninju bebek sebelum membuatnya dukun. Dengan cara itu, kita bisa memanggil makeItBarkbebek.

Standard MLdan OCamlbahkan lebih fleksibel karena Anda dapat memenuhi kelas tipe yang sama dalam lebih dari satu cara. Dalam bahasa-bahasa ini saya dapat mengatakan bahwa bilangan bulat dapat dipesan menggunakan pemesanan konvensional dan kemudian berbalik dan mengatakan mereka juga dapat dipesan oleh keterbelahan (misalnya 10 > 5karena 10 dapat dibagi 5). Di Haskell, Anda hanya dapat membuat instance kelas tipe satu kali. (Ini memungkinkan Haskell untuk secara otomatis tahu bahwa itu ok untuk memanggil barkpada bebek, dalam SML atau OCaml Anda harus eksplisit tentang yang bark berfungsi Anda inginkan, karena mungkin ada lebih dari satu.)

Keringkasan yg padat isinya

Tentu saja, ada perbedaan sintaksis. Kode Python yang Anda sajikan jauh lebih ringkas daripada yang setara dengan Java yang saya tulis. Dalam praktiknya, keringkasan itu adalah bagian besar dari daya pikat bahasa yang diketik secara dinamis. Tetapi inferensi tipe memungkinkan Anda untuk menulis kode yang sama ringkasnya dalam bahasa yang diketik secara statis, dengan membebaskan Anda dari keharusan untuk secara eksplisit menulis jenis setiap variabel. Bahasa yang diketik secara statis juga dapat memberikan dukungan asli untuk pengetikan dinamis, menghilangkan verbositas dari semua casting dan manipulasi peta (misalnya C # 's dynamic).

Program yang benar tetapi salah ketik

Agar adil, pengetikan statis harus mengesampingkan beberapa program yang secara teknis benar meskipun pemeriksa tipe tidak dapat memverifikasinya. Sebagai contoh:

if this_variable_is_always_true:
    return "some string"
else:
    return 6

Sebagian besar bahasa yang diketik secara statis akan menolak ifpernyataan ini , meskipun cabang lain tidak akan pernah muncul. Dalam praktiknya sepertinya tidak ada yang menggunakan jenis kode ini - apa pun yang terlalu pintar untuk pemeriksa tipe mungkin akan membuat pengelola kode masa depan Anda mengutuk Anda dan kerabat Anda berikutnya. Contohnya, seseorang berhasil menerjemahkan 4 proyek Python open source ke Haskell yang berarti mereka tidak melakukan apa pun yang tidak bisa dikompilasi oleh bahasa yang diketik secara statis. Terlebih lagi, kompiler menemukan beberapa bug terkait tipe yang tidak ditangkap oleh unit test.

Argumen terkuat yang pernah saya lihat untuk pengetikan dinamis adalah makro Lisp, karena mereka memungkinkan Anda untuk memperpanjang sintaks bahasa secara sewenang-wenang. Namun, Typed Racket adalah dialek Lisp yang diketik secara statis yang memiliki makro, jadi sepertinya pengetikan statis dan makro tidak saling eksklusif, meskipun mungkin lebih sulit untuk diterapkan secara bersamaan.

Apel dan Jeruk

Akhirnya, jangan lupa bahwa ada perbedaan yang lebih besar dalam bahasa daripada hanya sistem tipenya. Sebelum ke Java 8, melakukan segala jenis pemrograman fungsional di Jawa praktis tidak mungkin; lambda sederhana akan membutuhkan 4 baris kode kelas anonim boilerplate. Java juga tidak memiliki dukungan untuk kumpulan literal (mis [1, 2, 3].). Mungkin juga ada perbedaan dalam kualitas dan ketersediaan tooling (IDE, debugger), perpustakaan, dan dukungan komunitas. Ketika seseorang mengklaim lebih produktif dalam Python atau Ruby daripada Java, perbedaan fitur itu perlu diperhitungkan. Ada perbedaan antara membandingkan bahasa dengan semua baterai yang disertakan , inti bahasa dan sistem tipe .

Doval
sumber
2
Anda lupa mengaitkan sumber Anda dengan paragraf pertama - existentialtype.wordpress.com/2011/03/19/…
2
@ Mat Re: 1, saya belum menganggap itu tidak penting; Saya mengatasinya di bawah Keringkasan. Re: 2, meskipun saya tidak pernah secara eksplisit mengatakannya, dengan "baik" maksud saya "memiliki inferensi tipe menyeluruh" dan "memiliki sistem modul yang memungkinkan Anda untuk mencocokkan kode dengan mengetikkan tanda tangan setelah fakta ", tidak di muka seperti Java / Antarmuka C #. Re 3, beban pembuktian ada pada Anda untuk menjelaskan kepada saya bagaimana diberikan dua bahasa dengan sintaks dan fitur yang setara, satu diketik secara dinamis dan yang lainnya dengan inferensi tipe penuh, Anda tidak akan dapat menulis kode dengan panjang yang sama di kedua .
Doval
3
@MattFenwick Saya sudah membenarkannya - diberikan dua bahasa dengan fitur yang sama, satu diketik secara dinamis dan yang lain diketik secara statis, perbedaan utama di antara mereka adalah adanya anotasi jenis, dan inferensi tipe akan menghilangkannya. Perbedaan lain dalam sintaksis adalah dangkal, dan perbedaan dalam fitur mengubah perbandingan menjadi apel vs jeruk. Ada pada Anda untuk menunjukkan bagaimana logika ini salah.
Doval
1
Anda harus melihat Boo. Ini diketik secara statis dengan inferensi tipe, dan memiliki makro yang memungkinkan sintaks bahasa diperluas.
Mason Wheeler
1
@Doval: Benar. BTW, notasi lambda tidak digunakan secara eksklusif dalam pemrograman fungsional: sejauh yang saya tahu, Smalltalk memiliki blok anonim, dan Smalltalk adalah berorientasi objek yang bisa didapat. Jadi, seringkali solusinya adalah dengan mengirimkan blok kode anonim dengan beberapa parameter, tidak peduli apakah ini adalah fungsi anonim atau objek anonim dengan tepat satu metode anonim. Saya pikir dua konstruksi ini pada dasarnya mengekspresikan ide yang sama dari dua perspektif yang berbeda (fungsional dan berorientasi objek).
Giorgio
11

Ini adalah masalah yang sulit, dan cukup subyektif. (Dan pertanyaan Anda mungkin ditutup sebagai berbasis opini, tetapi itu tidak berarti itu adalah pertanyaan yang buruk - sebaliknya, bahkan memikirkan pertanyaan meta-bahasa semacam itu adalah pertanda baik - itu hanya tidak cocok dengan format Q&A dari forum ini.)

Inilah pandangan saya tentang hal itu: Inti dari bahasa tingkat tinggi adalah untuk membatasi apa yang dapat dilakukan oleh seorang programmer dengan komputer. Ini mengejutkan bagi banyak orang, karena mereka percaya tujuannya adalah untuk memberi pengguna lebih banyak kekuatan dan mencapai lebih banyak . Tetapi karena semua yang Anda tulis dalam Prolog, C ++ atau Daftar akhirnya dieksekusi sebagai kode mesin, sebenarnya tidak mungkin untuk memberi programmer lebih banyak kekuatan daripada bahasa assembly yang sudah disediakan.

Maksud dari bahasa tingkat tinggi adalah untuk membantu programmer memahami kode yang mereka buat sendiri dengan lebih baik, dan untuk membuatnya lebih efisien dalam melakukan hal yang sama. Nama subrutin lebih mudah diingat daripada alamat heksadesimal. Penghitung argumen otomatis lebih mudah digunakan daripada urutan panggilan di sini Anda harus mendapatkan jumlah argumen tepat sendiri, tanpa bantuan. Sistem tipe melangkah lebih jauh dan membatasi jenis argumen yang dapat Anda berikan di tempat tertentu.

Di sinilah persepsi orang berbeda. Beberapa orang (saya termasuk di antara mereka) berpikir bahwa selama rutin pengecekan kata sandi Anda akan mengharapkan tepat dua argumen, dan selalu berupa string yang diikuti oleh id numerik, akan sangat berguna untuk mendeklarasikan ini dalam kode dan secara otomatis diingatkan jika Anda kemudian lupa untuk mengikuti aturan itu. Mengalihdayakan pembukuan skala kecil ke kompiler membantu membebaskan pikiran Anda untuk masalah tingkat yang lebih tinggi dan membuat Anda lebih baik dalam merancang dan merancang sistem Anda. Oleh karena itu, sistem tipe adalah kemenangan besar: mereka membiarkan komputer melakukan apa yang baik, dan manusia melakukan apa yang mereka kuasai.

Yang lain melihat dengan sangat berbeda. Mereka tidak suka diberitahu oleh kompiler apa yang harus dilakukan. Mereka tidak menyukai upaya di muka ekstra untuk memutuskan deklarasi tipe dan mengetiknya. Mereka lebih suka gaya pemrograman eksplorasi di mana Anda menulis kode bisnis yang sebenarnya tanpa memiliki rencana yang akan memberi tahu Anda secara tepat jenis dan argumen yang digunakan di mana. Dan untuk gaya pemrograman yang mereka gunakan, itu mungkin benar.

Saya terlalu menyederhanakan di sini, tentu saja. Pengecekan tipe tidak terikat dengan deklarasi tipe eksplisit; ada juga tipe inferensi. Pemrograman dengan rutinitas yang benar-benar mengambil argumen dari berbagai jenis memang memungkinkan hal-hal yang sangat berbeda dan sangat kuat yang seharusnya tidak mungkin, hanya saja banyak orang tidak cukup perhatian dan konsisten untuk menggunakan kelonggaran seperti itu dengan sukses.

Pada akhirnya, fakta bahwa bahasa-bahasa yang berbeda tersebut sangat populer dan tidak menunjukkan tanda-tanda kematian menunjukkan kepada Anda bahwa orang-orang melakukan pemrograman yang sangat berbeda. Saya pikir fitur bahasa pemrograman sebagian besar tentang faktor manusia - yang mendukung proses pengambilan keputusan manusia lebih baik - dan selama orang bekerja dengan sangat berbeda, pasar akan memberikan solusi yang sangat berbeda secara bersamaan.

Kilian Foth
sumber
3
Terima kasih atas jawabannya. Anda mengatakan bahwa beberapa orang tidak suka diberitahu oleh kompiler apa yang harus dilakukan. [..] Mereka lebih suka gaya pemrograman eksploratori di mana Anda menulis kode bisnis yang sebenarnya tanpa memiliki rencana yang akan memberi tahu Anda secara tepat jenis dan argumen yang digunakan di mana. ' Ini adalah hal yang saya tidak mengerti: pemrograman tidak seperti improvisasi musik. Dalam musik jika Anda menekan not yang salah, itu mungkin terdengar keren. Dalam pemrograman, jika Anda melewatkan sesuatu ke dalam fungsi yang seharusnya tidak ada di sana, kemungkinan besar Anda akan mendapatkan bug jahat. (melanjutkan komentar berikutnya).
Aviv Cohn
3
Saya setuju, tetapi banyak orang tidak setuju. Dan orang-orang cukup posesif tentang prasangka mental mereka, terutama karena mereka sering tidak menyadarinya. Itu sebabnya perdebatan tentang gaya pemrograman biasanya berubah menjadi argumen atau perkelahian, dan jarang berguna untuk memulai dengan orang asing acak di internet.
Kilian Foth
1
Inilah sebabnya - menilai dari apa yang saya baca - orang-orang yang menggunakan bahasa dinamis mempertimbangkan jenis seperti halnya orang yang menggunakan bahasa statis. Karena ketika Anda menulis suatu fungsi, seharusnya mengambil argumen dari jenis tertentu. Tidak masalah jika kompiler memberlakukan ini atau tidak. Jadi, mengetik ke pengetikan statis membantu Anda dengan ini, dan pengetikan dinamis tidak. Dalam kedua kasus, suatu fungsi harus mengambil input jenis tertentu. Jadi saya tidak melihat apa keuntungan dari pengetikan dinamis. Bahkan jika Anda lebih suka 'gaya pemrograman eksploratori', Anda masih tidak bisa meneruskan apa pun yang Anda inginkan ke suatu fungsi.
Aviv Cohn
1
Orang sering berbicara tentang jenis proyek yang sangat berbeda (terutama mengenai ukuran). Logika bisnis untuk situs web akan sangat sederhana dibandingkan dengan mengatakan sistem ERP lengkap. Ada sedikit risiko bahwa Anda mendapatkan kesalahan dan keuntungan karena dapat menggunakan kembali beberapa kode lebih relevan. Katakanlah saya memiliki beberapa kode yang menghasilkan Pdf (atau HTML) dari struktur data. Sekarang saya memiliki sumber data yang berbeda (dulu JSON dari beberapa REST API, sekarang pengimpor Excel). Dalam bahasa seperti Ruby, sangat mudah untuk 'mensimulasikan' struktur pertama, 'membuatnya menjadi kulit' dan menggunakan kembali kode Pdf.
thorsten müller
@Prog: Keuntungan nyata dari bahasa dinamis adalah ketika menjelaskan hal-hal yang sangat sulit dengan sistem tipe statis. Fungsi dalam python, misalnya, dapat berupa referensi fungsi, lambda, objek fungsi, atau tuhan yang tahu apa dan semuanya akan bekerja sama. Anda dapat membangun objek yang membungkus objek lain dan secara otomatis mengirim metode dengan overhead sintaksis nol, dan setiap fungsi yang pada dasarnya memiliki tipe parametrized. Bahasa dinamis luar biasa untuk menyelesaikan berbagai hal dengan cepat.
Phoshi
5

Kode yang ditulis menggunakan bahasa dinamis tidak digabungkan ke sistem tipe statis. Oleh karena itu, kurangnya kopling ini merupakan keuntungan dibandingkan dengan sistem tipe statis yang buruk / tidak memadai (meskipun mungkin merupakan pencucian atau kerugian dibandingkan dengan sistem tipe statis yang bagus).

Selanjutnya, untuk bahasa yang dinamis, sistem tipe statis tidak harus dirancang, diimplementasikan, diuji, dan dipelihara. Ini bisa membuat implementasi lebih sederhana dibandingkan dengan bahasa dengan sistem tipe statis.


sumber
2
Bukankah orang-orang pada akhirnya cenderung menerapkan kembali sistem tipe statis dasar dengan unit test mereka (ketika menargetkan cakupan tes yang baik)?
Den
Juga apa yang Anda maksud dengan "kopling" di sini? Bagaimana ini akan bermanifestasi dalam arsitektur layanan mikro misalnya?
Den
@Den 1) pertanyaan yang bagus, namun, saya merasa itu di luar lingkup OP dan jawaban saya. 2) Maksud saya menggabungkan dalam hal ini ; secara singkat, sistem tipe yang berbeda memberikan batasan yang berbeda (tidak kompatibel) pada kode yang ditulis dalam bahasa itu. Maaf, saya tidak dapat menjawab pertanyaan terakhir - Saya tidak mengerti apa yang istimewa tentang layanan mikro dalam hal ini.
2
@Den: Poin yang sangat bagus: Saya sering mengamati bahwa unit test yang saya tulis dengan Python mencakup kesalahan yang akan ditangkap oleh kompiler dalam bahasa yang diketik secara statis.
Giorgio
@MattFenwick: Anda menulis bahwa ini merupakan keuntungan bahwa "... untuk bahasa yang dinamis, sistem tipe statis tidak harus dirancang, diimplementasikan, diuji, dan dipelihara." dan Den mengamati bahwa Anda sering kali harus merancang dan menguji jenis Anda langsung di kode Anda. Jadi usahanya tidak dihapus tetapi dipindahkan dari desain bahasa ke kode aplikasi.
Giorgio