Mengapa bahasa yang diketik secara dinamis jangan biarkan pengembang menentukan jenisnya?

14

Bahasa yang diketik secara dinamis yang saya tahu tidak pernah membiarkan pengembang menentukan jenis variabel, atau setidaknya memiliki dukungan yang sangat terbatas untuk itu.

JavaScript, misalnya, tidak menyediakan mekanisme apa pun untuk menegakkan jenis variabel saat nyaman untuk melakukannya. PHP membiarkan Anda menentukan beberapa jenis metode argumen, tetapi tidak ada cara untuk menggunakan jenis asli ( int, string, dll) untuk argumen, dan tidak ada cara untuk menegakkan jenis untuk apa pun selain argumen.

Pada saat yang sama, akan lebih mudah untuk memiliki pilihan untuk menentukan dalam beberapa kasus jenis variabel dalam bahasa yang diketik secara dinamis, daripada melakukan pemeriksaan jenis secara manual.

Mengapa ada batasan seperti itu? Apakah karena alasan teknis / kinerja (saya kira itu dalam kasus JavaScript), atau hanya karena alasan politik (yang, saya percaya, kasus PHP)? Apakah ini kasus untuk bahasa yang diketik secara dinamis lainnya yang saya tidak kenal?


Sunting: mengikuti jawaban dan komentar, berikut ini adalah contoh untuk klarifikasi: katakanlah kita memiliki metode berikut dalam PHP biasa:

public function CreateProduct($name, $description, $price, $quantity)
{
    // Check the arguments.
    if (!is_string($name)) throw new Exception('The name argument is expected to be a string.');
    if (!is_string($description)) throw new Exception('The description argument is expected to be a string.');
    if (!is_float($price) || is_double($price)) throw new Exception('The price argument is expected to be a float or a double.');
    if (!is_int($quantity)) throw new Exception('The quantity argument is expected to be an integer.');

    if (!$name) throw new Exception('The name argument cannot be an empty string.');
    if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
    if ($price < 0) throw new Exception('The price argument cannot be less than zero.');

    // We can finally begin to write the actual code.
    // TODO: Implement the method here.
}

Dengan beberapa upaya, ini dapat ditulis ulang sebagai (juga lihat Pemrograman dengan kontrak dalam PHP ):

public function CreateProduct($name, $description, $price, $quantity)
{
    Component::CheckArguments(__FILE__, __LINE__, array(
        'name' => array('value' => $name, 'type' => VTYPE_STRING),
        'description' => array('value' => $description, 'type' => VTYPE_STRING),
        'price' => array('value' => $price, 'type' => VTYPE_FLOAT_OR_DOUBLE),
        'quantity' => array('value' => $quantity, 'type' => VTYPE_INT)
    ));

    if (!$name) throw new Exception('The name argument cannot be an empty string.');
    if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
    if ($price < 0) throw new Exception('The price argument cannot be less than zero.');

    // We can finally begin to write the actual code.
    // TODO: Implement the method here.
}

Tetapi metode yang sama akan ditulis sebagai berikut jika PHP secara opsional akan menerima tipe asli untuk argumen:

public function CreateProduct(string $name, string $description, double $price, int $quantity)
{
    // Check the arguments.
    if (!$name) throw new Exception('The name argument cannot be an empty string.');
    if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
    if ($price < 0) throw new Exception('The price argument cannot be less than zero.');

    // We can finally begin to write the actual code.
    // TODO: Implement the method here.
}

Yang mana yang lebih pendek untuk ditulis? Yang mana yang lebih mudah dibaca?

Arseni Mourzenko
sumber
1
Anda dapat menentukan jenis dalam beberapa bahasa yang diketik secara dinamis - misalnya, dalam Common Lisp.
SK-logic
Cukup banyak bahasa yang diketik secara dinamis menggunakan gips untuk memaksa jenis ...
Trezoid
Beberapa melakukannya. Objective-C, misalnya, diketik secara dinamis, tetapi Anda dapat mendeklarasikan tipe untuk variabel dan kompiler akan mengeluarkan peringatan jika Anda tidak mendapatkan tipe yang Anda harapkan.
mipadi
1
Clojure adalah contoh bahasa yang biasanya diketik secara dinamis tetapi Anda dapat secara opsional memberikan tipe variabel melalui "tip petunjuk" (ini biasanya hanya dilakukan jika diperlukan untuk mendapatkan manfaat kinerja dari informasi tipe kompilasi waktu)
mikera
1
Groovy adalah contoh lain dari bahasa yang diketik secara dinamis yang memungkinkan suatu jenis ditentukan.
Eric Wilson

Jawaban:

17

Inti dari pengetikan statis adalah kemampuan untuk membuktikan secara statis bahwa program Anda benar dalam hal jenis (catatan: tidak sepenuhnya benar dalam semua hal). Jika Anda memiliki sistem tipe statis di seluruh, Anda dapat mendeteksi kesalahan tipe sebagian besar waktu.

Jika Anda hanya memiliki informasi jenis parsial, Anda hanya dapat memeriksa bagian kecil dari grafik panggilan tempat info jenis menjadi lengkap. Tetapi Anda telah menghabiskan waktu dan upaya untuk menentukan informasi jenis untuk bagian yang tidak lengkap, yang tidak dapat membantu Anda tetapi bisa memberikan rasa aman yang salah.

Untuk mengekspresikan informasi jenis, Anda memerlukan bagian bahasa yang tidak bisa terlalu sederhana. Anda akan segera mengetahui bahwa info seperti inttidak cukup; Anda akan menginginkan sesuatu seperti List<Pair<Int, String>>, kemudian tipe parametrik, dll. Ini dapat cukup membingungkan bahkan dalam kasus Jawa yang agak sederhana.

Kemudian, Anda harus menangani informasi ini selama fase terjemahan dan fase eksekusi, karena itu konyol untuk hanya memeriksa kesalahan statis; pengguna akan berharap bahwa batasan tipe selalu berlaku jika ditentukan sama sekali. Bahasa dinamis tidak terlalu cepat, dan pemeriksaan seperti itu akan memperlambat kinerja lebih rendah. Bahasa statis dapat menghabiskan upaya serius memeriksa jenis karena hanya melakukannya sekali; bahasa yang dinamis tidak bisa.

Sekarang bayangkan menambahkan dan memelihara semua ini hanya agar orang kadang-kadang secara opsional menggunakan fitur-fitur ini, hanya mendeteksi sebagian kecil dari kesalahan jenis. Saya tidak berpikir itu sepadan dengan usaha.

Inti dari bahasa dinamis adalah untuk memiliki kerangka kerja yang sangat kecil dan sangat mudah ditempa, di mana Anda dapat dengan mudah melakukan hal-hal yang jauh lebih terlibat ketika dilakukan dalam bahasa statis: berbagai bentuk tambalan monyet yang digunakan untuk metaprogramming, mengejek dan pengujian, penggantian kode secara dinamis, dll. Smalltalk dan Lisp, keduanya sangat dinamis, membawanya sedemikian ekstrim untuk mengirimkan gambar lingkungan daripada membangun dari sumber. Tetapi ketika Anda ingin memastikan bahwa jalur data tertentu adalah tipe-aman, tambahkan pernyataan dan tulis lebih banyak tes unit.

9000
sumber
1
+1, meskipun pengujian hanya dapat menunjukkan bahwa kesalahan tidak terjadi dalam situasi tertentu. Mereka adalah pengganti yang buruk untuk bukti bahwa kesalahan (tipe) tidak mungkin.
Ingo
1
@Ingo: pasti. Tetapi bahasa dinamis bagus untuk bermain-main dan membuat prototipe cepat, di mana Anda mengekspresikan ide-ide yang relatif sederhana dengan sangat cepat. Jika Anda menginginkan kode produksi anti peluru, Anda dapat beralih ke bahasa statis setelahnya, ketika Anda telah mengekstrak beberapa komponen inti yang stabil.
9000
1
@ 9000, saya tidak ragu mereka hebat. Dicari hanya untuk menunjukkan bahwa menulis 3 atau 4 tes lumpuh tidak, dan tidak dapat memastikan tipe keselamatan .
Ingo
2
@ 9000, Benar, dan berita buruknya adalah bahwa pada saat itu, praktis tidak mungkin. Bahkan kode Haskell atau Agda bergantung pada asumsi, seperti, misalnya, bahwa pustaka yang digunakan dalam runtime sudah benar. Yang sedang berkata, dalam proyek dengan sekitar 1000 LOC yang tersebar di beberapa lusin file kode sumber, sangat keren ketika Anda dapat mengubah sesuatu dan Anda tahu bahwa kompiler akan menunjuk pada setiap baris di mana perubahan memiliki dampak.
Ingo
4
Tes yang ditulis dengan buruk bukan pengganti untuk pemeriksaan tipe statis: mereka lebih rendah. Tes yang ditulis dengan baik juga bukan pengganti untuk pemeriksaan tipe statis: mereka lebih unggul.
Rein Henrichs
8

Dalam sebagian besar bahasa dinamis, Anda setidaknya dapat menguji secara dinamis jenis objek atau nilai.

Dan ada inferencer tipe statis, dam dan / atau penegak untuk beberapa bahasa dinamis: mis

Dan Perl 6 akan mendukung sistem tipe opsional dengan pengetikan statis.


Tapi saya kira intinya adalah bahwa banyak orang menggunakan bahasa secara dinamis karena mereka diketik secara dinamis, dan bagi mereka pengetikan statis opsional sangat "ho hum". Dan banyak orang lain menggunakannya karena mereka "mudah bagi non-programmer untuk menggunakan", sebagian besar sebagai konsekuensi dari sifat dinamis dinamis yang pemaaf. Bagi mereka, pengetikan opsional adalah sesuatu yang mereka tidak akan mengerti, atau tidak akan terganggu untuk digunakan.

Jika Anda sinis, Anda dapat mengatakan bahwa pengetikan statis opsional menawarkan yang terburuk dari kedua dunia. Untuk zealot tipe statis, itu tidak mencegah semua kegagalan tipe dinamis. Untuk penggemar tipe dinamis, ini masih jaket lurus ... meskipun dengan tali tidak dikencangkan.

Stephen C
sumber
2
Perlu dicatat bahwa memeriksa jenis sendiri disukai oleh sebagian besar komunitas di sebagian besar keadaan. Gunakan polimorfisme (khususnya, "mengetik bebek") ketika berhadapan dengan hierarki objek, memaksa ke tipe yang diharapkan jika mungkin / masuk akal. Beberapa kasus tetap tidak masuk akal untuk memperbolehkan jenis apa pun, tetapi dalam banyak bahasa Anda tetap mendapatkan pengecualian dalam sebagian besar kasus ini, jadi pemeriksaan jenis jarang berguna.
4
"orang biasanya menggunakan bahasa yang dinamis karena mereka diketik secara dinamis" : JavaScript digunakan karena itu adalah satu-satunya bahasa yang didukung oleh sebagian besar browser. PHP digunakan karena populer.
Arseni Mourzenko
2

Javascript memang berencana untuk memasukkan beberapa pengetikan statis opsional, dan tampaknya seolah-olah banyak bahasa dinamis dewasa mengarah ke sana-

Alasannya adalah bahwa ketika Anda pertama kali kode, Anda ingin cepat dan diketik secara dinamis. Setelah kode Anda solid, berfungsi dan memiliki banyak kegunaan (r), Anda ingin mengunci desain untuk mengurangi kesalahan. (Ini bermanfaat bagi pengguna dan pengembang, karena yang pertama akan mendapatkan kesalahan memeriksa panggilan mereka dan yang terakhir tidak akan merusak hal-hal secara tidak sengaja.

Masuk akal bagi saya, karena saya biasanya menemukan ada terlalu banyak pengecekan tipe pada awal proyek, terlalu sedikit pada akhir masa hidup, tidak peduli bahasa apa yang saya gunakan;).

Macke
sumber
Saya tidak tahu tentang rencana itu untuk memasukkan pengetikan statis opsional dalam JavaScript; tetapi berharap mereka tidak begitu mengerikan seperti itu di ActiveScript. yang terburuk dari JavaScript dan Java.
Javier
Ini direncanakan untuk JS 4 (atau ECMAscript 4) tetapi versi itu dibatalkan karena kontroversi. Saya yakin sesuatu yang serupa akan muncul di masa depan, dalam beberapa bahasa. (Dengan Python Anda dapat melakukan semacam itu dengan dekorator, btw.)
Macke
1
Dekorator menambahkan pengecekan tipe dinamis , semacam asersi. Anda tidak bisa mendapatkan pengecekan tipe statis yang komprehensif dalam Python, betapapun kerasnya Anda mencoba, karena dinamisme ekstrim dari bahasa tersebut.
9000
@ 9000: Itu benar. Namun, saya tidak berpikir pengecekan tipe dinamis itu buruk (tapi saya lebih suka perbandingan tipe bebek ala JS4), terutama bila dikombinasikan dengan unit test, dan menentukan dekorator bisa lebih berguna mendukung IDE / lint-checker jika mereka di mana terstandarisasi.
Macke
tentu saja tidak buruk! Ini bukan masalah moralitas. Pada titik tertentu, jenis harus "diperiksa" dengan satu atau lain cara. Jika Anda menulis * ((ganda *) 0x98765E) dalam C, CPU akan melakukannya dan memeriksa apakah 0x98765E memang pointer ke dobel.
Ingo
2

Objek Python memang memiliki tipe.

Anda menentukan jenis ketika Anda membuat objek.

Pada saat yang sama, akan lebih mudah untuk memiliki pilihan untuk menentukan dalam beberapa kasus jenis variabel dalam bahasa yang diketik secara dinamis, daripada melakukan pemeriksaan jenis secara manual.

Sebenarnya, pemeriksaan tipe manual dalam Python hampir selalu membuang-buang waktu dan kode.

Ini hanya praktik yang buruk untuk menulis kode periksa jenis dengan Python.

Jika jenis yang tidak pantas digunakan oleh beberapa sosiopat jahat, metode biasa Python akan menimbulkan pengecualian biasa ketika jenis itu gagal sesuai.

Anda tidak menulis kode, program Anda masih gagal dengan a TypeError.

Ada kasus yang sangat jarang terjadi ketika Anda harus menentukan jenis pada saat run-time.

Mengapa ada batasan seperti itu?

Karena ini bukan "batasan", pertanyaannya bukan pertanyaan nyata.

S.Lott
sumber
2
"Objek Python memang memiliki tipe." - Oh benarkah? Sama seperti objek perl, objek PHP, dan setiap item data lainnya di dunia. Perbedaan antara pengetikan statis dan dinamis hanya ketika jenis akan diperiksa, yaitu ketika kesalahan jenis memanifestasikan dirinya. Jika mereka muncul sebagai kesalahan kompiler, itu mengetik statis, jika mereka muncul sebagai kesalahan runtime, itu dinamis.
Ingo 9'11
@Ingo: Terima kasih atas klarifikasi. Masalahnya adalah bahwa objek C ++ dan Java dapat dilemparkan dari satu tipe ke tipe lainnya, membuat tipe objek agak suram, dan karenanya membuat "pemeriksaan tipe" pada kompiler tersebut juga agak keruh. Di mana pengecekan tipe Python - meskipun saat dijalankan - jauh lebih suram. Selain itu, pertanyaan berjalan agak dekat dengan mengatakan bahasa yang diketik secara dinamis tidak memiliki jenis. Berita baiknya adalah itu tidak membuat kesalahan umum.
S.Lott
1
Anda benar, ketik gips (berbeda dengan konversi tipe, yaitu ((ganda) 42)) menumbangkan pengetikan statis. Mereka diperlukan ketika sistem tipe tidak cukup kuat. Sebelum Java 5, Java tidak memiliki tipe parmeter, Anda tidak bisa hidup tanpa gips. Saat ini jauh lebih baik, namun sistem jenisnya masih kurang memiliki jenis yang lebih tinggi, belum lagi polimorfisme peringkat tinggi. Saya pikir sangat mungkin bahwa bahasa yang diketik secara dinamis menikmati pengikut sebanyak itu karena mereka membebaskan satu dari sistem tipe yang terlalu sempit.
Ingo 9'11
2

Sebagian besar waktu, Anda tidak perlu, setidaknya tidak pada tingkat detail yang Anda sarankan. Di PHP, operator yang Anda gunakan memperjelas apa yang Anda harapkan dari argumen; itu sedikit pengawasan desain meskipun PHP akan memberikan nilai-nilai Anda jika memungkinkan, bahkan ketika Anda melewatkan array ke operasi yang mengharapkan string, dan karena para pemain tidak selalu bermakna, Anda kadang-kadang mendapatkan hasil yang aneh ( dan ini adalah persis di mana jenis pemeriksaan yang berguna). Selain itu, tidak masalah jika Anda menambahkan bilangan bulat 1dan 5atau string "1"dan "5"- fakta bahwa Anda menggunakan+operator memberi sinyal ke PHP bahwa Anda ingin memperlakukan argumen sebagai angka, dan PHP akan patuh. Situasi yang menarik adalah ketika Anda menerima hasil kueri dari MySQL: Banyak nilai numerik hanya dikembalikan sebagai string, tetapi Anda tidak akan melihat karena PHP memberikannya untuk Anda setiap kali Anda memperlakukannya sebagai angka.

Python sedikit lebih ketat tentang jenisnya, tetapi tidak seperti PHP, Python memiliki pengecualian sejak awal dan menggunakannya secara konsisten. Paradigma "lebih mudah untuk meminta maaf daripada izin" menyarankan untuk hanya melakukan operasi tanpa memeriksa jenis, dan mengandalkan pengecualian yang diajukan ketika jenis tidak masuk akal. Satu-satunya downside dari hal ini yang dapat saya pikirkan adalah bahwa kadang-kadang, Anda akan menemukan bahwa suatu jenis tidak sesuai dengan yang Anda harapkan, tetapi menemukan alasannya bisa membosankan.

Dan ada alasan lain untuk dipertimbangkan: Bahasa dinamis tidak memiliki tahap kompilasi. Bahkan jika Anda memiliki batasan tipe, mereka hanya bisa menyala saat runtime, hanya karena tidak ada waktu kompilasi . Jika cek Anda menyebabkan kesalahan runtime, akan lebih mudah untuk memodelkannya sesuai: Sebagai pemeriksaan eksplisit (seperti is_XXX()dalam PHP atau typeofjavascript), atau dengan melemparkan pengecualian (seperti yang dilakukan Python). Secara fungsional, Anda memiliki efek yang sama (kesalahan ditandai saat runtime ketika pemeriksaan tipe gagal), tetapi terintegrasi lebih baik dengan sisa semantik bahasa. Sangat tidak masuk akal untuk memperlakukan kesalahan tipe yang secara fundamental berbeda dari kesalahan runtime lainnya dalam bahasa yang dinamis.

tammmer
sumber
0

Anda mungkin tertarik dengan Haskell - sistemnya tipe menyimpulkan jenis dari kode, dan Anda dapat menentukan jenis juga.

daven11
sumber
5
Haskell adalah bahasa yang bagus. Entah bagaimana itu bertolak belakang dengan bahasa dinamis: Anda menghabiskan banyak waktu untuk mendeskripsikan jenis, dan biasanya setelah Anda mengetahui jenisnya, program ini bekerja :)
9000
@ 9000: Memang. Setelah dikompilasi, biasanya berfungsi. :)
Macke
@ Macke - untuk nilai yang berbeda dari biasanya , tentu saja. :-) Bagi saya, manfaat terbesar dari sistem tipe dan paradigma fungsional adalah, seperti yang saya tunjukkan di tempat lain, bahwa seseorang tidak perlu peduli apakah perubahan di suatu tempat berdampak pada beberapa kode tergantung di tempat lain - kompiler akan menunjukkan kesalahan tipe dan keadaan yang bisa berubah tidak ada.
Ingo
0

Seperti jawaban lain yang disinggung, ada dua pendekatan untuk mengetik ketika mengimplementasikan bahasa pemrograman.

  1. Mintalah programmer memberi tahu Anda apa yang semua variabel dan fungsi gunakan untuk tipe. Idealnya, Anda juga memverifikasi bahwa spesifikasi jenisnya akurat. Kemudian, karena Anda tahu jenis hal apa yang akan ada di setiap tempat, Anda dapat menulis kode yang mengasumsikan hal yang sesuai akan ada di sana dan menggunakan struktur data apa pun yang Anda gunakan untuk mengimplementasikan tipe itu secara langsung.
  2. Lampirkan indikator jenis ke nilai-nilai yang akan disimpan dalam variabel dan diteruskan ke dan dikembalikan dari fungsi. Ini berarti bahwa programmer tidak perlu menentukan jenis apa pun untuk variabel atau fungsi, karena jenis sebenarnya milik objek variabel dan fungsi merujuk.

Kedua pendekatan itu valid, dan yang digunakan sebagian bergantung pada pertimbangan teknis seperti kinerja, dan sebagian lagi pada alasan politis seperti target pasar untuk bahasa tersebut.

Larry Coleman
sumber
0

Pertama-tama bahasa dinamis diciptakan terutama untuk kemudahan penggunaan. Seperti yang telah Anda sebutkan, sangat bagus untuk mengambil konversi tipe secara otomatis, dan memberikan kami lebih sedikit biaya tambahan. Tetapi pada saat yang sama kekurangan dalam masalah kinerja.

Anda dapat tetap menggunakan bahasa dinamis, jika Anda tidak mengkhawatirkan kinerja. Misalnya, JavaScript berjalan lebih lambat ketika harus melakukan banyak konversi jenis dalam program Anda, tetapi membantu mengurangi jumlah baris dalam kode Anda.

Dan untuk disebutkan, ada bahasa dinamis lain yang bahkan memungkinkan programmer untuk menentukan jenisnya. Misalnya Groovy adalah salah satu bahasa dinamis terkenal yang berjalan di JVM. Dan itu sudah sangat terkenal bahkan dalam beberapa hari terakhir. Perhatikan bahwa kinerja Groovy sama dengan Java.

Semoga ini bisa membantu Anda.

Semut
sumber
-1

Tidak masuk akal untuk melakukannya.

Mengapa?

Karena sistem tipe DTL persis sehingga tipe tidak dapat ditentukan pada waktu kompilasi. Oleh karena itu, kompiler bahkan tidak dapat memeriksa bahwa jenis yang ditentukan masuk akal.

Ingo
sumber
1
Mengapa? Masuk akal untuk mengisyaratkan kompiler tentang jenis apa yang diharapkan. Itu tidak akan bertentangan dengan salah satu kendala jenis sistem.
SK-logic
1
SK-logika: Jika diketik secara dinamis berarti bahwa setiap fungsi / metode / operasi mengambil objek bertipe "Dynamic", "Any" atau apa pun dan mengembalikan "Dynamic", "Any" apa pun, secara umum tidak ada cara untuk mengetahui nilai tertentu akan selalu berupa bilangan bulat, misalnya. Oleh karena itu, kode runtime harus memeriksa yang bukan bilangan bulat, sama seperti jika jenisnya "Dinamis". Inilah yang dilakukan oleh sistem tipe statis: memungkinkan untuk membuktikan bahwa variabel, bidang atau metode pengembalian tertentu akan selalu dari jenis tertentu.
Ingo
@Ingo, tidak, ada caranya. Lihat bagaimana penerapannya di Common Lisp, misalnya. Ini sangat berguna untuk variabel lokal - Anda dapat meningkatkan kinerja secara dramatis dengan memperkenalkan semua petunjuk mengetik.
SK-logic
@ SK-logic: Mungkin Anda bisa memberi tahu saya kapan dan bagaimana kesalahan ketik terdeteksi di CL? Bagaimanapun, @MainMa merangkum status quo dengan cukup baik dalam pertanyaannya: Hanya apa yang bisa diharapkan dari bahasa dinamis "murni".
Ingo
@ Ingo, apa yang membuat Anda berpikir bahwa tipe hanya berguna untuk membuktikan kebenaran secara statis? Itu tidak benar untuk bahasa genap seperti C, di mana Anda mendapatkan casting tipe yang tidak dicentang. Jenis anotasi dalam bahasa dinamis sebagian besar bermanfaat sebagai petunjuk kompiler yang meningkatkan kinerja atau menentukan representasi numerik yang konkret. Saya setuju bahwa dalam kebanyakan kasus, anotasi tidak boleh mengubah semantik kode.
SK-logic
-1

Lihatlah Go, pada permukaan itu diketik secara statis, tetapi jenis-jenis itu mungkin antarmuka yang pada dasarnya dinamis.

dan_waterworth
sumber