Mencari klarifikasi tentang kontradiksi yang tampak mengenai bahasa yang diketik dengan lemah

178

Saya pikir saya mengerti pengetikan yang kuat , tetapi setiap kali saya mencari contoh untuk pengetikan yang lemah, saya akhirnya menemukan contoh bahasa pemrograman yang hanya memaksa / mengubah tipe secara otomatis.

Misalnya, dalam artikel ini bernama Mengetik: Kuat vs Lemah, Statis vs Dinamis mengatakan bahwa Python sangat diketik karena Anda mendapatkan pengecualian jika Anda mencoba untuk:

Python

1 + "1"
Traceback (most recent call last):
File "", line 1, in ? 
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Namun, hal seperti itu dimungkinkan di Jawa dan di C #, dan kami tidak menganggap mereka mengetik dengan lemah hanya untuk itu.

Jawa

  int a = 10;
  String b = "b";
  String result = a + b;
  System.out.println(result);

C #

int a = 10;
string b = "b";
string c = a + b;
Console.WriteLine(c);

Dalam artikel lain yang bernama Weakly Type Languages , penulis mengatakan bahwa Perl diketik dengan lemah hanya karena saya dapat menyatukan string ke angka dan sebaliknya tanpa konversi eksplisit.

Perl

$a=10;
$b="a";
$c=$a.$b;
print $c; #10a

Jadi contoh yang sama membuat Perl diketik dengan lemah, tetapi tidak Java dan C # ?.

Wah, ini membingungkan masukkan deskripsi gambar di sini

Para penulis tampaknya menyiratkan bahwa bahasa yang mencegah penerapan operasi tertentu pada nilai-nilai dari jenis yang berbeda diketik dengan kuat dan sebaliknya berarti diketik dengan lemah.

Oleh karena itu, pada titik tertentu saya merasa terdorong untuk percaya bahwa jika suatu bahasa menyediakan banyak konversi otomatis atau paksaan antar jenis (seperti perl) mungkin dianggap diketik dengan lemah, sedangkan bahasa lain yang hanya menyediakan beberapa konversi mungkin berakhir sebagai dianggap sangat diketik.

Saya cenderung percaya, bahwa saya pasti salah dalam penafsiran ini, saya tidak tahu mengapa atau bagaimana menjelaskannya.

Jadi, pertanyaan saya adalah:

  • Apa arti sebenarnya dari bahasa yang diketik dengan benar-benar lemah?
  • Bisakah Anda menyebutkan contoh baik dari pengetikan lemah yang tidak terkait dengan konversi otomatis / paksaan otomatis yang dilakukan oleh bahasa?
  • Bisakah bahasa diketik dengan lemah dan diketik secara bersamaan?
Edwin Dalorzo
sumber
8
Pengetikan yang kuat versus yang lemah adalah semua tentang konversi jenis (apa lagi yang bisa dilakukan tentang itu?) Jika Anda ingin contoh bahasa lemah "sangat", perhatikan ini: destroyallsoftware.com/talks/wat .
Wilduck
2
@Wildduck Semua bahasa menyediakan konversi jenis, tetapi tidak semua dari mereka dianggap diketik dengan lemah. Contoh saya yang ditunjukkan di bawah ini menunjukkan bagaimana programmer menganggap bahasa yang diketik dengan lemah berdasarkan contoh yang sama yang mungkin pada bahasa lain dianggap sangat diketik. Dengan demikian pertanyaan saya masih berlaku. Apa bedanya?
Edwin Dalorzo
1
Jawaban singkatnya, saya kira, adalah "Typedness" bukanlah kondisi biner. Java dan C # diketik lebih kuat tetapi tidak sepenuhnya.
Jodrell
3
Saya percaya ini lebih cocok untuk Rekayasa Perangkat Lunak .
zzzzBov
4
@ Brendan Bagaimana dengan menjumlahkan float dan integer? Bukankah integer dipaksa menjadi pelampung dengan Python? Apakah Anda akan mengatakan sekarang bahwa Python tidak sepenuhnya diketik dengan kuat?
Edwin Dalorzo

Jawaban:

210

UPDATE: Pertanyaan ini adalah topik blog saya pada tanggal 15 Oktober 2012. Terima kasih atas pertanyaannya!


Apa arti sebenarnya dari bahasa yang "diketik dengan lemah"?

Ini berarti "bahasa ini menggunakan sistem tipe yang menurut saya tidak menyenangkan". Bahasa "sangat diketik" sebagai kontras adalah bahasa dengan sistem tipe yang menurut saya menyenangkan.

Istilah-istilah tersebut pada dasarnya tidak berarti dan Anda harus menghindarinya. Wikipedia mencantumkan sebelas arti berbeda untuk "sangat diketik", beberapa di antaranya kontradiktif. Ini menunjukkan bahwa kemungkinan kebingungan yang dibuat tinggi dalam setiap percakapan yang melibatkan istilah "sangat diketik" atau "diketik dengan lemah".

Yang dapat Anda benar-benar katakan dengan pasti adalah bahwa bahasa "yang diketik" dalam diskusi memiliki beberapa batasan tambahan dalam sistem tipe, baik pada saat runtime atau waktu kompilasi, yang kurang dalam bahasa "diketik" dalam diskusi. Apa pembatasan itu mungkin tidak dapat ditentukan tanpa konteks lebih lanjut.

Alih-alih menggunakan "sangat diketik" dan "diketik dengan lemah", Anda harus menjelaskan secara rinci jenis keamanan yang Anda maksud. Misalnya, C # adalah bahasa yang diketik secara statis dan jenis bahasa yang aman dan bahasa yang aman memori , untuk sebagian besar. C # memungkinkan ketiga bentuk pengetikan "kuat" dilanggar. Operator cor melanggar pengetikan statis; dikatakan ke kompiler "Saya tahu lebih banyak tentang tipe runtime dari ekspresi ini daripada Anda". Jika pengembang salah, maka runtime akan mengeluarkan pengecualian untuk melindungi keamanan jenis. Jika pengembang ingin memutuskan keamanan jenis atau keamanan memori, mereka dapat melakukannya dengan mematikan sistem keamanan jenis dengan membuat blok "tidak aman". Dalam blok yang tidak aman, Anda dapat menggunakan pointer pointer untuk memperlakukan int sebagai pelampung (melanggar keamanan jenis) atau untuk menulis ke memori yang bukan milik Anda. (Melanggar keamanan memori.)

C # memberlakukan batasan jenis yang diperiksa pada waktu kompilasi dan saat runtime, sehingga menjadikannya bahasa yang "sangat diketik" dibandingkan dengan bahasa yang melakukan pengecekan waktu kompilasi lebih sedikit atau pengecekan runtime yang kurang. C # juga memungkinkan Anda untuk secara khusus melakukan penghentian di sekitar pembatasan tersebut, menjadikannya bahasa yang "diketik dengan lemah" dibandingkan dengan bahasa yang tidak memungkinkan Anda untuk melakukan penghentian seperti itu.

Yang mana itu sebenarnya? Tidak mungkin untuk mengatakan; itu tergantung pada sudut pandang pembicara dan sikap mereka terhadap berbagai fitur bahasa.

Eric Lippert
sumber
14
@edalorzo: Ini didasarkan pada selera dan pendapat pribadi tentang (1) aspek apa dari teori tipe yang relevan dan mana yang tidak relevan, dan (2) apakah bahasa diperlukan untuk menegakkan atau hanya mendorong pembatasan jenis. Seperti yang saya tunjukkan, orang bisa mengatakan bahwa C # sangat diketik karena memungkinkan dan mendorong pengetikan statis, dan orang bisa juga mengatakan bahwa itu adalah pengetikan yang lemah karena memungkinkan kemungkinan melanggar keamanan jenis.
Eric Lippert
4
@edalorzo: Untuk perakitan, sekali lagi, ini masalah pendapat. Kompiler bahasa assembly tidak akan memungkinkan Anda untuk memindahkan 64 bit ganda dari tumpukan ke register 32 bit; itu akan memungkinkan Anda untuk memindahkan pointer 32 bit ke 64 bit ganda dari tumpukan ke register 32 bit. Dalam hal ini bahasanya adalah "typesafe" - itu memberlakukan batasan pada legalitas program berdasarkan pada klasifikasi jenis data. Apakah pembatasan itu "kuat" atau "lemah" adalah masalah pendapat, tetapi itu jelas merupakan batasan.
Eric Lippert
2
Saya pikir saya mengerti maksud Anda sekarang, bahasa yang diketik dengan benar-benar lemah harus benar-benar tanpa jenis atau monotipe, yang dalam kehidupan nyata praktis tidak mungkin. Dengan demikian, bahasa apa pun memiliki definisi tipe tertentu, yang aman, dan tergantung pada jumlah lubang yang disediakan bahasa untuk melanggar atau memanipulasi data atau tipe datanya, Anda mungkin berakhir dengan anggapan itu kurang lebih diketik dengan lemah, bahkan mungkin dalam hanya konteks tertentu.
Edwin Dalorzo
7
@edalorzo: Benar. Misalnya, kalkulus lambda yang tidak diketik adalah tentang yang diketik dengan lemah seperti yang Anda dapatkan. Setiap fungsi adalah fungsi dari fungsi ke fungsi; data apa pun dapat diteruskan ke fungsi apa pun tanpa batasan karena semuanya bertipe "sama". Validitas ekspresi dalam kalkulus lambda yang tidak diketik hanya bergantung pada bentuk sintaksisnya, bukan pada analisis semantik yang mengklasifikasikan ekspresi tertentu sebagai tipe tertentu.
Eric Lippert
3
@ Mark saya akan memberinya +1 lagi untuk memprediksi bahwa setiap orang akan memberikan interpretasi yang berbeda pada subjek. "Ketikan yang lemah" ini tampaknya merupakan "konsep mitos" atau "legenda urban", semua orang pernah melihatnya, tetapi tidak ada yang bisa membuktikannya ada :-)
Edwin Dalorzo
64

Seperti yang telah dicatat oleh orang lain, istilah "sangat diketik" dan "diketik dengan lemah" memiliki banyak arti yang berbeda sehingga tidak ada jawaban tunggal untuk pertanyaan Anda. Namun, karena Anda secara spesifik menyebut Perl dalam pertanyaan Anda, izinkan saya mencoba menjelaskan dalam arti apa Perl diketik dengan lemah.

Intinya adalah bahwa, di Perl, tidak ada yang namanya "variabel integer", "variabel float", "variabel string" atau "variabel boolean". Bahkan, sejauh pengguna dapat (biasanya) mengatakan, tidak ada bahkan integer, float, string atau boolean nilai : semua yang Anda miliki adalah "skalar", yang semua hal ini pada waktu yang sama. Jadi, Anda dapat, misalnya, menulis:

$foo = "123" + "456";           # $foo = 579
$bar = substr($foo, 2, 1);      # $bar = 9
$bar .= " lives";               # $bar = "9 lives"
$foo -= $bar;                   # $foo = 579 - 9 = 570

Tentu saja, seperti yang Anda catat dengan benar, semua ini bisa dilihat sebagai tipe paksaan saja. Tapi intinya adalah, di Perl, tipe selalu dipaksa. Pada kenyataannya, cukup sulit bagi pengguna untuk mengetahui apa "tipe" internal dari variabel mungkin: pada baris 2 dalam contoh saya di atas, menanyakan apakah nilai $barstring "9"atau jumlahnya 9cukup banyak tidak berarti, karena, seperti Sejauh Perl prihatin, mereka adalah hal yang sama . Memang, itu bahkan mungkin untuk skalar Perl untuk internal memiliki kedua string dan nilai numerik pada saat yang sama, seperti yang misalnya kasus untuk $foosetelah baris 2 di atas.

Sisi lain dari semua ini adalah bahwa, karena variabel Perl tidak diketik (atau, lebih tepatnya, tidak mengekspos tipe internal mereka kepada pengguna), operator tidak dapat kelebihan beban untuk melakukan hal yang berbeda untuk berbagai jenis argumen; Anda tidak bisa hanya mengatakan "operator ini akan melakukan X untuk angka dan Y untuk string", karena operator tidak dapat (tidak) memberi tahu jenis nilai argumennya.

Jadi, misalnya, Perl memiliki dan membutuhkan operator penambahan numerik ( +) dan operator penggabungan string ( .): seperti yang Anda lihat di atas, tidak apa-apa untuk menambahkan string ( "1" + "2" == "3") atau untuk menggabungkan angka ( 1 . 2 == 12). Demikian pula, operator perbandingan numerik ==, !=, <, >, <=, >=dan <=>membandingkan nilai numerik dari argumen mereka, sedangkan operator perbandingan string eq, ne, lt, gt, le, gedan cmpmembandingkan mereka leksikografis sebagai string. Jadi 2 < 10, tapi 2 gt 10(tapi "02" lt 10, sementara "02" == 2). (Ingat, bahasa - bahasa lain tertentu , seperti JavaScript, coba mengakomodasi sementara mengetik yang lemah seperti Perl)juga melakukan overloading operator. Ini sering mengarah pada keburukan, seperti hilangnya asosiatif +.)

(Lalat dalam salep di sini adalah bahwa, karena alasan historis, Perl 5 memang memiliki beberapa kasus sudut, seperti operator logis bitwise, yang perilakunya tergantung pada representasi internal argumen mereka. Mereka umumnya dianggap cacat desain yang mengganggu, karena representasi internal dapat berubah karena alasan yang mengejutkan, dan dengan demikian memprediksi apa yang dilakukan oleh operator dalam situasi tertentu dapat menjadi rumit.)

Semua yang dikatakan, orang bisa berpendapat bahwa Perl memang memiliki tipe yang kuat; mereka bukan tipe yang Anda harapkan. Secara khusus, selain tipe "skalar" yang dibahas di atas, Perl juga memiliki dua tipe terstruktur: "array" dan "hash". Mereka sangat berbeda dari skalar, ke titik di mana variabel Perl memiliki sigil berbeda yang menunjukkan tipe mereka ( $untuk skalar, @untuk array, %untuk hash) 1 . Ada yang aturan pemaksaan antara jenis ini, sehingga Anda dapat menulis misalnya %foo = @bar, tapi banyak dari mereka yang cukup lossy: misalnya, $foo = @barmenugaskan panjang dari array @baruntuk$foo, bukan isinya. (Juga, ada beberapa jenis aneh lainnya, seperti typeglobs dan I / O handle, yang tidak sering Anda lihat terkena.)

Juga, sedikit celah dalam desain yang bagus ini adalah adanya jenis referensi, yang merupakan jenis khusus skalar (dan yang dapat dibedakan dari skalar normal, menggunakan refoperator). Dimungkinkan untuk menggunakan referensi sebagai skalar normal, tetapi nilai string / numeriknya tidak terlalu berguna, dan mereka cenderung kehilangan referensi khusus mereka jika Anda memodifikasinya menggunakan operasi skalar normal. Juga, setiap variabel Perl 2 dapat blessdiedit ke kelas, mengubahnya menjadi objek dari kelas itu; sistem kelas OO di Perl agak ortogonal dengan tipe primitif (atau ketipisan) sistem yang dijelaskan di atas, meskipun juga "lemah" dalam arti mengikuti bebek mengetikparadigma. Pendapat umum adalah bahwa, jika Anda menemukan diri Anda memeriksa kelas objek di Perl, Anda melakukan sesuatu yang salah.


1 Sebenarnya, sigil menunjukkan tipe nilai yang sedang diakses, sehingga misalnya skalar pertama dalam array @foodilambangkan $foo[0]. Lihat perlfaq4 untuk lebih jelasnya.

2 Objek dalam Perl (biasanya) diakses melalui referensi ke mereka, tetapi apa yang benar-benar didapat blessadalah variabel (mungkin anonim) poin referensi. Namun, berkah memang merupakan properti dari variabel, bukan dari nilainya, jadi mis. Yang menetapkan variabel yang diberkati sebenarnya untuk yang lain hanya memberi Anda salinan yang dangkal dan tidak diberkatinya. Lihat perlobj untuk lebih jelasnya.

Ilmari Karonen
sumber
19

Selain apa yang dikatakan Eric, pertimbangkan kode C berikut:

void f(void* x);

f(42);
f("hello");

Berbeda dengan bahasa seperti Python, C #, Java atau yang lainnya, di atas diketik dengan lemah karena kami kehilangan informasi jenis. Eric dengan benar menunjukkan bahwa dalam C # kita dapat menghindari kompiler dengan casting, secara efektif mengatakan “Saya tahu lebih banyak tentang tipe variabel ini daripada Anda”.

Namun meskipun begitu, runtime masih akan memeriksa jenisnya! Jika gips tidak valid, sistem runtime akan menangkapnya dan melemparkan pengecualian.

Dengan penghapusan tipe, ini tidak terjadi - informasi jenis dibuang. Pemain void*dalam C melakukan hal itu. Dalam hal ini, di atas secara fundamental berbeda dari deklarasi metode C # seperti void f(Object x).

(Secara teknis, C # juga memungkinkan penghapusan tipe melalui kode yang tidak aman atau marshalling.)

Ini diketik dengan lemah. Segala sesuatu yang lain hanyalah masalah pemeriksaan tipe statis vs dinamis, yaitu waktu ketika suatu tipe diperiksa.

Konrad Rudolph
sumber
1
+1 Poin bagus, Anda sekarang membuat saya berpikir tentang penghapusan tipe sebagai fitur yang juga bisa menyiratkan "ketikan yang lemah". Ada juga tipe erasure di Java, dan pada saat runtime, sistem type akan membiarkan Anda melanggar batasan yang tidak akan disetujui oleh kompiler. Contoh C sangat bagus untuk menggambarkan poin.
Edwin Dalorzo
1
Setuju, ada lapisan ke bawang, atau neraka. Ini tampaknya definisi yang lebih signifikan dari kelemahan jenis.
Jodrell
1
@edalorzo Saya tidak berpikir ini cukup sama karena meskipun Java memungkinkan Anda untuk menghindari kompiler, sistem tipe runtime masih akan menangkap pelanggaran. Jadi sistem tipe runtime Java sangat diketik dalam hal ini (ada pengecualian, misalnya di mana refleksi dapat digunakan untuk menghindari kontrol akses).
Konrad Rudolph
1
@edalorzo Anda hanya dapat menghindari kompiler dengan cara ini, bukan sistem runtime. Sangat penting untuk menyadari bahwa bahasa seperti Java dan C # (dan sampai batas tertentu juga C ++) memiliki sistem tipe yang dipastikan dua kali: sekali pada waktu kompilasi, dan sekali pada saat runtime. void*menerobos kedua jenis pemeriksaan. Penghapusan tipe generik tidak, itu hanya menghindari pemeriksaan waktu kompilasi. Ini persis seperti pemeran eksplisit (disebutkan oleh Eric) dalam hal ini.
Konrad Rudolph
1
@edalorzo Re kebingungan Anda: kita seharusnya tidak. Perbedaannya lancar. Dan ya, tipe erasure membuat Java mengetik dengan lemah dalam hal ini. Maksud saya adalah bahwa bahkan dengan penghapusan tipe generik Anda masih tidak dapat menghindari pemeriksaan tipe runtime kecuali Anda juga menggunakan refleksi .
Konrad Rudolph
14

Contoh sempurna berasal dari artikel wikipedia tentang Strong Typing :

Secara umum pengetikan yang kuat menyiratkan bahwa bahasa pemrograman menempatkan pembatasan parah pada pencampuran yang diizinkan terjadi.

Mengetik Lemah

a = 2
b = "2"

concatenate(a, b) # returns "22"
add(a, b) # returns 4

Mengetik dengan kuat

a = 2
b = "2"

concatenate(a, b) # Type Error
add(a, b) # Type Error
concatenate(str(a), b) #Returns "22"
add(a, int(b)) # Returns 4

Perhatikan bahwa bahasa pengetikan yang lemah dapat mencampurkan berbagai jenis tanpa kesalahan. Bahasa tipe yang kuat membutuhkan tipe input untuk menjadi tipe yang diharapkan. Dalam bahasa tipe yang kuat, tipe dapat dikonversi ( str(a)mengonversi bilangan bulat menjadi string) atau melemparkan ( int(b)).

Ini semua tergantung pada interpretasi mengetik.

SaulBack
sumber
3
Tetapi ini mengarah pada contoh-contoh kontradiktif yang disediakan dalam pertanyaan. Bahasa yang diketik dengan kuat dapat menyertakan pemaksaan tersirat yang berarti salah satu (atau keduanya) dari dua contoh "Kesalahan Jenis" Anda secara otomatis dikonversi ke yang relevan dari dua contoh kedua, tetapi secara umum bahasa itu masih diketik dengan kuat.
Mark Hurd
3
Benar. Saya kira Anda bisa mengatakan ada berbagai tingkat pengetikan kuat dan pengetikan lemah. Konversi implisit dapat berarti bahwa bahasa tersebut diketik kurang kuat daripada bahasa yang tidak melakukan konversi implisit.
SaulBack
4

Saya ingin berkontribusi dalam diskusi dengan penelitian saya sendiri tentang masalah ini, ketika orang lain berkomentar dan berkontribusi saya telah membaca jawaban mereka dan mengikuti referensi mereka dan saya telah menemukan informasi yang menarik. Seperti yang disarankan, besar kemungkinan bahwa ini akan lebih baik dibahas di forum Programmer, karena tampaknya lebih teoretis daripada praktis.

Dari sudut pandang teoretis, saya pikir artikel oleh Luca Cardelli dan Peter Wegner bernama On Understanding Types, Abstraction Data and Polymorphism memiliki salah satu argumen terbaik yang pernah saya baca.

Jenis dapat dilihat sebagai satu set pakaian (atau baju besi) yang melindungi sebuah mendasari untyped representasi dari penggunaan sewenang-wenang atau tidak disengaja. Ini memberikan penutup pelindung yang menyembunyikan representasi yang mendasari dan membatasi cara objek dapat berinteraksi dengan objek lain. Dalam sistem yang tidak diketik, objek yang tidak diketik telanjang dalam representasi yang mendasari terbuka bagi semua untuk melihat. Melanggar sistem jenis melibatkan melepas set pakaian pelindung dan beroperasi langsung pada representasi telanjang.

Pernyataan ini tampaknya menunjukkan bahwa pengetikan yang lemah akan memungkinkan kita mengakses struktur bagian dalam suatu tipe dan memanipulasinya seolah-olah itu sesuatu yang lain (tipe lain). Mungkin apa yang bisa kita lakukan dengan kode yang tidak aman (disebutkan oleh Eric) atau dengan pointer tipe-terhapus yang disebutkan oleh Konrad.

Artikel berlanjut ...

Bahasa di mana semua ekspresi konsisten dengan jenis disebut bahasa yang sangat diketik. Jika suatu bahasa diketik dengan kuat, kompilernya dapat menjamin bahwa program yang diterimanya akan dijalankan tanpa kesalahan ketik. Secara umum, kita harus berusaha keras mengetik, dan mengadopsi pengetikan statis jika memungkinkan. Perhatikan bahwa setiap bahasa yang diketik secara statis diketik dengan kuat tetapi kebalikannya tidak selalu benar.

Karena itu, pengetikan yang kuat berarti tidak adanya kesalahan ketik, saya hanya bisa berasumsi bahwa pengetikan yang lemah berarti sebaliknya: kemungkinan adanya kesalahan ketik. Saat runtime atau waktu kompilasi? Tampaknya tidak relevan di sini.

Lucunya, sesuai definisi ini, bahasa dengan paksaan tipe kuat seperti Perl akan dianggap sangat diketik, karena sistem tidak gagal, tetapi berhadapan dengan jenis-jenis dengan memaksa mereka ke dalam kesetaraan yang tepat dan didefinisikan dengan baik.

Di sisi lain, dapatkah saya mengatakan selain dari kelonggaran ClassCastExceptiondan ArrayStoreException(di Jawa) dan InvalidCastException, ArrayTypeMismatchException(dalam C #) akan menunjukkan tingkat pengetikan yang lemah, setidaknya pada waktu kompilasi? Jawaban Eric sepertinya setuju dengan ini.

Dalam artikel kedua bernama Pemrograman Typeful yang disediakan di salah satu referensi yang disediakan di salah satu jawaban dalam pertanyaan ini, Luca Cardelli menyelidiki konsep pelanggaran tipe:

Sebagian besar bahasa pemrograman sistem memungkinkan pelanggaran jenis sewenang-wenang, beberapa tanpa pandang bulu, beberapa hanya di bagian terbatas dari suatu program. Operasi yang melibatkan jenis pelanggaran disebut tidak sehat. Jenis pelanggaran termasuk dalam beberapa kelas [di antaranya dapat kami sebutkan]:

Paksaan nilai dasar : Ini termasuk konversi antara bilangan bulat, boolean, karakter, set, dll. Tidak perlu untuk pelanggaran tipe di sini, karena antarmuka bawaan dapat disediakan untuk melakukan paksaan dengan cara jenis suara.

Dengan demikian, tipe pemaksaan seperti yang disediakan oleh operator dapat dianggap sebagai pelanggaran tipe, tetapi kecuali jika mereka merusak konsistensi sistem tipe, kita dapat mengatakan bahwa mereka tidak mengarah pada sistem yang diketik dengan lemah.

Berdasarkan ini, baik Python, Perl, Java atau C # tidak diketik dengan benar.

Cardelli menyebutkan dua jenis fitnah yang saya anggap sangat baik dalam kasus pengetikan yang benar-benar lemah:

Alamat aritmatika. Jika perlu, harus ada antarmuka built-in (tidak sehat), menyediakan operasi yang memadai pada alamat dan jenis konversi. Berbagai situasi melibatkan pointer ke tumpukan (sangat berbahaya dengan memindahkan kolektor), pointer ke tumpukan, pointer ke area statis, dan pointer ke ruang alamat lainnya. Terkadang pengindeksan array dapat menggantikan aritmatika alamat. Pemetaan memori. Ini melibatkan melihat area memori sebagai array yang tidak terstruktur, meskipun berisi data terstruktur. Ini tipikal dari pengalokasi dan pengumpul memori.

Hal-hal semacam ini dimungkinkan dalam bahasa seperti C (disebutkan oleh Konrad) atau melalui kode yang tidak aman di .Net (disebutkan oleh Eric) akan benar-benar menyiratkan pengetikan yang lemah.

Saya percaya jawaban terbaik sejauh ini adalah Eric, karena definisi konsep-konsep ini sangat teoretis, dan ketika sampai pada bahasa tertentu, interpretasi semua konsep ini dapat mengarah pada kesimpulan yang dapat diperdebatkan.

Edwin Dalorzo
sumber
4

Mengetik yang lemah memang berarti bahwa persentase tipe yang tinggi dapat dipaksakan secara implisit, mencoba menebak apa yang dimaksud oleh pembuat kode.

Mengetik dengan kuat berarti jenis tidak dipaksakan, atau setidaknya dipaksakan kurang.

Pengetikan statis berarti tipe variabel Anda ditentukan pada waktu kompilasi.

Banyak orang baru-baru ini bingung "diketik secara nyata" dengan "sangat diketik". "Manifestly typed" artinya Anda mendeklarasikan tipe variabel secara eksplisit.

Python sebagian besar sangat diketik, meskipun Anda dapat menggunakan hampir semua hal dalam konteks boolean, dan boolean dapat digunakan dalam konteks integer, dan Anda dapat menggunakan integer dalam konteks float. Ini tidak diketik secara nyata, karena Anda tidak perlu mendeklarasikan tipe Anda (kecuali untuk Cython, yang tidak sepenuhnya python, meskipun menarik). Ini juga tidak diketik secara statis.

C dan C ++ diketik secara nyata, diketik secara statis, dan agak diketik keras, karena Anda menyatakan tipe Anda, tipe ditentukan pada waktu kompilasi, dan Anda dapat mencampur bilangan bulat dan pointer, atau bilangan bulat dan ganda, atau bahkan melemparkan pointer ke satu jenis ke dalam pointer ke tipe lain.

Haskell adalah contoh yang menarik, karena tidak diketik secara nyata, tetapi juga diketik secara statis dan kuat.

pengguna1277476
sumber
+1 Karena saya menyukai istilah coined "typly typed", yang mengategorikan bahasa seperti Java dan C # di mana Anda harus secara eksplisit mendeklarasikan tipe dan membedakannya dari bahasa tipe statis lainnya seperti Haskell dan Scala di mana inferensi tipe memainkan peran penting dan ini biasanya membingungkan orang, seperti yang Anda katakan, dan membuat mereka percaya bahwa bahasa-bahasa ini diketik secara dinamis.
Edwin Dalorzo
3

Yang kuat <=> lemah mengetik tidak hanya tentang kontinum pada seberapa banyak atau sedikit dari nilai-nilai yang dipaksa secara otomatis oleh bahasa untuk satu datatype yang lain, tetapi seberapa kuat atau lemah yang sebenarnya nilai-nilai yang diketik. Dalam Python dan Java, dan sebagian besar dalam C #, nilai-nilai memiliki tipe mereka diatur dalam batu. Dalam Perl, tidak begitu banyak - hanya ada beberapa set nilai yang berbeda untuk disimpan dalam variabel.

Mari kita buka kasing satu per satu.


Python

Dalam contoh Python 1 + "1", +operator memanggil __add__tipe untuk intmemberinya string "1"sebagai argumen - namun, ini menghasilkan NotImplemented:

>>> (1).__add__('1')
NotImplemented

Selanjutnya, juru bahasa mencoba __radd__str:

>>> '1'.__radd__(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__radd__'

Karena gagal, +operator gagal dengan hasilnya TypeError: unsupported operand type(s) for +: 'int' and 'str'. Dengan demikian, perkecualian tidak mengatakan banyak tentang pengetikan yang kuat, tetapi fakta bahwa operator + tidak memaksa argumennya secara otomatis ke tipe yang sama, adalah penunjuk fakta bahwa Python bukan bahasa yang diketik paling lemah dalam kontinum.

Di sisi lain, di Python 'a' * 5 adalah diimplementasikan:

>>> 'a' * 5
'aaaaa'

Itu adalah,

>>> 'a'.__mul__(5)
'aaaaa'

Fakta bahwa operasi berbeda memerlukan pengetikan yang kuat - namun kebalikan dari *pemaksaan nilai ke angka sebelum dikalikan masih belum tentu membuat nilai diketik dengan lemah.


Jawa

Contoh Java, String result = "1" + 1;hanya berfungsi karena sebagai fakta kenyamanan, operator +kelebihan beban untuk string. +Operator Java menggantikan urutan dengan menciptakan StringBuilder(lihat ini ):

String result = a + b;
// becomes something like
String result = new StringBuilder().append(a).append(b).toString()

Ini lebih merupakan contoh pengetikan yang sangat statis, tanpa paksaan yang sebenarnya - StringBuildermemiliki metode append(Object)yang khusus digunakan di sini. Dokumentasi mengatakan sebagai berikut:

Menambahkan representasi string Objectargumen.

Efek keseluruhannya persis seperti argumen yang dikonversi ke string oleh metode String.valueOf(Object), dan karakter dari string itu kemudian ditambahkan ke urutan karakter ini.

String.valueOfLalu dimana

Mengembalikan representasi string dari argumen Object. [Kembali] jika argumennya adalah null, maka sebuah string sama dengan "null"; jika tidak, nilai obj.toString()dikembalikan.

Jadi ini adalah kasus yang sama sekali tidak ada paksaan oleh bahasa - mendelegasikan setiap masalah ke objek itu sendiri.


C #

Menurut jawaban Jon Skeet di sini , operator +bahkan tidak kelebihan beban untuk stringkelas - seperti Java, ini hanya kenyamanan yang dihasilkan oleh kompiler, berkat pengetikan yang statis dan kuat.


Perl

Seperti yang dijelaskan perldata ,

Perl memiliki tiga tipe data bawaan: skalar, array skalar, dan array asosiatif skalar, yang dikenal sebagai "hashes". Skalar adalah string tunggal (ukuran apa pun, hanya dibatasi oleh memori yang tersedia), angka, atau referensi ke sesuatu (yang akan dibahas dalam perlref). Array normal adalah daftar skalar yang diindeks berdasarkan angka, dimulai dengan 0. Hash adalah kumpulan nilai skalar yang tidak berurutan yang diindeks oleh kunci string yang terkait.

Namun Perl tidak memiliki tipe data terpisah untuk angka, boolean, string, nulls, undefineds, referensi ke objek lain dll - ia hanya memiliki satu tipe untuk semua ini, tipe skalar; 0 adalah nilai skalar sebanyak "0". Variabel skalar yang ditetapkan sebagai string dapat benar-benar berubah menjadi angka, dan dari sana berperilaku berbeda dari "hanya string", jika diakses dalam konteks numerik. Skalar dapat menampung apa saja di Perl, objek sebanyak yang ada dalam sistem. sedangkan dalam Python nama hanya merujuk ke objek, di Perl nilai skalar dalam nama adalah objek yang dapat diubah. Lebih lanjut, sistem Object Oriented Type terpaku di atasnya: hanya ada 3 tipe data dalam perl - skalar, daftar dan hash. Objek yang ditetapkan pengguna dalam Perl adalah referensi (yang merupakan pointer ke salah satu dari 3 sebelumnya) yang blessdiedit ke sebuah paket - Anda dapat mengambil nilai seperti itu dan memberkatinya ke kelas mana saja kapan saja Anda inginkan.

Perl bahkan memungkinkan Anda untuk mengubah kelas nilai saat itu - ini tidak mungkin dalam Python di mana untuk membuat nilai beberapa kelas Anda perlu secara eksplisit membangun nilai milik kelas itu dengan object.__new__atau serupa. Dengan Python Anda tidak dapat mengubah esensi objek setelah penciptaan, di Perl Anda dapat melakukan banyak hal:

package Foo;
package Bar;

my $val = 42;
# $val is now a scalar value set from double
bless \$val, Foo;
# all references to $val now belong to class Foo
my $obj = \$val;
# now $obj refers to the SV stored in $val
# thus this prints: Foo=SCALAR(0x1c7d8c8)
print \$val, "\n"; 
# all references to $val now belong to class Bar
bless \$val, Bar;
# thus this prints Bar=SCALAR(0x1c7d8c8)
print \$val, "\n";
# we change the value stored in $val from number to a string
$val = 'abc';
# yet still the SV is blessed: Bar=SCALAR(0x1c7d8c8)
print \$val, "\n";
# and on the course, the $obj now refers to a "Bar" even though
# at the time of copying it did refer to a "Foo".
print $obj, "\n";

dengan demikian identitas tipe lemah terikat pada variabel, dan dapat diubah melalui referensi apa pun dengan cepat. Bahkan, jika Anda melakukannya

my $another = $val;

\$anothertidak memiliki identitas kelas, meskipun \$valmasih akan memberikan referensi yang diberkati.


TL; DR

Ada lebih banyak tentang pengetikan lemah ke Perl daripada hanya paksaan otomatis, dan lebih banyak tentang jenis nilai itu sendiri tidak ditetapkan menjadi batu, tidak seperti Python yang secara dinamis namun sangat diketik bahasa. Python memberi TypeErrorpada 1 + "1"merupakan indikasi bahwa bahasa tersebut sangat diketik, meskipun sebaliknya melakukan sesuatu yang bermanfaat, seperti di Jawa atau C # tidak menghalangi mereka menjadi bahasa yang diketik dengan kuat.

Antti Haapala
sumber
Ini sangat membingungkan. Variabel Perl 5 yang tidak memiliki tipe tidak memiliki kaitan dengan nilai , yang selalu memiliki tipe.
Jim Balter
@ JimBalter baik, ya, nilai memiliki tipe di mana itu adalah string atau angka, dan itu dapat berperilaku berbeda dalam beberapa konteks tergantung pada apakah variabel skalar berisi string atau angka; tetapi nilai yang terkandung dalam variabel dapat mengubah jenis dengan hanya mengakses variabel, dan karena nilai itu sendiri tinggal di dalam variabel, nilai itu sendiri dapat dianggap bisa berubah di antara jenis.
Antti Haapala
Nilai tidak mengubah tipe - itu tidak jelas; suatu nilai selalu bertipe . Nilai yang berisi variabel dapat berubah. Perubahan dari 1 menjadi "1" sama seperti perubahan dalam nilai dari perubahan dari 1 menjadi 2.
Jim Balter
Bahasa yang diketik dengan lemah seperti Perl memungkinkan tipe perubahan nilai yang sebelumnya terjadi secara implisit tergantung pada konteksnya. Tetapi bahkan C ++ memungkinkan konversi tersirat tersebut melalui definisi operator. Mengetik yang lemah adalah properti yang sangat informal dan benar-benar bukan cara yang berguna untuk menggambarkan bahasa, seperti yang ditunjukkan Eric Lippert.
Jim Balter
PS Dapat ditunjukkan bahwa, bahkan di Perl, <digits> dan "<digits>" memiliki nilai yang berbeda, bukan hanya tipe yang berbeda. Perl membuat <digits> dan "<digits>" tampaknya memiliki nilai yang sama dalam kebanyakan kasus melalui konversi tersirat , tetapi ilusi tidak lengkap; mis. "12" | "34" adalah 36 sedangkan 12 | 34 adalah 46. Contoh lain adalah bahwa "00" secara numerik sama dengan 00 di sebagian besar konteks, tetapi tidak dalam konteks boolean, di mana "00" benar, tetapi 00 salah.
Jim Balter
1

Seperti yang banyak orang lain nyatakan, seluruh gagasan mengetik "kuat" vs "lemah" bermasalah.

Sebagai arketipe, Smalltalk diketik dengan sangat kuat - itu akan selalu menimbulkan pengecualian jika operasi antara dua objek tidak kompatibel. Namun, saya menduga beberapa orang dalam daftar ini akan menyebut Smalltalk bahasa yang sangat diketik, karena diketik secara dinamis.

Saya menemukan gagasan mengetik "statis" versus "dinamis" lebih bermanfaat daripada "kuat" versus "lemah." Bahasa yang diketik secara statis memiliki semua jenis yang ditemukan pada saat kompilasi, dan programmer harus secara eksplisit menyatakan jika sebaliknya.

Berbeda dengan bahasa yang diketik secara dinamis, di mana pengetikan dilakukan saat run-time. Ini biasanya merupakan persyaratan untuk bahasa polimorfik, sehingga keputusan tentang apakah operasi antara dua objek adalah legal tidak harus diputuskan oleh programmer terlebih dahulu.

Dalam polimorfik, bahasa yang diketik secara dinamis (seperti Smalltalk dan Ruby), lebih berguna untuk menganggap "tipe" sebagai "kesesuaian dengan protokol." Jika suatu objek mematuhi protokol dengan cara yang sama seperti objek lain - bahkan jika dua objek tidak berbagi warisan atau mixin atau voodoo lainnya - mereka dianggap "tipe" yang sama oleh sistem run-time. Lebih tepatnya, objek dalam sistem seperti itu adalah otonom, dan dapat memutuskan apakah masuk akal untuk menanggapi pesan tertentu yang merujuk pada argumen tertentu.

Ingin objek yang dapat membuat respons bermakna terhadap pesan "+" dengan argumen objek yang menggambarkan warna biru? Anda dapat melakukannya dalam bahasa yang diketik secara dinamis, tetapi itu menyusahkan dalam bahasa yang diketik secara statis.

Jan Steinman
sumber
3
Saya pikir konsep pengetikan dinamis vs statis tidak sedang dibahas. Meskipun saya harus mengatakan bahwa saya tidak percaya bahwa polimorfisme adalah cacat dalam jenis bahasa statis. Pada akhirnya, sistem tipe memverifikasi jika operasi yang diberikan berlaku untuk operan yang diberikan, apakah pada saat runtime atau pada waktu kompilasi. Juga, bentuk-bentuk polimorfisme lain, seperti fungsi parametrik dan kelas memungkinkan penggabungan tipe dalam bahasa tipe statis dengan cara yang Anda gambarkan sangat sulit bila dibandingkan dengan yang diketik secara dinamis, bahkan lebih bagus jika inferensi tipe disediakan.
Edwin Dalorzo
0

Saya suka jawaban @Eric Lippert , tetapi untuk menjawab pertanyaan - bahasa yang diketik sangat biasanya memiliki pengetahuan eksplisit tentang jenis variabel pada setiap titik program. Bahasa yang diketik dengan lemah tidak, sehingga mereka dapat mencoba melakukan operasi yang mungkin tidak dapat dilakukan untuk jenis tertentu. Itu berpikir cara termudah untuk melihat ini dalam suatu fungsi. C ++:

void func(string a) {...}

Variabel adiketahui tipe string dan setiap operasi yang tidak kompatibel akan ditangkap pada waktu kompilasi.

Python:

def func(a)
  ...

Variabelnya abisa apa saja dan kita dapat memiliki kode yang memanggil metode yang tidak valid, yang hanya akan tertangkap saat runtime.

Lubo Antonov
sumber
12
Saya pikir Anda mungkin membingungkan mengetik dinamis vs mengetik statis dengan mengetik kuat vs mengetik lemah. Di kedua versi kode Anda, sistem tipe runtime tahu betul bahwa a adalah string. Hanya saja dalam kasus pertama, kompiler dapat memberi tahu Anda bahwa, dalam kasus kedua tidak. Tapi ini tidak membuat salah satu dari bahasa ini diketik dengan lemah.
Edwin Dalorzo