Fungsi apa yang diizinkan oleh pengetikan dinamis? [Tutup]

91

Saya telah menggunakan python selama beberapa hari sekarang dan saya pikir saya mengerti perbedaan antara pengetikan dinamis dan statis. Apa yang saya tidak mengerti adalah dalam keadaan apa itu akan lebih disukai. Ini fleksibel dan mudah dibaca, tetapi dengan mengorbankan lebih banyak pemeriksaan runtime dan pengujian unit tambahan yang diperlukan.

Selain kriteria non-fungsional seperti fleksibilitas dan keterbacaan, alasan apa yang ada untuk memilih pengetikan dinamis? Apa yang bisa saya lakukan dengan pengetikan dinamis yang tidak dimungkinkan? Contoh kode spesifik apa yang dapat Anda pikirkan yang menggambarkan keuntungan nyata dari pengetikan dinamis?

Justin984
sumber
5
Secara teoritis tidak ada yang bisa Anda lakukan di keduanya, asalkan bahasa Turing Lengkap . Pertanyaan yang lebih menarik bagi saya adalah apa yang mudah atau alami dalam satu vs yang lain. Ada hal-hal yang saya lakukan secara teratur dalam Python yang saya bahkan tidak akan pertimbangkan dalam C ++ meskipun saya tahu itu mampu.
Mark Ransom
28
Seperti yang ditulis oleh Chris Smith dalam esainya yang luar biasa Apa yang perlu diketahui sebelum berdebat tentang sistem tipe : "Masalahnya, dalam hal ini, adalah kebanyakan programmer memiliki pengalaman yang terbatas, dan belum mencoba banyak bahasa. Untuk konteks, di sini, enam atau tujuh tidak dihitung sebagai "banyak." ... Dua konsekuensi menarik dari ini adalah: (1) Banyak programmer menggunakan bahasa yang diketik secara statis sangat buruk. (2) Banyak programmer yang menggunakan bahasa yang diketik secara dinamis dengan sangat buruk. "
Daniel Pryden
3
@suslik: Jika bahasa primitif memiliki tipe yang tidak masuk akal, maka tentu saja Anda dapat melakukan hal-hal yang tidak masuk akal dengan tipe. Itu tidak ada hubungannya dengan perbedaan antara pengetikan statis dan dinamis.
Jon Purdy
10
@CzarekTomczak: Itu adalah fitur dari beberapa bahasa yang diketik secara dinamis, ya. Tetapi dimungkinkan untuk bahasa yang diketik secara statis dapat dimodifikasi pada saat runtime. Sebagai contoh, Visual Studio memungkinkan Anda untuk menulis ulang kode C # saat Anda berada di breakpoint di debugger, dan bahkan mundur pointer instruksi untuk menjalankan kembali kode Anda dengan perubahan baru. Seperti yang saya kutip dari Chris Smith dalam komentar saya yang lain: "Banyak programmer menggunakan bahasa yang diketik secara statis sangat buruk" - jangan menilai semua bahasa yang diketik secara statis oleh yang Anda tahu.
Daniel Pryden
11
@ WarrenP: Anda menyatakan bahwa "sistem tipe dinamis mengurangi jumlah cruft tambahan yang harus saya ketik" - tetapi kemudian Anda membandingkan Python ke C ++. Itu bukan perbandingan yang adil: tentu saja C ++ lebih verbose daripada Python, tapi itu bukan karena perbedaan dalam sistem tipe mereka, itu karena perbedaan dalam tata bahasa mereka. Jika Anda hanya ingin mengurangi jumlah karakter di sumber program Anda, pelajari J atau APL: Saya jamin mereka akan lebih pendek. Perbandingan yang lebih adil adalah membandingkan Python dengan Haskell. (Sebagai catatan: Saya suka Python dan lebih suka daripada C ++, tapi saya lebih suka Haskell.)
Daniel Pryden

Jawaban:

50

Karena Anda meminta contoh spesifik, saya akan memberikan satu.

ORM besar - besaran Rob Conery adalah 400 baris kode. Itu kecil karena Rob mampu memetakan tabel SQL dan memberikan hasil objek tanpa memerlukan banyak jenis statis untuk mencerminkan tabel SQL. Ini dilakukan dengan menggunakan dynamictipe data dalam C #. Halaman web Rob menjelaskan proses ini secara terperinci, tetapi tampak jelas bahwa, dalam kasus penggunaan khusus ini, pengetikan dinamis sebagian besar bertanggung jawab atas singkatnya kode.

Bandingkan dengan Dapper Sam Saffron , yang menggunakan tipe statis; yang SQLMapperkelas saja 3000 baris kode.

Perhatikan bahwa sangkalan yang biasa berlaku, dan jarak tempuh Anda dapat bervariasi; Dapper memiliki tujuan yang berbeda dengan Massive. Saya hanya menunjukkan ini sebagai contoh dari sesuatu yang dapat Anda lakukan dalam 400 baris kode yang mungkin tidak akan mungkin tanpa pengetikan dinamis.


Pengetikan dinamis memungkinkan Anda untuk menunda keputusan mengetik untuk runtime. Itu saja.

Apakah Anda menggunakan bahasa yang diketik secara dinamis atau yang diketik secara statis, pilihan jenis Anda harus tetap masuk akal. Anda tidak akan menambahkan dua string bersama dan mengharapkan jawaban numerik kecuali string berisi data numerik, dan jika tidak, Anda akan mendapatkan hasil yang tidak terduga. Bahasa yang diketik secara statis tidak akan membiarkan Anda melakukan ini sejak awal.

Para pendukung jenis bahasa statis menunjukkan bahwa kompiler dapat melakukan sejumlah besar "kewarasan memeriksa" kode Anda pada waktu kompilasi, sebelum satu baris dieksekusi. Ini adalah Good Thing ™.

C # memiliki dynamickata kunci, yang memungkinkan Anda untuk menunda keputusan tipe untuk runtime tanpa kehilangan manfaat keamanan tipe statis di sisa kode Anda. Ketik inferensi ( var) menghilangkan banyak rasa sakit menulis dalam bahasa yang diketik secara statis dengan menghilangkan kebutuhan untuk selalu secara eksplisit menyatakan jenis.


Bahasa dinamis tampaknya lebih menyukai pendekatan yang lebih interaktif dan langsung untuk pemrograman. Tidak ada yang mengharapkan Anda harus menulis kelas dan melalui siklus kompilasi untuk mengetikkan sedikit kode Lisp dan melihatnya mengeksekusi. Namun itulah yang saya harapkan dilakukan di C #.

Robert Harvey
sumber
22
Jika saya menambahkan dua string numerik bersama, saya masih tidak akan mengharapkan hasil numerik.
PDR
22
@ Robert Saya setuju dengan sebagian besar jawaban Anda. Namun, perhatikan bahwa ada bahasa yang diketik secara statis dengan loop read-eval-print interaktif, seperti Scala dan Haskell. Mungkin C # itu bukan bahasa yang sangat interaktif.
Andres F.
14
Harus mengajariku Haskell.
Robert Harvey
7
@RobertHarvey: Anda mungkin akan terkejut / terkesan dengan F # jika Anda belum mencobanya. Anda mendapatkan semua jenis keamanan (waktu kompilasi) yang biasanya Anda dapatkan dalam bahasa .NET, kecuali bahwa Anda jarang harus mendeklarasikan jenis apa pun. Jenis inferensi dalam F # melampaui apa yang tersedia / bekerja di C #. Juga: mirip dengan apa yang ditunjukkan oleh Andres dan Daniel, F # interactive adalah bagian dari Visual Studio ...
Steven Evers
8
"Anda tidak akan menambahkan dua string bersama dan mengharapkan jawaban numerik kecuali string berisi data numerik, dan jika tidak, Anda akan mendapatkan hasil yang tidak terduga" maaf, ini tidak ada hubungannya dengan pengetikan dinamis vs statis , ini mengetik kuat vs lemah .
vartec
26

Frasa seperti "pengetikan statis" dan "pengetikan dinamis" banyak digunakan, dan orang-orang cenderung menggunakan definisi yang agak berbeda, jadi mari kita mulai dengan mengklarifikasi apa yang kita maksudkan.

Pertimbangkan bahasa yang memiliki tipe statis yang diperiksa pada waktu kompilasi. Tetapi katakan bahwa kesalahan ketik hanya menghasilkan peringatan non-fatal, dan pada saat runtime, semuanya diketik bebek. Tipe statis ini hanya untuk kenyamanan programmer, dan tidak mempengaruhi codegen. Ini menggambarkan bahwa pengetikan statis tidak dengan sendirinya memaksakan batasan apa pun, dan tidak saling eksklusif dengan pengetikan dinamis. (Objective-C sangat banyak seperti ini.)

Tetapi kebanyakan sistem tipe statis tidak berperilaku seperti ini. Ada dua sifat umum dari sistem tipe statis yang dapat memaksakan batasan:

Kompiler dapat menolak program yang berisi kesalahan tipe statis.

Ini adalah batasan karena banyak program aman jenis tentu mengandung kesalahan jenis statis.

Sebagai contoh, saya memiliki skrip Python yang perlu dijalankan karena Python 2 dan Python 3. Beberapa fungsi mengubah tipe parameter mereka antara Python 2 dan 3, jadi saya punya kode seperti ini:

if sys.version_info[0] == 2:
    wfile.write(txt)
else:
    wfile.write(bytes(txt, 'utf-8'))

Pemeriksa tipe statis Python 2 akan menolak kode Python 3 (dan sebaliknya), meskipun itu tidak akan pernah dieksekusi. Program safe type saya mengandung kesalahan tipe statis.

Sebagai contoh lain, pertimbangkan program Mac yang ingin dijalankan pada OS X 10.6, tetapi manfaatkan fitur-fitur baru di 10.7. Metode 10.7 mungkin ada atau tidak ada saat runtime, dan ada pada saya, programmer, untuk mendeteksinya. Pemeriksa tipe statis dipaksa untuk menolak program saya untuk memastikan keamanan jenis, atau menerima program, bersama dengan kemungkinan menghasilkan kesalahan jenis (fungsi hilang) saat runtime.

Pemeriksaan tipe statis mengasumsikan bahwa lingkungan runtime dijelaskan dengan memadai oleh informasi waktu kompilasi. Tetapi memprediksi masa depan itu berbahaya!

Berikut ini satu batasan lagi:

Compiler dapat menghasilkan kode yang menganggap tipe runtime adalah tipe statis.

Dengan asumsi tipe statis "benar" memberikan banyak peluang untuk optimasi, tetapi optimasi ini dapat membatasi. Contoh yang baik adalah objek proxy, misalnya remoting. Katakanlah Anda ingin memiliki objek proxy lokal yang meneruskan pemanggilan metode ke objek nyata dalam proses lain. Akan lebih baik jika proxy itu generik (sehingga dapat menyamar sebagai objek apa pun) dan transparan (sehingga kode yang ada tidak perlu tahu itu berbicara ke proxy). Tetapi untuk melakukan ini, kompiler tidak dapat menghasilkan kode yang menganggap tipe statis sudah benar, misalnya dengan pemanggilan metode inlining secara statis, karena itu akan gagal jika objek sebenarnya adalah proxy.

Contoh dari tindakan remoting seperti itu termasuk NSXPCConnection milik ObjC atau TransparentProxy C # (yang implementasinya membutuhkan beberapa pesimisasi dalam runtime - lihat di sini untuk diskusi).

Ketika codegen tidak bergantung pada tipe statis, dan Anda memiliki fasilitas seperti penerusan pesan, Anda dapat melakukan banyak hal keren dengan objek proxy, debugging, dll.

Jadi itu adalah contoh dari beberapa hal yang dapat Anda lakukan jika Anda tidak diharuskan untuk memuaskan pemeriksa tipe. Batasan tidak dikenakan oleh tipe statis, tetapi oleh pemeriksaan tipe statis yang dipaksakan.

ridiculous_fish
sumber
2
"Pemeriksa tipe statis Python 2 akan menolak kode Python 3 (dan sebaliknya), meskipun itu tidak akan pernah dieksekusi. Program safe type saya mengandung kesalahan tipe statis." Kedengarannya seperti apa yang Anda benar-benar perlu ada semacam "statis jika", di mana kompiler / penerjemah bahkan tidak melihat kode jika kondisinya salah.
David Stone
@davidstone Itu ada di c ++
Milind R
A Python 2 static type checker would reject the Python 3 code (and vice versa), even though it would never be executed. My type safe program contains a static type error. Dalam bahasa statis apa pun yang masuk akal, Anda bisa melakukan ini dengan IFDEFpernyataan preprocessor tipe, sambil menjaga keamanan tipe dalam kedua kasus.
Mason Wheeler
1
@MasonWheeler, davidstone Tidak, trik preprosesor dan static_if keduanya terlalu statis. Dalam contoh saya, saya menggunakan Python2 dan Python3, tetapi bisa saja dengan mudah seperti AmazingModule2.0 dan AmazingModule3.0, di mana beberapa antarmuka berubah antar versi. Paling awal Anda dapat mengetahui antarmuka adalah pada waktu impor modul, yang tentu saat runtime (setidaknya jika Anda memiliki keinginan untuk mendukung tautan dinamis).
ridiculous_fish
18

Variabel tipe-bebek adalah hal pertama yang dipikirkan semua orang, tetapi dalam kebanyakan kasus Anda bisa mendapatkan manfaat yang sama melalui inferensi tipe statis.

Tetapi mengetik bebek dalam koleksi yang dibuat secara dinamis sulit dicapai dengan cara lain:

>>> d = JSON.parse(foo)
>>> d['bar'][3]
12
>>> d['baz']['qux']
'quux'

Jadi, tipe apa yang JSON.parsekembali? Kamus array-of-integer-atau-kamus-of-string? Tidak, bahkan itu tidak cukup umum.

JSON.parseharus mengembalikan semacam "nilai varian" yang dapat berupa null, bool, float, string, array dari semua jenis ini secara rekursif, atau kamus dari string ke salah satu dari jenis ini secara rekursif. Kekuatan utama dari pengetikan dinamis berasal dari jenis varian yang demikian.

Sejauh ini, ini adalah manfaat dari tipe dinamis , bukan bahasa yang diketik secara dinamis. Bahasa statis yang layak dapat mensimulasikan jenis seperti itu dengan sempurna. (Dan bahkan bahasa "buruk" sering dapat mensimulasikan mereka dengan memecah keselamatan tipe di bawah tenda dan / atau membutuhkan sintaks akses yang canggung.)

Keuntungan dari bahasa yang diketik secara dinamis adalah bahwa tipe seperti itu tidak dapat disimpulkan oleh sistem inferensi tipe statis. Anda harus menulis jenisnya secara eksplisit. Tetapi dalam banyak kasus seperti itu — termasuk sekali ini — kode untuk menggambarkan tipe persis sama rumitnya dengan kode untuk mem-parsing / membangun objek tanpa mendeskripsikan jenisnya, sehingga masih belum tentu merupakan keuntungan.

abarnert
sumber
21
Contoh penguraian JSON Anda dapat dengan mudah ditangani secara statis oleh Tipe Data Aljabar.
2
OK, jawaban saya tidak cukup jelas; Terima kasih. JSValue itu adalah definisi eksplisit dari tipe dinamis, persis seperti apa yang saya bicarakan. Jenis dinamis itulah yang bermanfaat, bukan bahasa yang memerlukan pengetikan dinamis. Namun, masih relevan bahwa tipe dinamis tidak dapat secara otomatis dihasilkan oleh sistem inferensi tipe nyata apa pun, sementara sebagian besar contoh umum orang dapat diremehkan secara sepele. Saya harap versi baru menjelaskannya dengan lebih baik.
abarnert
4
@MattFenwick Jenis Data Aljabar cukup banyak terbatas pada bahasa fungsional (dalam praktiknya). Bagaimana dengan bahasa seperti Java dan c #?
spirc
4
ADT ada di C / C ++ sebagai serikat yang ditandai. Ini tidak unik untuk bahasa fungsional.
Clark Gaebel
2
@spirc Anda dapat meniru ADT dalam bahasa OO klasik menggunakan beberapa kelas yang semuanya berasal dari antarmuka umum, panggilan run-time ke getClass () atau GetType (), dan pemeriksaan kesetaraan. Atau Anda dapat menggunakan pengiriman ganda, tapi saya pikir itu terbayar lebih banyak di C ++. Jadi Anda mungkin memiliki antarmuka JSObject, dan kelas JSString, JSNumber, JSHash, dan JSArray. Anda kemudian akan memerlukan beberapa kode untuk mengubah struktur data "tidak diketik" ini menjadi struktur data "diketik aplikasi". Tapi Anda mungkin ingin melakukan ini dalam bahasa yang diketik secara dinamis juga.
Daniel Yankowsky
12

Karena setiap jenis sistem statis praktis jarak jauh sangat terbatas dibandingkan dengan bahasa pemrograman yang bersangkutan, ia tidak dapat mengekspresikan semua invarian yang dapat diperiksa kode saat runtime. Agar tidak mengelak dari jaminan yang mencoba diberikan oleh sistem tipe, maka sistem itu memilih untuk bersikap konservatif dan melarang kasus penggunaan yang lulus pemeriksaan ini, tetapi tidak dapat (dalam sistem tipe) terbukti.

Saya akan membuat contoh. Misalkan Anda menerapkan model data sederhana untuk menggambarkan objek data, koleksi mereka, dll. Yang diketik secara statis dalam arti bahwa, jika model mengatakan atribut xobjek tipe Foo memegang integer, itu harus selalu memegang integer. Karena ini adalah konstruksi runtime, Anda tidak dapat mengetiknya secara statis. Misalkan Anda menyimpan data yang dijelaskan dalam file YAML. Anda membuat peta hash (untuk diserahkan ke perpustakaan YAML nanti), dapatkan xatribut, simpan di peta, dapatkan atribut lainnya yang kebetulan berupa string, ... tahan sebentar? Apa jenis the_map[some_key]sekarang? Nah menembak, kita tahu bahwa some_keyadalah 'x'dan hasil tersebut maka harus integer, tapi sistem jenis bahkan tidak bisa mulai untuk alasan tentang ini.

Beberapa jenis sistem yang diteliti secara aktif dapat bekerja untuk contoh spesifik ini, tetapi ini sangat rumit (baik untuk penulis kompiler untuk mengimplementasikan dan untuk programmer untuk alasan), terutama untuk sesuatu yang "sederhana" ini (maksud saya, saya hanya menjelaskannya dalam satu ayat).

Tentu saja, solusi hari ini adalah tinju segalanya dan kemudian casting (atau memiliki banyak metode override, yang sebagian besar menimbulkan pengecualian "tidak diterapkan"). Tapi ini tidak diketik secara statis, ini adalah hack di sekitar sistem tipe untuk melakukan pemeriksaan tipe saat runtime.

user7043
sumber
Jenis generik tidak memiliki persyaratan tinju.
Robert Harvey
@RobertHarvey Ya. Saya tidak berbicara dengan para tinju di Jawa C #, saya sedang berbicara tentang "membungkusnya dalam beberapa kelas wrapper yang tujuan utamanya adalah mewakili nilai T di subtipe dari U". Polimorfisme parametrik (apa yang Anda sebut pengetikan generik) tidak berlaku untuk contoh saya. Ini adalah abstraksi waktu kompilasi terhadap tipe beton, tetapi kita membutuhkan mekanisme pengetikan runtime.
Mungkin layak untuk menunjukkan bahwa sistem jenis Scala adalah Turing lengkap. Jadi sistem tipe bisa lebih sepele daripada yang Anda bayangkan.
Andrea
@Andrea saya sengaja tidak mengurangi uraian saya menjadi turing kelengkapan. Pernah diprogram dalam tarpit turing? Atau mencoba untuk menyandikan hal-hal ini dalam tipe? Pada titik tertentu, menjadi terlalu rumit untuk dapat dilakukan.
@nannan saya setuju. Saya baru saja menunjukkan bahwa sistem tipe dapat melakukan hal-hal yang cukup rumit. Saya mendapat kesan bahwa jawaban Anda berarti bahwa sistem tipe hanya dapat melakukan verifikasi sepele, tetapi pada saat membaca kedua Anda tidak menulis hal seperti ini!
Andrea
7

Tidak ada yang dapat Anda lakukan dengan pengetikan dinamis yang tidak dapat Anda lakukan dengan pengetikan statis, karena Anda dapat menerapkan pengetikan dinamis di atas bahasa yang diketik secara statis.

Contoh singkat di Haskell:

data Data = DString String | DInt Int | DDouble Double

-- defining a '+' operator here, with explicit promotion behavior
DString a + DString b = DString (a ++ b)
DString a + DInt b = DString (a ++ show b)
DString a + DDouble b = DString (a ++ show b)
DInt a + DString b = DString (show a ++ b)
DInt a + DInt b = DInt (a + b)
DInt a + DDouble b = DDouble (fromIntegral a + b)
DDouble a + DString b = DString (show a ++ b)
DDouble a + DInt b = DDouble (a + fromIntegral b)
DDouble a + DDouble b = DDouble (a + b)

Dengan cukup case Anda dapat menerapkan sistem tipe dinamis apa pun yang diberikan.

Sebaliknya, Anda juga dapat menerjemahkan program yang diketik secara statis menjadi program dinamis yang setara. Tentu saja, Anda akan kehilangan semua jaminan waktu kompilasi kebenaran yang disediakan oleh bahasa yang diketik secara statis.

Sunting: Saya ingin menjaga ini tetap sederhana, tetapi di sini ada lebih banyak detail tentang model objek

Fungsi mengambil daftar data sebagai argumen dan melakukan perhitungan dengan efek samping di ImplMonad, dan mengembalikan data.

type Function = [Data] -> ImplMonad Data

DMember adalah nilai anggota atau fungsi.

data DMember = DMemValue Data | DMemFunction Function

Perluas Datauntuk menyertakan Objek dan Fungsi. Objek adalah daftar anggota yang disebutkan.

data Data = .... | DObject [(String, DMember)] | DFunction Function

Tipe statis ini cukup untuk mengimplementasikan setiap sistem objek yang diketik secara dinamis yang saya kenal.

NovaDenizen
sumber
Ini tidak sama sekali karena Anda tidak dapat menambahkan tipe baru tanpa meninjau kembali definisi Data.
Jed
5
Anda mencampurkan konsep pengetikan dinamis dengan pengetikan lemah dalam contoh Anda. Pengetikan dinamis adalah tentang pengoperasian pada jenis yang tidak dikenal, tidak mendefinisikan daftar jenis yang diizinkan dan operasi kelebihan beban di antara mereka.
hcalves
2
@ Jed Setelah Anda menerapkan model objek, tipe dasar, dan operasi primitif, tidak ada dasar lain yang diperlukan. Anda dapat dengan mudah dan otomatis menerjemahkan program dalam bahasa dinamis asli ke dalam dialek ini.
NovaDenizen
2
@ Hcalves Karena Anda mengacu pada kelebihan dalam kode Haskell saya, saya kira Anda tidak memiliki ide yang tepat tentang itu semantik. Di sana saya telah mendefinisikan +operator baru yang menggabungkan dua Datanilai menjadi nilai lain Data. Datamewakili nilai standar dalam sistem tipe dinamis.
NovaDenizen
1
@ Jed: Sebagian besar bahasa dinamis memiliki serangkaian kecil tipe "primitif" dan beberapa cara induktif untuk memperkenalkan nilai baru (struktur data seperti daftar). Skema, misalnya, berjalan cukup jauh dengan sedikit lebih dari atom, pasangan, dan vektor. Anda harus dapat menerapkan ini dengan cara yang sama seperti sisa dari jenis dinamis yang diberikan.
Tikhon Jelvis
3

Membran :

Membran adalah pembungkus di sekitar seluruh grafik objek, sebagai lawan pembungkus hanya untuk satu objek. Biasanya, pencipta membran mulai membungkus hanya satu objek dalam membran. Ide kuncinya adalah bahwa setiap referensi objek yang melintasi membran itu sendiri dibungkus secara transitif dalam membran yang sama.

masukkan deskripsi gambar di sini

Setiap jenis dibungkus oleh jenis yang memiliki antarmuka yang sama, tetapi yang memotong pesan dan membungkus dan membuka nilai saat mereka melintasi membran. Apa jenis fungsi bungkus dalam bahasa yang diketik secara statis favorit Anda? Mungkin Haskell memiliki tipe untuk fungsi-fungsi itu, tetapi sebagian besar bahasa yang diketik secara statis tidak atau mereka akhirnya menggunakan Object → Object, secara efektif melepaskan tanggung jawab mereka sebagai pemeriksa tipe.

Mike Samuel
sumber
4
Ya, Haskell memang bisa menggunakan tipe eksistensial ini. Jika Anda memiliki beberapa jenis kelas Foo, Anda dapat membuat pembungkus di sekitar jenis apa pun yang membuat antarmuka itu. class Foo a where ... data Wrapper = forall a. Foo a => Wrapper a
Jake McArthur
@ JakeMcArthur, Terima kasih telah menjelaskan. Itu alasan lain bagi saya untuk duduk dan belajar Haskell.
Mike Samuel
2
Membran Anda adalah 'antarmuka' dan jenis objek "diketik secara eksistensial" - yaitu, kita tahu mereka ada di bawah antarmuka, tetapi hanya itu yang kita ketahui. Jenis eksistensial untuk abstraksi data telah dikenal sejak tahun 80-an. Ref yang bagus adalah cs.cmu.edu/~rwh/plbook/book.pdf bab 21.1
Don Stewart
@DonStewart. Apakah kelas proxy Java merupakan mekanisme tipe eksistensial? Satu tempat di mana membran menjadi sulit adalah dalam bahasa dengan sistem tipe nominal yang memiliki nama untuk tipe beton yang terlihat di luar definisi tipe itu. Sebagai contoh, seseorang tidak dapat membungkus Stringkarena ini adalah jenis beton di Jawa. Smalltalk tidak memiliki masalah ini karena tidak mencoba mengetik #doesNotUnderstand.
Mike Samuel
1

Seperti yang disebutkan seseorang, secara teori tidak banyak yang dapat Anda lakukan dengan pengetikan dinamis yang tidak dapat Anda lakukan dengan pengetikan statis jika Anda ingin menerapkan mekanisme tertentu sendiri. Sebagian besar bahasa menyediakan mekanisme relaksasi jenis untuk mendukung fleksibilitas jenis seperti pointer kosong, dan root Object object atau antarmuka kosong.

Pertanyaan yang lebih baik adalah mengapa pengetikan dinamis lebih cocok dan lebih tepat dalam situasi dan masalah tertentu.

Pertama, mari kita definisikan

Entitas - Saya akan memerlukan gagasan umum tentang beberapa entitas dalam kode. Itu bisa berupa apa saja dari bilangan primitif hingga data kompleks.

Perilaku - katakanlah entitas kita memiliki beberapa keadaan dan serangkaian metode yang memungkinkan dunia luar untuk menginstruksikan entitas untuk reaksi tertentu. Mari kita memanggil antarmuka negara + dari entitas ini perilakunya. Satu entitas dapat memiliki lebih dari satu perilaku yang digabungkan dengan cara tertentu oleh alat yang disediakan oleh bahasa.

Definisi entitas dan perilakunya - setiap bahasa menyediakan beberapa cara abstraksi yang membantu Anda mendefinisikan perilaku (serangkaian metode + keadaan internal) entitas tertentu dalam program. Anda dapat menetapkan nama untuk perilaku ini dan mengatakan bahwa semua instance yang memiliki perilaku ini adalah tipe tertentu .

Ini mungkin sesuatu yang tidak biasa. Dan seperti yang Anda katakan, Anda memahami perbedaannya, tetapi tetap saja. Mungkin penjelasan yang tidak lengkap dan paling akurat tapi saya harap cukup menyenangkan untuk membawa nilai :)

Pengetikan statis - perilaku semua entitas dalam program Anda diperiksa dalam waktu kompilasi, sebelum kode mulai dijalankan. Ini berarti bahwa jika Anda ingin misalnya entitas tipe Person Anda memiliki perilaku (berperilaku seperti) Magician maka Anda harus mendefinisikan entitas MagicianPerson dan memberikannya perilaku seorang magician seperti throwMagic (). Jika Anda berada dalam kode Anda, salah kirim ke kompiler Person.throwMagic () biasa akan memberi tahu Anda"Error >>> hell, this Person has no this behavior, dunno throwing magics, no run!".

Pengetikan dinamis - di lingkungan pengetikan dinamis, perilaku entitas yang tersedia tidak dicentang hingga Anda benar-benar mencoba melakukan sesuatu dengan entitas tertentu. Menjalankan kode Ruby yang meminta Person.throwMagic () tidak akan ditangkap sampai kode Anda benar-benar datang ke sana. Ini terdengar frustasi, bukan. Tapi itu kedengarannya menyenangkan juga. Berdasarkan pada properti ini Anda dapat melakukan hal-hal menarik. Seperti, katakanlah Anda merancang game di mana segala sesuatu dapat berubah menjadi Magician dan Anda tidak benar-benar tahu siapa itu, sampai Anda tiba pada titik tertentu dalam kode. Dan kemudian Frog datang dan kamu berkataHeyYouConcreteInstanceOfFrog.include Magicdan sejak saat itu Katak ini menjadi satu Katak tertentu yang memiliki kekuatan Sihir. Katak lain, masih belum. Anda lihat, dalam bahasa pengetikan statis, Anda harus mendefinisikan hubungan ini dengan rata-rata standar kombinasi perilaku (seperti implementasi antarmuka). Dalam bahasa pengetikan dinamis, Anda bisa melakukannya di runtime dan tidak ada yang peduli.

Sebagian besar bahasa pengetikan dinamis memiliki mekanisme untuk menyediakan perilaku umum yang akan menangkap pesan apa pun yang diteruskan ke antarmuka mereka. Misalnya Ruby method_missingdan PHP __callkalau saya ingat bagus. Itu berarti bahwa Anda dapat melakukan segala hal menarik dalam menjalankan waktu program dan membuat keputusan tipe berdasarkan status program saat ini. Ini membawa alat untuk pemodelan masalah yang jauh lebih fleksibel daripada di, katakanlah, bahasa pemrograman statis konservatif seperti Java.

ivanjovanovic
sumber