Prinsip dasar pemrograman: pengetikan statis dan dinamis. Pengetikan bahasa pemrograman Jenis pengetikan

Artikel ini berisi hal-hal minimal yang perlu Anda ketahui tentang mengetik sehingga Anda tidak menyebut pengetikan dinamis sebagai kejahatan, Lisp sebagai bahasa yang tidak diketik, dan C sebagai bahasa yang diketik dengan kuat.

DI DALAM versi lengkap ada penjelasan rinci tentang semua jenis pengetikan, dibumbui dengan contoh kode, tautan ke bahasa pemrograman populer dan gambar demonstratif.

Saya sarankan Anda membaca versi pendek artikel terlebih dahulu, dan kemudian, jika Anda mau, versi lengkapnya.

Versi pendek

Bahasa pemrograman mengetik biasanya dibagi menjadi dua kubu besar - diketik dan tidak diketik (tidak diketik). Yang pertama termasuk C, Python, Scala, PHP, dan Lua, sedangkan yang terakhir termasuk bahasa assembly, Forth, dan Brainfuck.

Karena "pengetikan yang tidak diketik" pada dasarnya sesederhana gabus, itu tidak dibagi lagi menjadi jenis lainnya. Tetapi bahasa yang diketik dibagi menjadi beberapa kategori yang tumpang tindih:

  • Pengetikan statis / dinamis. Statis ditentukan oleh fakta bahwa tipe akhir dari variabel dan fungsi ditetapkan pada waktu kompilasi. Itu. kompiler sudah 100% yakin tipe yang mana. Dalam pengetikan dinamis, semua jenis ditentukan saat runtime.

    Contoh:
    Statis: C, Java, C#;
    Dinamis: Python, JavaScript, Ruby.

  • Pengetikan kuat / lemah (terkadang juga disebut kuat / longgar). Pengetikan yang kuat dibedakan oleh fakta bahwa bahasa tidak memungkinkan pencampuran tipe yang berbeda dalam ekspresi dan tidak melakukan konversi implisit otomatis, misalnya, Anda tidak dapat mengurangi satu set dari string. Bahasa yang diketik dengan lemah melakukan banyak konversi implisit secara otomatis, meskipun kehilangan presisi atau konversi dapat menjadi ambigu.

    Contoh:
    Kuat: Java, Python, Haskell, Lisp;
    Lemah: C, JavaScript, Dasar visual PHP.

  • Pengetikan eksplisit / implisit. Bahasa yang diketik secara eksplisit berbeda karena jenis variabel / fungsi baru / argumennya harus ditetapkan secara eksplisit. Karenanya, bahasa dengan pengetikan implisit mengalihkan tugas ini ke kompiler / juru bahasa.

    Contoh:
    Eksplisit: C++, D, C#
    Tersirat: PHP, Lua, JavaScript

Perlu juga dicatat bahwa semua kategori ini berpotongan, misalnya, bahasa C memiliki pengetikan statis lemah eksplisit, dan bahasa Python memiliki pengetikan implisit kuat dinamis.

Namun demikian, tidak ada bahasa dengan pengetikan statis dan dinamis pada saat yang bersamaan. Meskipun melihat ke depan, saya akan mengatakan bahwa saya berbaring di sini - mereka benar-benar ada, tetapi lebih dari itu nanti.

versi rinci

Jika versi singkatnya tidak cukup untuk Anda, baiklah. Tidak heran saya menulis rinci? Hal utama adalah bahwa dalam versi pendek tidak mungkin untuk memasukkan semua informasi yang berguna dan menarik, dan yang mendetail mungkin akan terlalu panjang untuk dibaca semua orang tanpa melelahkan.

Pengetikan yang tidak diketik

Dalam bahasa pemrograman yang tidak bertipe, semua entitas dianggap sebagai rangkaian bit dengan panjang yang bervariasi.

Pengetikan yang tidak diketik biasanya melekat pada bahasa tingkat rendah (bahasa assembler, Forth) dan esoterik (Brainfuck, HQ9, Piet). Namun, selain kekurangannya, ia juga memiliki beberapa kelebihan.

Keuntungan
  • Memungkinkan Anda menulis pada level yang sangat rendah, dan kompiler / juru bahasa tidak akan mengganggu pemeriksaan jenis apa pun. Anda bebas melakukan operasi apa pun pada jenis data apa pun.
  • Kode yang dihasilkan biasanya lebih efisien.
  • Transparansi instruksi. Dengan pengetahuan bahasa, biasanya tidak diragukan lagi apa kode ini atau itu.
Kekurangan
  • Kompleksitas. Seringkali ada kebutuhan untuk merepresentasikan nilai kompleks seperti daftar, string, atau struktur. Ini dapat menyebabkan ketidaknyamanan.
  • Tidak ada pemeriksaan. Tindakan tidak masuk akal apa pun, seperti mengurangi pointer ke array dari karakter, akan dianggap normal, yang penuh dengan kesalahan halus.
  • Tingkat abstraksi rendah. Bekerja dengan tipe data kompleks apapun tidak berbeda dengan bekerja dengan angka, yang tentunya akan menimbulkan banyak kesulitan.
Pengetikan tanpa tipe yang kuat?

Ya, ini ada. Misalnya, dalam bahasa rakitan (untuk arsitektur x86 / x86-64, saya tidak tahu yang lain) Anda tidak dapat merakit program jika Anda mencoba memuat data dari register rax (64 bit) ke dalam register cx (16 bit).

mov cx, eax ; kesalahan waktu perakitan

Jadi ternyata assembler masih mengetik? Saya pikir pemeriksaan ini tidak cukup. Dan pendapat Anda, tentu saja, hanya bergantung pada Anda.

Pengetikan statis dan dinamis

Hal utama yang membedakan pengetikan statis (statis) dari dinamis (dinamis) adalah bahwa semua pemeriksaan jenis dilakukan pada waktu kompilasi, dan bukan pada waktu proses.

Beberapa orang mungkin berpikir bahwa pengetikan statis terlalu membatasi (sebenarnya memang demikian, tetapi telah lama dihilangkan dengan bantuan beberapa teknik). Bagi sebagian orang, bahasa yang diketik secara dinamis sedang bermain api, tetapi fitur apa yang membuatnya menonjol? Apakah kedua spesies memiliki kesempatan untuk eksis? Jika tidak, mengapa ada begitu banyak bahasa yang diketik secara statis dan dinamis?

Mari kita cari tahu.

Manfaat pengetikan statis
  • Pengecekan tipe hanya terjadi sekali, pada waktu kompilasi. Dan ini berarti bahwa kita tidak perlu terus-menerus mencari tahu apakah kita mencoba membagi angka dengan string (dan membuat kesalahan atau melakukan konversi).
  • Kecepatan eksekusi. Jelas dari poin sebelumnya bahwa bahasa yang diketik secara statis hampir selalu lebih cepat daripada yang diketik secara dinamis.
  • Dalam beberapa kondisi tambahan, ini memungkinkan Anda untuk mendeteksi potensi kesalahan yang sudah ada pada tahap kompilasi.
Manfaat pengetikan dinamis
  • Kemudahan membuat koleksi universal - tumpukan segalanya dan segalanya (jarang kebutuhan seperti itu muncul, tetapi ketika muncul, pengetikan dinamis akan membantu).
  • Kenyamanan dalam mendeskripsikan algoritme umum (misalnya, penyortiran larik, yang akan bekerja tidak hanya pada daftar bilangan bulat, tetapi juga pada daftar bilangan real dan bahkan pada daftar string).
  • Mudah dipelajari - Bahasa yang diketik secara dinamis biasanya sangat bagus untuk memulai pemrograman.

Pemrograman umum

Nah, argumen terpenting untuk pengetikan dinamis adalah kemudahan mendeskripsikan algoritme generik. Bayangkan sebuah masalah - kita memerlukan fungsi pencarian untuk beberapa larik (atau daftar) - larik bilangan bulat, larik real, dan larik karakter.

Bagaimana kita akan menyelesaikannya? Mari kita selesaikan dalam 3 bahasa berbeda: satu dengan pengetikan dinamis dan dua dengan pengetikan statis.

Saya akan mengambil salah satu algoritma pencarian paling sederhana - pencacahan. Fungsi akan menerima elemen yang dicari, array (atau daftar) itu sendiri dan mengembalikan indeks elemen, atau jika elemen tidak ditemukan - (-1).

Solusi dinamis (Python):

def find(required_element, list): for (index, element) in enumerate(list): if element == required_element: return index return (-1)

Seperti yang Anda lihat, semuanya sederhana dan tidak ada masalah dengan fakta bahwa daftar dapat berisi angka genap, daftar genap, meskipun tidak ada array lain. Sangat bagus. Mari melangkah lebih jauh - selesaikan masalah yang sama di C!

Solusi statis (C):

unsigned int find_int(int required_element, int array, unsigned int size) ( for (unsigned int i = 0; i< size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_float(float required_element, float array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_char(char required_element, char array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); }

Nah, setiap fungsi secara individual mirip dengan versi Python, tetapi mengapa ada tiga? Apakah pemrograman statis hilang?

Iya dan tidak. Ada beberapa teknik pemrograman, salah satunya sekarang akan kita pertimbangkan. Ini disebut Pemrograman Generik, dan bahasa C++ mendukungnya dengan cukup baik. Mari kita lihat versi barunya:

Solusi statis (pemrograman umum, C++):

Templat unsigned int find(T required_element, std::vector array) ( untuk (unsigned int i = 0; i< array.size(); ++i) if (required_element == array[i]) return i; return (-1); }

Bagus! Itu tidak terlihat lebih rumit daripada versi Python, dan tidak perlu banyak menulis. Selain itu, kami mendapatkan implementasi untuk semua array, bukan hanya 3 yang diperlukan untuk menyelesaikan masalah!

Versi ini tampaknya persis seperti yang kami butuhkan - kami mendapatkan keuntungan dari pengetikan statis dan beberapa keuntungan dari pengetikan dinamis.

Sangat bagus bahkan mungkin, tetapi bisa lebih baik lagi. Pertama, pemrograman generik bisa lebih nyaman dan indah (misalnya, di Haskell). Kedua, selain pemrograman generik, Anda juga bisa menggunakan polimorfisme (hasilnya akan lebih buruk), kelebihan fungsi (serupa) atau makro.

Statis dalam dinamika

Juga harus disebutkan bahwa banyak bahasa statis memungkinkan pengetikan dinamis, misalnya:

  • C # mendukung tipe semu dinamis.
  • F# mendukung gula sintaksis dalam bentuk operator ?, yang dapat digunakan untuk meniru pengetikan dinamis.
  • Haskell - pengetikan dinamis disediakan oleh modul Data.Dynamic.
  • Delphi - melalui varian tipe khusus.

Selain itu, beberapa bahasa yang diketik secara dinamis memungkinkan Anda memanfaatkan pengetikan statis:

  • Common Lisp - deklarasi tipe.
  • Perl - sejak versi 5.6, agak terbatas.

Mengetik kuat dan lemah

Bahasa yang diketik dengan kuat tidak mengizinkan pencampuran entitas dari jenis yang berbeda dalam ekspresi dan tidak melakukan konversi otomatis apa pun. Mereka juga disebut "bahasa yang diketik dengan kuat". Istilah bahasa Inggris untuk ini adalah pengetikan yang kuat.

Sebaliknya, bahasa yang diketik dengan lemah mendorong pemrogram untuk mencampur berbagai jenis dalam satu ekspresi, dan kompiler itu sendiri akan mengonversi semuanya menjadi satu jenis. Mereka juga disebut "bahasa yang diketik dengan lemah". Istilah bahasa Inggris untuk ini adalah pengetikan yang lemah.

Pengetikan yang lemah sering dikacaukan dengan pengetikan dinamis, yang sama sekali salah. Bahasa yang diketik secara dinamis dapat diketik dengan lemah dan kuat.

Namun, hanya sedikit orang yang mementingkan ketatnya pengetikan. Sering diklaim bahwa jika suatu bahasa diketik secara statis, Anda dapat menemukan banyak potensi kesalahan kompilasi. Mereka berbohong padamu!

Bahasanya juga harus memiliki pengetikan yang kuat. Memang, jika kompiler alih-alih melaporkan kesalahan hanya menambahkan string ke nomor, atau, lebih buruk lagi, mengurangi yang lain dari satu array, apa untungnya bagi kita bahwa semua "pemeriksaan" tipe akan berada pada tahap kompilasi? Itu benar - pengetikan statis yang lemah bahkan lebih buruk daripada pengetikan dinamis yang kuat! (Yah, itu pendapat saya)

Jadi mengapa pengetikan yang lemah tidak memiliki keuntungan sama sekali? Kelihatannya seperti itu, tetapi terlepas dari kenyataan bahwa saya adalah pendukung kuat pengetikan yang kuat, saya harus setuju bahwa pengetikan yang lemah juga memiliki kelebihan.

Apakah Anda ingin tahu yang mana?

Manfaat pengetikan yang kuat
  • Keandalan - Anda akan mendapatkan pengecualian atau kesalahan kompilasi alih-alih melakukan kesalahan.
  • Kecepatan - alih-alih konversi implisit, yang bisa sangat mahal, dengan pengetikan yang kuat, Anda harus menulisnya secara eksplisit, yang membuat programmer setidaknya sadar bahwa potongan kode ini bisa lambat.
  • Memahami cara kerja program - sekali lagi, alih-alih casting tipe implisit, programmer menulis semuanya sendiri, yang berarti dia kira-kira memahami bahwa membandingkan string dan angka tidak terjadi dengan sendirinya dan bukan dengan sihir.
  • Kepastian - saat Anda menulis transformasi dengan tangan, Anda tahu persis apa yang Anda ubah dan menjadi apa. Selain itu, Anda akan selalu memahami bahwa konversi semacam itu dapat menyebabkan hilangnya presisi dan hasil yang salah.
Manfaat Mengetik Lemah
  • Kenyamanan menggunakan ekspresi campuran (misalnya, dari bilangan bulat dan bilangan real).
  • Mengabstraksi dari mengetik dan fokus pada tugas.
  • Singkatnya catatan.

Oke, sudah ketahuan, ternyata pengetikan yang lemah juga punya kelebihan! Apakah ada cara untuk mentransfer keuntungan dari pengetikan yang lemah ke pengetikan yang kuat?

Ternyata malah ada dua.

Pengecoran tipe implisit, dalam situasi yang tidak ambigu dan tanpa kehilangan data

Wow… Paragraf yang cukup panjang. Biarkan saya lebih jauh menyingkatnya menjadi "konversi implisit terbatas" Jadi apa arti dari situasi yang tidak ambigu dan kehilangan data?

Situasi yang tidak ambigu adalah transformasi atau operasi di mana esensinya segera menjadi jelas. Misalnya, penambahan dua angka adalah situasi yang tidak ambigu. Tetapi mengubah angka menjadi larik bukanlah (mungkin larik dari satu elemen akan dibuat, mungkin larik dengan panjang seperti itu, diisi dengan elemen secara default, dan mungkin angka akan diubah menjadi string, dan kemudian menjadi larik karakter).

Kehilangan data bahkan lebih mudah. Jika kita mengubah bilangan real 3,5 menjadi bilangan bulat, kita akan kehilangan sebagian data (pada kenyataannya, operasi ini juga ambigu - bagaimana pembulatan dilakukan? Ke atas? Ke bawah? Menjatuhkan bagian pecahan?).

Konversi dalam situasi ambigu dan konversi dengan kehilangan data sangat, sangat buruk. Tidak ada yang lebih buruk dari ini dalam pemrograman.

Jika Anda tidak mempercayai saya, pelajari bahasa PL/I, atau lihat saja spesifikasinya. Ini memiliki aturan konversi antara SEMUA tipe data! Itu hanya neraka!

Oke, mari kita ingat tentang konversi implisit terbatas. Apakah ada bahasa seperti itu? Ya, misalnya di Pascal Anda dapat mengubah bilangan bulat menjadi bilangan real, tetapi tidak sebaliknya. Ada juga mekanisme serupa di C#, Groovy dan Common Lisp.

Oke, saya katakan bahwa ada cara lain untuk mendapatkan beberapa keuntungan dari pengetikan yang lemah dalam bahasa yang kuat. Dan ya, itu ada dan disebut polimorfisme konstruktor.

Saya akan menjelaskannya menggunakan bahasa indah Haskell sebagai contoh.

Konstruktor polimorfik muncul sebagai hasil pengamatan bahwa konversi implisit yang aman paling sering dibutuhkan saat menggunakan literal numerik.

Misalnya, dalam ekspresi pi + 1 , Anda tidak ingin menulis pi + 1.0 atau pi + float(1) . Saya ingin menulis hanya pi + 1!

Dan ini dilakukan di Haskell, karena literal 1 tidak memiliki tipe konkret. Itu tidak utuh, tidak nyata, atau kompleks. Itu hanya angka!

Akibatnya, saat menulis fungsi sederhana sum x y , yang mengalikan semua angka dari x ke y (dengan kenaikan 1), kami mendapatkan beberapa versi sekaligus - jumlah untuk bilangan bulat, jumlah untuk real, jumlah untuk rasional, jumlah untuk kompleks angka, dan bahkan jumlah untuk semua tipe numerik yang telah Anda tentukan sendiri.

Tentu saja, teknik ini hanya menghemat penggunaan ekspresi campuran dengan literal numerik, dan ini hanyalah puncak gunung es.

Dengan demikian, kita dapat mengatakan bahwa jalan keluar terbaik adalah menyeimbangkan di ambang, antara pengetikan yang kuat dan yang lemah. Namun sejauh ini, tidak ada bahasa yang memiliki keseimbangan sempurna, jadi saya lebih condong ke bahasa yang diketik dengan kuat (seperti Haskell, Java, C#, Python) daripada bahasa yang diketik dengan lemah (seperti C, JavaScript, Lua, PHP) .

Pengetikan eksplisit dan implisit

Bahasa yang diketik secara eksplisit mengharuskan pemrogram untuk menentukan jenis semua variabel dan fungsi yang dideklarasikannya. Istilah bahasa Inggris untuk ini adalah pengetikan eksplisit.

Sebaliknya, bahasa yang diketik secara implisit mengundang Anda untuk melupakan tipe dan menyerahkan tugas inferensi tipe ke kompiler atau juru bahasa. Istilah bahasa Inggris untuk ini adalah pengetikan implisit.

Pada awalnya, orang mungkin berpikir bahwa pengetikan implisit setara dengan pengetikan dinamis, dan pengetikan eksplisit setara dengan pengetikan statis, tetapi kita akan melihat nanti bahwa bukan itu masalahnya.

Apakah ada kelebihan untuk masing-masing jenis, dan sekali lagi, apakah ada kombinasinya dan apakah ada bahasa yang mendukung kedua metode tersebut?

Manfaat pengetikan eksplisit
  • Memiliki tanda tangan untuk setiap fungsi (misalnya, int add(int, int)) memudahkan untuk menentukan fungsi apa yang dilakukan.
  • Pemrogram segera menuliskan jenis nilai apa yang dapat disimpan dalam variabel tertentu, yang menghilangkan kebutuhan untuk mengingatnya.
Manfaat pengetikan implisit
  • Singkatan - def add(x, y) jelas lebih pendek dari int add(int x, int y) .
  • Ketahanan untuk berubah. Misalnya, jika dalam suatu fungsi variabel sementara memiliki tipe yang sama dengan argumen input, maka dalam bahasa yang diketik secara eksplisit, ketika tipe argumen input berubah, tipe variabel sementara juga perlu diubah.

Nah, sepertinya kedua pendekatan tersebut memiliki pro dan kontra (dan siapa yang mengharapkan hal lain?), jadi mari kita cari cara untuk menggabungkan kedua pendekatan ini!

Pengetikan eksplisit berdasarkan pilihan

Ada bahasa dengan pengetikan implisit secara default dan kemampuan untuk menentukan jenis nilai jika perlu. Kompiler akan menyimpulkan tipe sebenarnya dari ekspresi secara otomatis. Salah satu bahasa tersebut adalah Haskell, izinkan saya memberi Anda contoh sederhana untuk diilustrasikan:

Tidak ada tipe eksplisit add (x, y) = x + y -- Tipe eksplisit add :: (Integer, Integer) -> Integer add (x, y) = x + y

Catatan: Saya sengaja menggunakan fungsi uncurried, dan juga sengaja menulis tanda tangan pribadi alih-alih yang lebih umum add:: (Num a) -> a -> a -> a , karena Saya ingin menunjukkan idenya, tanpa menjelaskan sintaks Haskell.

Hm. Seperti yang bisa kita lihat, ini sangat bagus dan pendek. Entri fungsi hanya membutuhkan 18 karakter per baris, termasuk spasi!

Namun, inferensi tipe otomatis cukup rumit, dan bahkan dalam bahasa keren seperti Haskell, terkadang gagal. (contohnya adalah kendala monomorfisme)

Apakah ada bahasa dengan pengetikan eksplisit secara default dan pengetikan implisit karena kebutuhan? Kon
untuk ya.

Pengetikan implisit berdasarkan pilihan

Standar bahasa C++ baru, disebut C++11 (sebelumnya disebut C++0x), diperkenalkan kata kunci auto , berkat itu Anda dapat memaksa kompiler untuk menyimpulkan jenis berdasarkan konteksnya:

Mari kita bandingkan: // Spesifikasi tipe manual unsigned int a = 5; unsigned int b = a + 3; // Inferensi tipe otomatis unsigned int a = 5; otomatis b = a + 3;

Tidak buruk. Tapi rekornya tidak menyusut banyak. Mari kita lihat contoh dengan iterator (jika Anda tidak mengerti, jangan takut, hal utama yang perlu diperhatikan adalah catatannya sangat berkurang karena keluaran otomatis):

// Ketik manual std::vektor vec = vektor acak(30); for (std::vector::const_iterator it = vec.cbegin(); ...) ( ... ) // Inferensi Tipe Otomatis auto vec = randomVector (tigapuluh); untuk (otomatis itu = vec.cbegin(); ...) ( ... )

Wow! Ini singkatannya. Oke, tetapi mungkinkah melakukan sesuatu dengan semangat Haskell, di mana tipe pengembalian akan bergantung pada tipe argumen?

Sekali lagi, jawabannya adalah ya, berkat kata kunci decltype yang dikombinasikan dengan auto:

// Ketik manual int bagi(int x, int y) ( ... ) // Ketik otomatis pengurangan bagi otomatis(int x, int y) -> decltype(x / y) ( ... )

Bentuk notasi ini mungkin kedengarannya tidak bagus, tetapi bila digabungkan dengan generik (templat / generik), pengetikan implisit atau inferensi tipe otomatis bekerja dengan sangat baik.

Beberapa bahasa pemrograman menurut klasifikasi ini

Saya akan memberikan daftar singkat bahasa populer dan menulis bagaimana mereka dikategorikan di bawah setiap kategori "mengetik".

JavaScript - Ruby Dinamis / Lemah / Implisit - Python Dinamis / Kuat / Implisit - Java Dinamis / Kuat / Implisit - PHP Statis / Kuat / Eksplisit - C Dinamis / Lemah / Implisit - C++ Statis / Lemah / Eksplisit - Statis / Semi-kuat / Perl Eksplisit - Dinamis / Lemah / Implisit Objective-C - C# Statis / Lemah / Eksplisit - Statis / Kuat / Eksplisit Haskell - Statis / Kuat / Implisit Lisp Umum - Dinamis / Kuat / Implisit

Mungkin saya membuat kesalahan di suatu tempat, terutama dengan CL, PHP dan Obj-C, jika Anda memiliki pendapat berbeda tentang beberapa bahasa, tulis di komentar.

Kesederhanaan pengetikan dalam pendekatan OO merupakan konsekuensi dari kesederhanaan model komputasi objek. Menghilangkan detail, kita dapat mengatakan bahwa selama eksekusi sistem OO, hanya satu jenis peristiwa yang terjadi - pemanggilan fitur:


menunjukkan operasi F di atas benda yang dilekatkan X, dengan argumen lewat arg(mungkin banyak argumen atau tidak sama sekali). Pemrogram Smalltalk berbicara dalam hal ini tentang "melewati objek X pesan F dengan argumen arg", tetapi ini hanya perbedaan terminologi, oleh karena itu tidak signifikan.

Bahwa semuanya didasarkan pada Konstruksi Dasar ini menjelaskan bagian dari rasa keindahan dalam ide OO.

Dari Konstruksi Dasar ikuti situasi abnormal yang mungkin timbul dalam proses eksekusi:

Definisi: jenis pelanggaran

Pelanggaran jenis run-time, atau singkatnya hanya pelanggaran jenis, terjadi pada saat panggilan. x.f(arg), Di mana X melekat pada suatu objek OBJ jika:

[X]. tidak ada komponen yang cocok F dan berlaku untuk OBJ,

[X]. ada komponen seperti itu, bagaimanapun, argumennya arg tidak dapat diterima baginya.

Masalah pengetikan adalah untuk menghindari situasi seperti ini:

Masalah pengetikan untuk sistem OO

Kapan kita menemukan bahwa pelanggaran tipe dapat terjadi dalam pelaksanaan sistem OO?

Kata kuncinya adalah Kapan. Cepat atau lambat Anda akan menyadari bahwa ada pelanggaran jenis. Misalnya, mencoba mengeksekusi komponen "Peluncuran Torpedo" pada objek "Karyawan" tidak akan berfungsi dan eksekusi akan gagal. Namun, Anda mungkin lebih suka menemukan bug sedini mungkin daripada nanti.

Pengetikan statis dan dinamis

Meskipun opsi perantara dimungkinkan, dua pendekatan utama disajikan di sini:

[X]. Pengetikan dinamis: tunggu hingga setiap panggilan selesai, lalu ambil keputusan.

[X]. Pengetikan statis: diberi seperangkat aturan, tentukan dari teks sumber apakah pelanggaran jenis dimungkinkan selama eksekusi. Sistem dijalankan jika aturan menjamin bahwa tidak ada kesalahan.

Istilah-istilah ini mudah dijelaskan: dengan pengetikan dinamis, pengecekan tipe terjadi selama operasi sistem (secara dinamis), sedangkan dengan pengetikan statis, pengecekan tipe dilakukan pada teks secara statis (sebelum eksekusi).

Pengetikan statis melibatkan pemeriksaan otomatis, yang biasanya menjadi tanggung jawab kompiler. Akibatnya, kami memiliki definisi sederhana:

Definisi: bahasa yang diketik secara statis

Bahasa OO diketik secara statis jika dilengkapi dengan seperangkat aturan yang konsisten, diperiksa oleh kompiler, yang memastikan bahwa eksekusi sistem tidak mengarah pada pelanggaran tipe.

Syarat " kuat mengetik" ( kuat). Ini sesuai dengan sifat ultimatum dari definisi tersebut, yang membutuhkan tidak adanya pelanggaran jenis sama sekali. Mungkin dan lemah (lemah) bentuk pengetikan statis, di mana aturan menghilangkan pelanggaran tertentu tanpa menghilangkannya sama sekali. Dalam pengertian ini, beberapa bahasa OO diketik dengan lemah secara statis. Kami akan berjuang untuk pengetikan terkuat.

Dalam bahasa yang diketik secara dinamis, yang dikenal sebagai bahasa yang tidak diketik, tidak ada deklarasi tipe, dan nilai apa pun dapat dilampirkan ke entitas saat runtime. Pengecekan tipe statis tidak dimungkinkan di dalamnya.

Aturan pengetikan

Notasi OO kami diketik secara statis. Aturan tipenya diperkenalkan di kuliah sebelumnya dan diringkas menjadi tiga persyaratan sederhana.

[X]. Saat mendeklarasikan setiap entitas atau fungsi, jenisnya harus ditentukan, misalnya, acc: AKUN. Setiap subrutin memiliki 0 atau lebih argumen formal, yang jenisnya harus diberikan, misalnya: masukkan(x: G; i: INTEGER).

[X]. Dalam penugasan apapun x:= y dan pada setiap panggilan subrutin di mana y adalah argumen aktual untuk argumen formal X, jenis sumber y harus kompatibel dengan jenis target X. Definisi kompatibilitas didasarkan pada warisan: B cocok dengan A, jika turunannya, - dilengkapi dengan aturan untuk parameter umum (lihat Kuliah 14).

[X]. Panggilan x.f(arg) membutuhkan itu F adalah komponen kelas dasar untuk tipe target X, Dan F harus diekspor ke kelas tempat panggilan muncul (lihat 14.3).

Realisme

Meskipun definisi bahasa yang diketik secara statis cukup tepat, itu tidak cukup - kriteria informal diperlukan saat membuat aturan pengetikan. Mari pertimbangkan dua kasus ekstrim.

[X]. Bahasa yang sangat tepat, di mana setiap sistem yang benar secara sintaksis juga benar jenisnya. Aturan deklarasi tipe tidak diperlukan. Bahasa seperti itu ada (pikirkan notasi Polandia untuk menambah dan mengurangi bilangan bulat). Sayangnya, tidak ada bahasa universal yang memenuhi kriteria ini.

[X]. Bahasa yang benar-benar salah, yang mudah dibuat dengan mengambil bahasa apa pun yang ada dan menambahkan aturan pengetikan yang membuatnya setiap sistem tidak benar. Menurut definisi, bahasa ini diketik: karena tidak ada sistem yang sesuai dengan aturan, tidak ada sistem yang akan menyebabkan pelanggaran jenis.

Kita dapat mengatakan bahwa bahasa jenis pertama bugar, Tetapi tidak berguna, yang terakhir mungkin berguna, tetapi tidak cocok.

Dalam praktiknya, yang dibutuhkan adalah sistem tipe yang berguna dan berguna pada saat yang sama: cukup kuat untuk memenuhi kebutuhan perhitungan dan cukup nyaman untuk tidak memaksa kita berbelit-belit untuk memenuhi aturan pengetikan.

Kami akan mengatakan bahwa bahasa realistis jika dapat digunakan dan berguna dalam praktek. Berbeda dengan definisi pengetikan statis, yang memberikan jawaban tegas untuk pertanyaan: " Apakah X diketik secara statis?", definisi realisme sebagian bersifat subyektif.

Dalam kuliah ini, kami akan memastikan bahwa notasi yang kami usulkan realistis.

Pesimisme

Pengetikan statis pada dasarnya mengarah pada kebijakan "pesimistis". Upaya untuk menjamin itu semua perhitungan tidak menyebabkan kegagalan, menolak perhitungan yang bisa berakhir tanpa kesalahan.

Pertimbangkan bahasa biasa, non-obyektif, seperti Pascal dengan tipe yang berbeda NYATA Dan BILANGAN BULAT. Saat mendeskripsikan n: INTEGER; r: Nyata operator n:=r akan ditolak karena melanggar aturan. Dengan demikian, kompiler akan menolak semua pernyataan berikut:


Jika kita membiarkannya berjalan, kita akan melihat bahwa [A] akan selalu berfungsi, karena sistem bilangan apa pun memiliki representasi yang tepat dari bilangan real 0,0, yang diterjemahkan secara jelas menjadi 0 bilangan bulat. [B] hampir pasti juga akan berfungsi. Hasil dari tindakan [C] tidak jelas (apakah kita ingin mendapatkan hasil dengan membulatkan atau membuang bagian pecahan?). [D] akan melakukan pekerjaan itu, seperti halnya operator:


jika ^ 2< 0 then n:= 3.67 end [E]

yang mencakup tugas yang tidak dapat dijangkau ( n^2 adalah kuadrat dari bilangan tersebut N). Setelah penggantian n^2 pada N hanya serangkaian proses yang akan memberikan hasil yang benar. Penugasan N nilai riil besar yang tidak dapat direpresentasikan sebagai bilangan bulat akan mengakibatkan kegagalan.

Dalam bahasa yang diketik, semua contoh ini (berfungsi, tidak berfungsi, terkadang berfungsi) diperlakukan dengan kejam sebagai pelanggaran aturan untuk mendeskripsikan tipe dan ditolak oleh kompiler mana pun.

Pertanyaannya adalah tidak kami akan apakah kita pesimis, dan sebenarnya, berapa harganya kita bisa bersikap pesimis. Kembali ke persyaratan realisme: jika aturan tipe sangat pesimis sehingga mencegah perhitungan menjadi mudah untuk ditulis, kami akan menolaknya. Tetapi jika pencapaian keamanan jenis dicapai dengan sedikit kehilangan daya ekspresif, kami akan menerimanya. Misalnya, dalam lingkungan pengembangan yang menyediakan fungsi untuk membulatkan dan mengekstrak bagian bilangan bulat - bulat Dan memotong, operator n:=r dianggap salah, memang demikian, karena itu memaksa Anda untuk secara eksplisit menulis konversi real-to-integer, daripada menggunakan konversi default yang ambigu.

Pengetikan statis: bagaimana dan mengapa

Meskipun manfaat pengetikan statis sudah jelas, ada baiknya membicarakannya lagi.

Keuntungan

Kami membuat daftar alasan untuk menggunakan pengetikan statis dalam teknologi objek di awal kuliah. Ini adalah keandalan, kemudahan pemahaman dan efisiensi.

Keandalan karena deteksi kesalahan yang sebaliknya hanya dapat muncul selama operasi, dan hanya dalam beberapa kasus. Aturan pertama, yang memaksa entitas untuk dideklarasikan serta fungsi, memperkenalkan redundansi ke dalam teks program, yang memungkinkan kompiler, menggunakan dua aturan lainnya, untuk mendeteksi ketidakkonsistenan antara penggunaan entitas, komponen, dan tujuan yang dimaksudkan dan aktual. ekspresi.

Deteksi dini kesalahan juga penting karena semakin lama kita menunda menemukannya, semakin besar biaya perbaikannya. Properti ini, yang secara intuitif dapat dipahami oleh semua pemrogram profesional, dikonfirmasi secara kuantitatif oleh karya Boehm yang dikenal luas. Ketergantungan biaya koreksi pada saat menemukan kesalahan ditunjukkan dalam grafik yang dibangun berdasarkan sejumlah proyek industri besar dan eksperimen yang dilakukan dengan proyek terkontrol kecil:

Beras. 17.1. Biaya perbaikan bug komparatif (diterbitkan dengan izin)

Keterbacaan atau Kemudahan Pemahaman(keterbacaan) memiliki kelebihan. Dalam semua contoh di buku ini, kemunculan suatu tipe pada suatu entitas memberikan informasi kepada pembaca tentang tujuannya. Keterbacaan sangat penting pada tahap pemeliharaan.

Akhirnya, efisiensi dapat menentukan keberhasilan atau kegagalan teknologi objek dalam prakteknya. Dengan tidak adanya pengetikan statis saat eksekusi x.f(arg) itu bisa memakan waktu berapa pun. Alasannya adalah pada saat dijalankan, tidak ditemukan F di kelas target dasar X, pencarian akan berlanjut pada keturunannya, dan ini adalah jalan yang pasti menuju inefisiensi. Anda dapat mengatasi masalah tersebut dengan meningkatkan pencarian komponen dalam hierarki. Penulis bahasa Self telah melakukan banyak pekerjaan mencoba menghasilkan kode terbaik untuk bahasa yang diketik secara dinamis. Tapi itu adalah pengetikan statis yang memungkinkan produk OO seperti itu mendekati atau setara dalam efisiensi dengan perangkat lunak tradisional.

Kunci untuk pengetikan statis adalah gagasan yang telah dinyatakan bahwa kompiler yang menghasilkan kode untuk konstruk x.f(arg), tahu jenisnya X. Karena polimorfisme, tidak mungkin menentukan versi komponen yang sesuai secara unik F. Tetapi deklarasi mempersempit kumpulan tipe yang memungkinkan, memungkinkan kompiler untuk membuat tabel yang menyediakan akses ke yang benar F dengan biaya minimal, dengan konstanta terbatas kesulitan akses. Optimalisasi tambahan dilakukan pengikatan statis Dan substitusi (inlining)- juga difasilitasi oleh pengetikan statis, yang sepenuhnya menghilangkan overhead jika berlaku.

Argumen untuk pengetikan dinamis

Terlepas dari semua ini, pengetikan dinamis tidak kehilangan pengikutnya, khususnya di kalangan programmer Smalltalk. Argumen mereka didasarkan terutama pada realisme yang dibahas di atas. Mereka percaya bahwa pengetikan statis terlalu membatasi, mencegah mereka untuk mengekspresikan ide kreatif mereka dengan bebas, terkadang menyebutnya sebagai "sabuk kesucian".

Orang dapat setuju dengan argumentasi ini, tetapi hanya untuk bahasa yang diketik secara statis yang tidak mendukung sejumlah fitur. Perlu dicatat bahwa semua konsep yang terkait dengan konsep tipe dan diperkenalkan pada kuliah sebelumnya diperlukan - penolakan salah satu dari mereka penuh dengan batasan serius, dan pengenalannya, sebaliknya, memberikan fleksibilitas pada tindakan kita, dan memberi kami kesempatan untuk sepenuhnya menikmati kepraktisan pengetikan statis.

Typization: komponen kesuksesan

Apa mekanisme untuk pengetikan statis yang realistis? Semuanya telah diperkenalkan pada kuliah sebelumnya, dan oleh karena itu tinggal kita mengingatnya secara singkat. Pencacahan bersama mereka menunjukkan koherensi dan kekuatan asosiasi mereka.

Sistem tipe kami sepenuhnya didasarkan pada konsep kelas. Kelas bahkan tipe dasar seperti BILANGAN BULAT, dan oleh karena itu, kami tidak memerlukan aturan khusus untuk mendeskripsikan tipe yang telah ditentukan sebelumnya. (Di sinilah notasi kami berbeda dari bahasa "hibrid" seperti Object Pascal, Java, dan C++, di mana sistem jenis bahasa lama digabungkan dengan teknologi objek berbasis kelas.)

Jenis yang diperluas beri kami lebih banyak fleksibilitas dengan mengizinkan jenis yang nilainya menunjukkan objek serta jenis yang nilainya menunjukkan referensi.

Kata yang menentukan dalam menciptakan sistem tipe fleksibel milik warisan dan konsep terkait kesesuaian. Ini mengatasi batasan utama bahasa yang diketik klasik, misalnya Pascal dan Ada, di mana operatornya x:= y membutuhkan tipe tersebut X Dan y adalah sama. Aturan ini terlalu ketat: melarang penggunaan entitas yang dapat menunjukkan objek dari jenis terkait ( SAVINGS_ACCOUNT Dan CHECKING_ACCOUNT). Dalam pewarisan, kami hanya memerlukan kompatibilitas tipe y dengan tipe X, Misalnya, X memiliki tipe AKUN, y- SAVINGS_ACCOUNT, dan kelas kedua adalah penerus dari yang pertama.

Dalam praktiknya, bahasa yang diketik secara statis membutuhkan dukungan pewarisan berganda. Tuduhan mendasar dari pengetikan statis yang diketahui tidak memberikan kesempatan untuk menafsirkan objek dengan cara yang berbeda. Ya, objeknya DOKUMEN(dokumen) dapat ditransmisikan melalui jaringan, oleh karena itu perlu adanya komponen yang terkait dengan jenisnya PESAN(pesan). Tetapi kritik ini hanya berlaku untuk bahasa yang terbatas pada pewarisan tunggal.

Beras. 17.2. Beberapa Warisan

Keserbagunaan diperlukan, misalnya, untuk menggambarkan struktur data wadah yang fleksibel namun aman (misalnya DAFTAR kelas[G]...). Tanpa mekanisme ini, pengetikan statis memerlukan deklarasi kelas yang berbeda untuk daftar dengan tipe elemen yang berbeda.

Dalam beberapa kasus, fleksibilitas diperlukan membatasi, yang memungkinkan Anda menggunakan operasi yang hanya berlaku untuk entitas bertipe generik. Jika kelas generik SORTABLE_LIST mendukung penyortiran, itu membutuhkan entitas tipe G, Di mana G- parameter umum, adanya operasi perbandingan. Ini dicapai dengan menghubungkan ke G kelas mendefinisikan kendala generik - SEBANDING:


kelas SORTABLE_LIST ...

Setiap parameter umum yang sebenarnya SORTABLE_LIST harus menjadi anak kelas SEBANDING, yang memiliki komponen yang diperlukan.

Mekanisme penting lainnya adalah upaya penugasan- mengatur akses ke objek tersebut, jenis yang tidak dikontrol oleh perangkat lunak. Jika y adalah objek database atau objek yang diperoleh melalui jaringan, maka pernyataan x ?= y menetapkan X arti y, Jika y adalah tipe yang kompatibel, atau jika tidak, akan diberikan X arti Ruang kosong.

Pernyataan, diasosiasikan sebagai bagian dari ide Desain dengan Kontrak dengan kelas dan komponennya dalam bentuk prasyarat, kondisi akhir, dan invarian kelas, memungkinkan untuk mendeskripsikan batasan semantik yang tidak dicakup oleh spesifikasi tipe. Bahasa seperti Pascal dan Ada memiliki tipe rentang yang dapat membatasi nilai suatu entitas antara 10 dan 20, misalnya, tetapi Anda tidak dapat menggunakannya untuk memaksakan nilai Saya negatif, selalu dua kali lipat J. Invarian kelas datang untuk menyelamatkan, dirancang untuk secara akurat mencerminkan batasan yang diperkenalkan, tidak peduli seberapa rumitnya.

Iklan yang disematkan diperlukan untuk menghindari duplikasi kode dalam praktek. mengumumkan y: seperti x, Anda mendapatkan jaminan itu y akan berubah mengikuti deklarasi tipe berulang X pada keturunan. Dengan tidak adanya mekanisme ini, pengembang akan mendeklarasikan ulang tanpa henti, mencoba untuk menjaga agar jenis yang berbeda tetap konsisten.

Deklarasi lengket adalah kasus khusus dari mekanisme bahasa terakhir yang kami butuhkan - kovarians, yang akan dibahas lebih detail nanti.

Saat mengembangkan sistem perangkat lunak, sebenarnya, satu properti lagi diperlukan yang melekat pada lingkungan pengembangan itu sendiri - kompilasi ulang inkremental cepat. Saat Anda menulis atau memodifikasi sistem, Anda ingin melihat efek dari perubahan tersebut sesegera mungkin. Dengan pengetikan statis, Anda harus memberikan waktu kepada kompiler untuk memeriksa ulang jenisnya. Rutinitas kompilasi tradisional memerlukan kompilasi ulang seluruh sistem (dan majelisnya), dan proses ini bisa sangat lama, terutama dengan transisi ke sistem berskala besar. Fenomena ini telah menjadi argumen yang mendukung menafsirkan sistem, seperti lingkungan Lisp atau Smalltalk awal, yang menjalankan sistem dengan sedikit atau tanpa pemrosesan, tanpa pemeriksaan jenis. Sekarang argumen ini dilupakan. Kompiler modern yang baik mendeteksi bagaimana kode telah berubah sejak kompilasi terakhir dan hanya memproses perubahan yang ditemukannya.

"Apakah bayi itu dilambangkan"?

Tujuan kita - ketat pengetikan statis. Itulah mengapa kita harus menghindari celah apa pun dalam "bermain sesuai aturan", setidaknya mengidentifikasinya secara akurat jika ada.

Celah paling umum dalam bahasa yang diketik secara statis adalah adanya konversi yang mengubah jenis entitas. Dalam C dan bahasa turunannya, mereka disebut "type casting" atau casting. Rekaman (OTHER_TYPE) x menunjukkan bahwa nilai X dianggap oleh kompiler memiliki tipe OTHER_TYPE, tunduk pada batasan tertentu pada jenis yang mungkin.

Mekanisme seperti ini melewati batasan pemeriksaan tipe. Casting umum dalam pemrograman C, termasuk dialek ANSI C. Bahkan dalam C++, type casting, meski kurang umum, tetap umum dan mungkin diperlukan.

Mematuhi aturan pengetikan statis tidaklah mudah, jika sewaktu-waktu dapat dielakkan dengan casting.

Mengetik dan menautkan

Meskipun, sebagai pembaca buku ini, Anda pasti akan membedakan pengetikan statis dari statis mengikat Nah, ada orang yang tidak bisa melakukan itu. Ini mungkin sebagian karena pengaruh bahasa Smalltalk, yang mendukung pendekatan dinamis untuk kedua masalah tersebut dan dapat menyebabkan kesalahpahaman bahwa mereka memiliki solusi yang sama. (Kami berpendapat dalam buku ini bahwa sebaiknya menggabungkan pengetikan statis dan penautan dinamis untuk membuat program yang kuat dan fleksibel.)

Baik pengetikan maupun penjilidan berurusan dengan semantik Konstruksi Dasar x.f(arg) tetapi jawab dua pertanyaan berbeda:

Mengetik dan menautkan

[X]. Sebuah pertanyaan tentang mengetik: ketika kita perlu mengetahui dengan pasti bahwa operasi yang sesuai dengan F Berlaku untuk objek yang melekat pada entitas X(dengan parameter arg)?

[X]. Pertanyaan yang menghubungkan: Kapan kita perlu mengetahui operasi apa yang dimulai oleh panggilan tertentu?

Mengetik menjawab pertanyaan tentang ketersediaan setidaknya satu operasi, mengikat bertanggung jawab untuk memilih diperlukan.

Dalam kerangka pendekatan objek:

[X]. Masalah dengan pengetikan terkait dengan polimorfisme: karena Xsaat runtime dapat menunjukkan objek dari beberapa jenis yang berbeda, kita harus yakin bahwa operasi tersebut mewakili F, tersedia dalam setiap kasus ini;

[X]. masalah yang mengikat disebabkan pengumuman berulang: karena kelas dapat mengubah komponen yang diwariskan, mungkin ada dua atau lebih operasi yang diklaim mewakili F dalam panggilan ini.

Kedua masalah tersebut dapat diselesaikan baik secara dinamis maupun statis. Bahasa yang ada menyediakan keempat solusi tersebut.

[X]. Sejumlah bahasa non-objektif, seperti Pascal dan Ada, mengimplementasikan pengetikan statis dan penautan statis. Setiap entitas mewakili objek hanya dari satu tipe yang didefinisikan secara statis. Ini memastikan keandalan solusi, harga yang merupakan fleksibilitasnya.

[X]. Smalltalk dan bahasa OO lainnya berisi tautan dinamis dan pengetikan dinamis. Preferensi diberikan pada fleksibilitas dengan mengorbankan keandalan bahasa.

[X]. Beberapa bahasa non-objektif mendukung pengetikan dinamis dan penautan statis. Diantaranya adalah bahasa rakitan dan sejumlah bahasa skrip.

[X]. Gagasan pengetikan statis dan penjilidan dinamis diwujudkan dalam notasi yang diajukan dalam buku ini.

Perhatikan kekhasan bahasa C ++, yang mendukung pengetikan statis, meskipun tidak ketat karena adanya pengecoran tipe, pengikatan statis (secara default), pengikatan dinamis saat virtual ( maya) iklan.

Alasan memilih pengetikan statis dan penjilidan dinamis sudah jelas. Pertanyaan pertama adalah: "Kapan kita akan tahu tentang keberadaan komponen?" - menyarankan respons statis: " Lebih awal lebih baik", yang artinya: pada waktu kompilasi. Pertanyaan kedua, "Komponen mana yang akan digunakan?" menyarankan respons dinamis: " yang Anda butuhkan", sesuai dengan tipe dinamis dari objek yang ditentukan pada waktu proses. Ini adalah satu-satunya solusi yang dapat diterima jika pengikatan statis dan dinamis menghasilkan hasil yang berbeda.

Contoh hierarki pewarisan berikut akan membantu menjelaskan konsep-konsep ini:

Beras. 17.3. Jenis pesawat

Pertimbangkan panggilan:


my_aircraft.lower_landing_gear

Sebuah pertanyaan tentang mengetik: kapan harus memastikan bahwa sebuah komponen akan ada di sini lower_landing_gear("release landing gear"), berlaku untuk objek (untuk HELIKOPTER itu tidak akan sama sekali) Pertanyaan tentang pengikatan: mana dari beberapa versi yang mungkin untuk dipilih.

Pengikatan statis berarti kita mengabaikan jenis objek yang dilampirkan dan bergantung pada deklarasi entitas. Akibatnya, ketika berurusan dengan Boeing 747-400, kami akan meminta versi yang dirancang untuk pesawat seri 747 konvensional, dan bukan 747-400 untuk modifikasinya. Pengikatan dinamis menerapkan operasi yang diperlukan oleh objek, dan ini pendekatan yang tepat.

Dengan pengetikan statis, kompiler tidak akan menolak panggilan jika dapat dijamin saat mengeksekusi program ke entitas my_aircraft akan dilampirkan objek yang disertakan dengan komponen yang sesuai lower_landing_gear. Teknik dasar untuk mendapatkan jaminan sederhana: dengan deklarasi wajib my_aircraft kelas dasar dari jenisnya diperlukan untuk menyertakan komponen semacam itu. Itu sebabnya my_aircraft tidak dapat dinyatakan sebagai PESAWAT TERBANG, karena yang terakhir tidak memiliki lower_landing_gear pada tingkat ini; helikopter, setidaknya dalam contoh kita, tidak tahu cara melepaskan roda pendaratan. Jika kita mendeklarasikan entitas sebagai PESAWAT, - kelas yang berisi komponen yang diperlukan - semuanya akan baik-baik saja.

Pengetikan dinamis dalam gaya Smalltalk mengharuskan Anda menunggu panggilan, dan pada saat pelaksanaannya, periksa keberadaan komponen yang diinginkan. Perilaku ini dimungkinkan untuk prototipe dan pengembangan eksperimental, tetapi tidak dapat diterima untuk sistem industri - pada saat penerbangan sudah terlambat untuk menanyakan apakah Anda memiliki roda pendaratan.

Kovarian dan Penyembunyian Anak

Jika dunia ini sederhana, percakapan tentang mengetik bisa berakhir. Kami mengidentifikasi tujuan dan manfaat pengetikan statis, memeriksa batasan yang harus dipenuhi oleh sistem tipe realistis, dan memverifikasi bahwa metode pengetikan yang diusulkan memenuhi kriteria kami.

Tapi dunia ini tidak sederhana. Menggabungkan pengetikan statis dengan beberapa persyaratan rekayasa perangkat lunak menciptakan masalah yang lebih kompleks daripada yang terlihat. Masalah disebabkan oleh dua mekanisme: kovarians- perubahan jenis parameter saat mendefinisikan ulang, persembunyian keturunan- kemampuan kelas keturunan untuk membatasi status ekspor komponen yang diwariskan.

kovarians

Apa yang terjadi pada argumen komponen saat tipenya didefinisikan ulang? Ini adalah masalah utama, dan kita telah melihat sejumlah contohnya: perangkat dan printer, daftar tertaut tunggal dan ganda, dan sebagainya (lihat Bagian 16.6, 16.7).

Berikut adalah contoh lain untuk membantu memperjelas sifat masalah. Dan meski jauh dari kenyataan dan metaforis, kedekatannya dengan skema program terlihat jelas. Selain itu, saat menganalisisnya, kita akan sering kembali ke soal dari latihan.

Bayangkan tim ski universitas mempersiapkan kejuaraan. Kelas GADIS termasuk pemain ski yang merupakan bagian dari tim wanita, ANAK LAKI-LAKI- pemain ski. Sejumlah peserta dari kedua tim diperingkat, menunjukkan hasil yang baik di kompetisi sebelumnya. Ini penting bagi mereka, karena sekarang mereka akan berlari lebih dulu, mendapatkan keunggulan dibandingkan yang lain. (Aturan ini, yang mengistimewakan yang sudah diistimewakan, mungkin yang membuat slalom dan ski lintas alam begitu menarik di mata banyak orang, menjadi metafora yang baik untuk kehidupan itu sendiri.) Jadi kami memiliki dua kelas baru: RANKED_GIRL Dan RANKED_BOY.

Beras. 17.4. Klasifikasi pemain ski

Sejumlah kamar telah dipesan untuk akomodasi para atlet: hanya untuk putra, hanya untuk putri, hanya untuk pemenang putri. Untuk menampilkan ini, kami menggunakan hierarki kelas paralel: RUANG, GADIS_KAMAR Dan RANKED_GIRL_ROOM.

Berikut sketsa kelasnya PEMAIN SKI:


- Teman sekamar.
... Lainnya komponen yang mungkin dihilangkan dalam kelas ini dan selanjutnya ...

Kami tertarik pada dua komponen: atribut teman sekamar dan prosedur membagikan, yang "menempatkan" pemain ski ini di ruangan yang sama dengan pemain ski saat ini:


Saat mendeklarasikan entitas lainnya Anda dapat menjatuhkan jenisnya PEMAIN SKI mendukung tipe tetap seperti teman sekamar(atau seperti Arus Untuk teman sekamar Dan lainnya serentak). Tapi mari kita lupakan sejenak tentang mengetik pin (kita akan kembali lagi nanti) dan melihat masalah kovarians dalam bentuk aslinya.

Bagaimana cara memperkenalkan pengesampingan tipe? Aturan mengharuskan tempat tinggal terpisah antara anak laki-laki dan perempuan, pemenang dan peserta lainnya. Untuk mengatasi masalah ini, saat mendefinisikan ulang, kami akan mengubah jenis komponen teman sekamar, seperti yang ditunjukkan di bawah ini (selanjutnya, elemen yang diganti digarisbawahi).


- Teman sekamar.

Mendefinisikan kembali, masing-masing, argumen dari prosedur membagikan. Versi kelas yang lebih lengkap sekarang terlihat seperti ini:


- Teman sekamar.
-- Pilih yang lain sebagai tetangga.

Demikian pula, Anda harus mengubah semua yang dihasilkan dari PEMAIN SKI kelas (kami tidak menggunakan perbaikan tipe sekarang). Akibatnya, kami memiliki hierarki:

Beras. 17.5. Hierarki anggota dan definisi ulang

Karena pewarisan adalah spesialisasi, aturan jenis mengharuskan saat mengesampingkan hasil komponen, dalam hal ini teman sekamar, tipe baru adalah anak dari aslinya. Hal yang sama berlaku untuk mendefinisikan ulang jenis argumen. lainnya rutinitas membagikan. Strategi ini, seperti yang kita ketahui, disebut kovarians, di mana awalan "ko" menunjukkan perubahan bersama dalam jenis parameter dan hasil. Strategi sebaliknya disebut pertentangan.

Semua contoh kami secara meyakinkan menunjukkan perlunya kovarian secara praktis.

[X]. Elemen daftar tertaut tunggal DAPAT DITAUTKAN harus dikaitkan dengan elemen serupa lainnya, dan instance BI_LINKABLE- dengan yang serupa. Kovarian perlu diganti dan argumennya masuk letakkan dengan benar.

[X]. Setiap subrutin di LINKED_LIST dengan tipe argumen DAPAT DITAUTKAN saat pindah ke DUA_WAY_LIST akan membutuhkan argumen BI_LINKABLE.

[X]. Prosedur set_alternate menerima PERANGKAT-argumen di kelas PERANGKAT Dan PENCETAK-argumen - di kelas PENCETAK.

Redefinisi kovarian sangat populer karena penyembunyian informasi mengarah pada pembuatan prosedur formulir


-- Tetapkan atribut ke v.

bekerja dengan attrib jenis BEBERAPA_TYPE. Prosedur seperti itu, tentu saja, kovarian, karena setiap kelas yang mengubah tipe atribut harus mendefinisikan kembali argumennya. set_attrib. Meskipun contoh yang disajikan cocok dengan satu skema, kovarians jauh lebih luas. Pikirkan, misalnya, prosedur atau fungsi yang melakukan penggabungan daftar tertaut tunggal ( LINKED_LIST). Argumennya harus didefinisikan ulang sebagai daftar tertaut ganda ( DUA_WAY_LIST). Operasi penjumlahan universal infiks "+" menerima NUMERIK-argumen di kelas NUMERIK, NYATA- di kelas NYATA Dan BILANGAN BULAT- di kelas BILANGAN BULAT. Dalam hierarki layanan telepon paralel, prosedur awal di kelas PHONE_SERVICE argumen mungkin diperlukan ALAMAT, mewakili alamat pelanggan, (untuk penagihan), sedangkan prosedur yang sama di kelas CORPORATE_SERVICE jenis argumen diperlukan ALAMAT_PERUSAHAAN.

Beras. 17.6. Layanan Komunikasi

Apa yang dapat dikatakan tentang solusi kontravarian? Dalam contoh pemain ski, itu berarti jika, lolos ke kelas RANKED_GIRL, jenis hasil teman sekamar didefinisikan ulang sebagai RANKED_GIRL, kemudian, karena kontravarian, jenis argumen membagikan dapat didefinisikan ulang untuk mengetik GADIS atau PEMAIN SKI. Satu-satunya tipe yang tidak diperbolehkan di bawah solusi kontravarian adalah RANKED_GIRL! Cukup untuk menimbulkan kecurigaan terburuk pada orang tua gadis itu.

Hirarki Paralel

Agar tidak melewatkan batu, pertimbangkan varian dari contoh PEMAIN SKI dengan dua hierarki paralel. Ini akan memungkinkan kami untuk mensimulasikan situasi yang telah ditemui dalam praktik: TWO_WAY_LIST > LINKED_LIST Dan BI_LINKABLE > LINKABLE; atau hirarki dengan layanan telepon PHONE_SERVICE.

Biarkan ada hierarki dengan kelas RUANG, keturunan siapa GADIS_KAMAR(Kelas ANAK LAKI-LAKI dihilangkan):

Beras. 17.7. Pemain ski dan kamar

Kelas pemain ski kami dalam hierarki paralel ini sebagai gantinya teman sekamar Dan membagikan akan memiliki komponen yang serupa akomodasi (akomodasi) Dan menampung (tempat):


deskripsi: "Varian baru dengan hierarki paralel"
mengakomodasi (r: ROOM) adalah ... membutuhkan ... lakukan

Penggantian kovarian juga diperlukan di sini: di kelas GADIS1 Bagaimana akomodasi, dan argumen subrutin menampung harus diganti dengan jenisnya GADIS_KAMAR, di kelas BOY1- jenis BOY_ROOM dll. (Ingat, kita masih bekerja tanpa penyematan tipe.) Seperti contoh versi sebelumnya, kontravarian tidak berguna di sini.

Ketidakpatuhan polimorfisme

Apakah tidak ada cukup contoh yang menegaskan kepraktisan kovarians? Mengapa ada orang yang menganggap kontravarian yang bertentangan dengan apa yang dibutuhkan dalam praktik (selain perilaku beberapa anak muda)? Untuk memahami ini, pertimbangkan masalah yang muncul saat menggabungkan polimorfisme dan strategi kovarians. Membuat skema sabotase tidaklah sulit, dan Anda mungkin sudah membuatnya sendiri:


buat b; buat g;-- Buat objek BOY dan GIRL.

Hasil panggilan terakhir, sangat mungkin menyenangkan anak-anak muda, persis seperti yang kami coba cegah dengan pengesampingan tipe. Panggilan membagikan mengarah pada fakta bahwa objek ANAK LAKI-LAKI, dikenal sebagai B dan berkat polimorfisme menerima alias S jenis PEMAIN SKI, menjadi tetangga objek GADIS dikenal dengan nama G. Namun, seruan tersebut, meski bertentangan dengan aturan asrama, cukup benar dalam teks program, karena membagikan-mengekspor komponen dalam komposisi PEMAIN SKI, A GADIS, jenis argumen G, cocok dengan PEMAIN SKI, jenis parameter formal membagikan.

Skema hierarki paralel sama sederhananya: ganti PEMAIN SKI pada SKIER1, tantangan membagikan- dalam panggilan s.mengakomodasi(gr), Di mana gr- jenis entitas GADIS_KAMAR. Hasilnya sama.

Dengan solusi kontravarian untuk masalah ini, tidak akan ada: spesialisasi target panggilan (dalam contoh kami S) akan membutuhkan generalisasi argumen. Akibatnya, kontravarian mengarah ke model matematika yang lebih sederhana dari mekanisme tersebut: pewarisan - redefinisi - polimorfisme. Fakta ini dijelaskan dalam sejumlah artikel teoretis yang mengusulkan strategi ini. Argumennya tidak terlalu meyakinkan, karena, seperti yang ditunjukkan oleh contoh kami dan publikasi lain, kontravarian tidak memiliki kegunaan praktis.

Oleh karena itu, tanpa mencoba menarik pakaian kontravarian pada tubuh kovarian, seseorang harus menerima realitas kovarian dan mencari cara untuk menghilangkan efek yang tidak diinginkan.

Bersembunyi oleh keturunan

Sebelum mencari solusi untuk masalah kovarians, pertimbangkan mekanisme lain yang dapat menyebabkan pelanggaran tipe dalam kondisi polimorfisme. Persembunyian keturunan adalah kemampuan kelas untuk tidak mengekspor komponen yang diterima dari induknya.

Beras. 17.8. Bersembunyi oleh keturunan

Contoh tipikal adalah komponen add_vertex(tambahkan simpul) yang diekspor oleh kelas POLIGON, tetapi disembunyikan oleh keturunannya PERSEGI PANJANG(mengingat kemungkinan pelanggaran invarian - kelas ingin tetap berbentuk persegi panjang):


Contoh non-pemrograman: kelas "Burung Unta" menyembunyikan metode "Terbang", yang diterima dari induk "Burung".

Mari kita ambil skema ini untuk sesaat dan tanyakan apakah kombinasi warisan dan persembunyian akan sah. Peran pemodelan persembunyian, seperti kovarians, dilanggar oleh trik yang dimungkinkan karena polimorfisme. Dan di sini tidak sulit untuk membuat contoh jahat yang memungkinkan, meskipun menyembunyikan komponen, untuk memanggilnya dan menambahkan simpul ke persegi panjang:


pencipta; -- Buat objek RECTANGLE.
p:=r; - Penugasan polimorfik.

Sejak objek R bersembunyi di bawah esensi P kelas POLIGON, A add_vertex komponen yang diekspor POLIGON, lalu pemanggilannya oleh entitas P benar. Sebagai hasil dari eksekusi, satu titik lagi akan muncul di persegi panjang, yang berarti objek yang tidak valid akan dibuat.

Kebenaran sistem dan kelas

Untuk membahas masalah kovarian dan persembunyian keturunan, kita memerlukan beberapa istilah baru. Kami akan menelepon kelas-benar (kelas-valid) sistem yang memenuhi tiga aturan untuk mendeskripsikan jenis yang diberikan di awal kuliah. Ingat mereka: setiap entitas memiliki tipenya sendiri; jenis argumen aktual harus sesuai dengan jenis argumen formal, situasinya mirip dengan penugasan; komponen yang dipanggil harus dideklarasikan di kelasnya dan diekspor ke kelas yang berisi panggilan tersebut.

Sistem ini disebut sistem-benar (sistem-valid), jika tidak ada pelanggaran tipe yang terjadi selama pelaksanaannya.

Idealnya, kedua konsep harus cocok. Namun, kita telah melihat bahwa sistem kelas yang benar di bawah kondisi pewarisan, kovarians, dan penyembunyian oleh keturunan mungkin bukan sistem yang benar. Sebut saja kesalahan ini kesalahan validitas sistem.

Aspek praktis

Kesederhanaan masalah menciptakan semacam paradoks: seorang pemula yang ingin tahu dapat membuat contoh tandingan dalam hitungan menit, dalam praktik nyata kesalahan kelas kebenaran sistem terjadi hari demi hari, tetapi pelanggaran kebenaran sistem, bahkan dalam jumlah besar, multi- proyek tahun, sangat jarang terjadi.

Namun, ini tidak memungkinkan kami untuk mengabaikannya, dan oleh karena itu kami mulai mempelajari tiga cara yang mungkin untuk menyelesaikan masalah ini.

Selanjutnya, kita akan menyentuh aspek pendekatan objek yang sangat halus dan tidak begitu sering terwujud. Jika Anda membaca buku ini untuk pertama kalinya, Anda mungkin ingin melewatkan bagian-bagian selanjutnya dari kuliah ini. Jika Anda baru mengenal teknologi OO, Anda akan lebih memahami materi ini setelah mempelajari kuliah 1-11 dari kursus "Dasar-dasar Desain Berorientasi Objek", yang dikhususkan untuk metodologi pewarisan, dan khususnya kuliah 6 dari kursus "Dasar-dasar Desain Berorientasi Objek", dikhususkan untuk pewarisan metodologi.

Kebenaran sistem: perkiraan pertama

Mari kita fokus dulu pada masalah kovarians, yang lebih penting dari keduanya. Ada banyak literatur yang dikhususkan untuk topik ini, menawarkan sejumlah solusi berbeda.

Kontravarian dan invarian

Contravariance menghilangkan masalah teoritis yang terkait dengan pelanggaran kebenaran sistem. Namun, ini kehilangan realisme sistem tipe, oleh karena itu, tidak perlu mempertimbangkan pendekatan ini lebih jauh.

Keaslian bahasa C++ adalah bahwa ia menggunakan strategi novarian, mencegah Anda mengubah jenis argumen dalam subrutin yang diganti! Jika C++ adalah bahasa yang diketik dengan kuat, sistem tipenya akan sulit digunakan. Solusi paling sederhana untuk masalah dalam bahasa ini, serta melewati batasan lain dari C ++ (katakanlah, kurangnya universalitas terbatas), adalah dengan menggunakan casting - type casting, yang memungkinkan Anda untuk sepenuhnya mengabaikan mekanisme pengetikan yang ada. Solusi ini sepertinya tidak menarik. Perhatikan, bagaimanapun, bahwa sejumlah proposal yang dibahas di bawah ini akan bergantung pada ketiadaan varians, yang artinya akan diberikan oleh pengenalan mekanisme baru untuk bekerja dengan tipe alih-alih redefinisi kovarian.

Menggunakan Parameter Generik

Universalitas adalah inti dari ide menarik yang pertama kali diajukan oleh Franz Weber. Mari kita mendeklarasikan kelas SKIER1, membatasi generikisasi parameter generik ke kelas RUANG:


fitur kelas SKIER1
mengakomodasi (r: G) adalah ... memerlukan ... melakukan akomodasi:= r akhir

Kemudian kelas GADIS1 akan menjadi ahli waris SKIER1 dll. Teknik yang sama, betapapun anehnya pada pandangan pertama, dapat digunakan jika tidak ada hierarki paralel: pemain ski kelas.

Pendekatan ini memecahkan masalah kovarians. Setiap penggunaan kelas harus menentukan parameter generik yang sebenarnya RUANG atau GADIS_KAMAR, sehingga kombinasi yang salah menjadi tidak mungkin. Bahasa menjadi tidak bervarian, dan sistem sepenuhnya memenuhi kebutuhan kovarian karena parameter umum.

Sayangnya, teknik ini tidak dapat diterima sebagai solusi umum, karena mengarah ke daftar parameter generik yang terus bertambah, satu untuk setiap jenis argumen kovarian yang mungkin. Lebih buruk lagi, menambahkan subrutin kovarian dengan argumen yang tipenya tidak ada dalam daftar akan membutuhkan penambahan parameter kelas generik dan karenanya akan mengubah antarmuka kelas, menyebabkan semua klien kelas berubah, yang tidak dapat diterima.

Ketik variabel

Sejumlah penulis, termasuk Kim Bruce, David Shang, dan Tony Simons, telah menemukan solusi berdasarkan variabel tipe yang nilainya adalah tipe. Ide mereka sederhana:

[X]. alih-alih penimpaan kovarian, izinkan deklarasi tipe yang menggunakan variabel tipe;

[X]. memperluas aturan kompatibilitas tipe untuk mengelola variabel tersebut;

[X]. memberikan kemampuan untuk menetapkan variabel tipe sebagai nilai untuk tipe bahasa.

Pembaca dapat menemukan presentasi terperinci dari ide-ide ini di sejumlah artikel tentang topik ini, serta dalam publikasi oleh Cardelli (Cardelli), Castagna (Castagna), Weber (Weber), dan lain-lain. Kami tidak akan menangani masalah ini, dan inilah alasannya.

[X]. Mekanisme variabel tipe yang diimplementasikan dengan benar termasuk dalam kategori yang memungkinkan suatu tipe digunakan tanpa menentukannya sepenuhnya. Kategori yang sama mencakup keserbagunaan dan penahan iklan. Mekanisme ini dapat menggantikan mekanisme lain dalam kategori ini. Pada awalnya, ini dapat ditafsirkan untuk mendukung variabel tipe, tetapi hasilnya bisa menjadi bencana, karena tidak jelas apakah mekanisme komprehensif ini dapat menangani semua tugas dengan kemudahan dan kesederhanaan yang melekat pada generalitas dan penyematan tipe.

[X]. Asumsikan bahwa mekanisme variabel tipe dikembangkan yang dapat mengatasi masalah penggabungan kovarians dan polimorfisme (masih mengabaikan masalah penyembunyian turunan). Maka pengembang kelas akan membutuhkan intuisi yang luar biasa untuk memutuskan terlebih dahulu komponen mana yang akan tersedia untuk mendefinisikan ulang tipe di kelas turunan, dan mana yang tidak. Di bawah ini kami akan membahas masalah ini, yang terjadi dalam praktik pembuatan program dan, sayangnya, meragukan penerapan banyak skema teoritis.

Ini memaksa kita untuk kembali ke mekanisme yang sudah dipertimbangkan: universalitas terbatas dan tidak terbatas, penyematan tipe, dan, tentu saja, pewarisan.

Mengandalkan penyematan tipe

Kami akan menemukan solusi yang hampir siap pakai untuk masalah kovarians dengan melihat mekanisme deklarasi tersemat yang kami ketahui.

Saat mendeskripsikan kelas PEMAIN SKI Dan SKIER1 Anda mau tidak mau dikunjungi oleh keinginan, menggunakan pengumuman tetap, untuk menyingkirkan banyak definisi ulang. Menjepit adalah mekanisme kovarian yang khas. Seperti inilah contoh kita (semua perubahan digarisbawahi):


berbagi (lainnya: seperti Saat Ini) adalah ... membutuhkan ... lakukan
mengakomodasi (r: seperti akomodasi) adalah ... membutuhkan ... lakukan

Sekarang anak-anak dapat meninggalkan kelas PEMAIN SKI tidak berubah, dan SKIER1 mereka hanya perlu mengganti atribut akomodasi. Entitas yang disematkan: atribut teman sekamar dan argumen subrutin membagikan Dan menampung- akan berubah secara otomatis. Ini sangat menyederhanakan pekerjaan dan menegaskan fakta bahwa dengan tidak adanya penahan (atau mekanisme serupa lainnya, seperti variabel tipe), tidak mungkin untuk menulis produk OO dengan pengetikan yang realistis.

Tetapi apakah Anda berhasil menghilangkan pelanggaran terhadap kebenaran sistem? TIDAK! Kita dapat, seperti sebelumnya, mengecoh pengecekan tipe dengan melakukan tugas polimorfik yang menyebabkan pelanggaran kebenaran sistem.

Benar, versi asli dari contoh akan ditolak. Biarlah:


buat b;buat g;-- Buat objek BOY dan GIRL.
s:=b; - Penugasan polimorfik.

Argumen G ditularkan membagikan, sekarang salah karena memerlukan objek bertipe seperti s, dan kelas GADIS tidak kompatibel dengan tipe ini, karena menurut aturan tipe tetap tidak ada tipe yang kompatibel seperti s kecuali untuk dirinya sendiri.

Namun, kami tidak bersukacita lama. Di sisi lain, aturan ini mengatakan itu seperti s kompatibel dengan tipe S. Jadi, menggunakan polimorfisme tidak hanya dari objek S, tetapi juga parameternya G, kita dapat melewati sistem pengecekan tipe lagi:


s: pemain ski; b:LAKI-LAKI; g: suka; aktual_g:GIRL;
buat b; create actual_g -- Membuat objek BOY dan GIRL.
s:= aktual_g; g:= s -- Tambahkan g ke GIRL via s.
s:= b -- Penugasan polimorfik.

Akibatnya, panggilan ilegal berhasil.

Ada jalan keluar. Jika kita serius menggunakan deklarasi pinning sebagai satu-satunya mekanisme untuk kovarians, maka kita dapat menyingkirkan pelanggaran kebenaran sistemik dengan sepenuhnya melarang polimorfisme entitas yang disematkan. Ini akan membutuhkan perubahan bahasa: perkenalkan kata kunci baru jangkar(kita membutuhkan konstruksi hipotetis ini semata-mata untuk digunakan dalam diskusi ini):


Mari izinkan deklarasi seperti seperti s hanya bila S digambarkan sebagai jangkar. Mari ubah aturan kompatibilitas untuk memastikan: S dan elemen tipe seperti s hanya dapat dilampirkan (dalam penugasan atau penyampaian argumen) satu sama lain.

Dengan pendekatan ini, kami menghapus dari bahasa kemungkinan untuk mendefinisikan ulang jenis argumen subrutin apa pun. Selain itu, kami dapat melarang pendefinisian ulang jenis hasil, tetapi ini tidak perlu. Kemampuan untuk mengesampingkan jenis atribut, tentu saja, dipertahankan. Semua redefinisi tipe argumen sekarang akan dilakukan secara implisit melalui mekanisme penyematan yang dipicu oleh kovarians. Di mana, dengan pendekatan sebelumnya, kelas D menimpa komponen yang diwariskan sebagai:


sedangkan kelas C- orang tua D itu tampak


Di mana Y berkorespondensi X, sekarang mendefinisikan ulang komponen R akan terlihat seperti ini:


Tetap di kelas D mengesampingkan jenis jangkar_anda.

Solusi untuk masalah kovarians - polimorfisme ini akan disebut pendekatan Penahan. Akan lebih akurat untuk mengatakan: "Covariance only through Pinning". Sifat-sifat pendekatan ini menarik:

[X]. Pinning didasarkan pada gagasan pemisahan yang ketat kovarian dan elemen yang berpotensi polimorfik (atau, singkatnya, polimorfik). Semua entitas dinyatakan sebagai jangkar atau seperti some_anchor kovarian; lainnya bersifat polimorfik. Di masing-masing dari dua kategori, lampiran apa pun diperbolehkan, tetapi tidak ada entitas atau ekspresi yang melanggar batas. Anda tidak dapat, misalnya, menetapkan sumber polimorfik ke target kovarian.

[X]. Solusi sederhana dan elegan ini mudah dijelaskan bahkan untuk pemula.

[X]. Ini sepenuhnya menghilangkan kemungkinan melanggar kebenaran sistem dalam sistem yang dibangun secara kovarian.

[X]. Ini mempertahankan kerangka konseptual yang ditetapkan di atas, termasuk konsep universalitas terbatas dan tidak terbatas. (Akibatnya, solusi ini, menurut saya, lebih disukai untuk mengetik variabel yang menggantikan mekanisme kovarians dan universalitas, yang dirancang untuk menyelesaikan berbagai masalah praktis.)

[X]. Ini membutuhkan sedikit perubahan dalam bahasa - menambahkan satu kata kunci yang tercermin dalam aturan pencocokan - dan tidak melibatkan kesulitan implementasi yang berarti.

[X]. Ini realistis (setidaknya dalam teori): setiap sistem yang mungkin sebelumnya dapat ditulis ulang dengan mengganti penimpaan kovarian dengan pernyataan ulang berlabuh. Memang benar bahwa beberapa lampiran akan menjadi tidak valid sebagai akibatnya, tetapi lampiran tersebut sesuai dengan kasus yang dapat menyebabkan pelanggaran jenis, dan oleh karena itu harus diganti dengan upaya penugasan dan ditangani saat runtime.

Tampaknya diskusi bisa berakhir di situ. Jadi mengapa pendekatan Anchoring tidak sepenuhnya sesuai dengan keinginan kita? Pertama-tama, kami belum menyentuh masalah persembunyian anak. Selain itu, alasan utama untuk melanjutkan diskusi adalah masalah yang sudah diungkapkan dalam penyebutan variabel tipe secara singkat. Pembagian lingkup pengaruh menjadi bagian polimorfik dan kovarian agak mirip dengan hasil Konferensi Yalta. Diasumsikan bahwa perancang kelas memiliki intuisi yang luar biasa, bahwa dia mampu, untuk setiap entitas yang dia perkenalkan, khususnya untuk setiap argumen, untuk memilih satu dari dua kemungkinan untuk selamanya:

[X]. Entitas berpotensi polimorfik: sekarang atau nanti (dengan meneruskan parameter atau dengan penugasan) entitas dapat dilampirkan ke objek yang tipenya berbeda dari yang dideklarasikan. Jenis entitas asli tidak dapat diubah oleh keturunan kelas mana pun.

[X]. Entitas adalah subjek dari pengesampingan tipe, yang berarti entitas tersebut berlabuh atau merupakan poros itu sendiri.

Tapi bagaimana pengembang bisa meramalkan semua ini? Semua daya tarik metode OO, yang diekspresikan dalam banyak cara dalam prinsip Terbuka-Tertutup, justru karena kemungkinan perubahan yang berhak kami lakukan untuk pekerjaan yang telah dilakukan sebelumnya, serta fakta bahwa pengembang solusi universal Bukan harus memiliki kebijaksanaan yang tak terbatas, memahami bagaimana produknya dapat disesuaikan dengan kebutuhan mereka oleh keturunan.

Dengan pendekatan ini, pendefinisian ulang tipe dan persembunyian oleh keturunan adalah semacam "katup pengaman" yang memungkinkan untuk menggunakan kembali kelas yang sudah ada yang hampir cocok untuk tujuan kita:

[X]. Dengan beralih ke redefinisi tipe, kita dapat mengubah deklarasi di kelas turunan tanpa mempengaruhi aslinya. Dalam hal ini, solusi kovarian murni akan memerlukan pengeditan yang asli dengan transformasi yang dijelaskan.

[X]. Bersembunyi oleh keturunan melindungi dari banyak kegagalan saat membuat kelas. Dimungkinkan untuk mengkritik proyek di mana PERSEGI PANJANG, menggunakan fakta bahwa adalah keturunan POLIGON, mencoba menambahkan simpul. Sebaliknya, seseorang dapat mengusulkan struktur warisan di mana angka dengan jumlah simpul tetap dipisahkan dari yang lain, dan masalah tidak akan muncul. Namun, saat merancang struktur pewarisan, selalu lebih baik memiliki struktur yang tidak dimiliki pengecualian taksonomi. Tapi bisakah mereka dihilangkan sama sekali? Saat membahas pembatasan ekspor di salah satu kuliah berikut, kita akan melihat bahwa hal ini tidak mungkin dilakukan karena dua alasan. Yang pertama adalah adanya kriteria klasifikasi yang bersaing. Kedua, kemungkinan pengembang tidak akan menemukan solusi yang ideal, meskipun solusi itu ada.

Analisis Global

Bagian ini dikhususkan untuk deskripsi pendekatan menengah. Solusi praktis utama disajikan dalam kuliah 17 .

Saat menjelajahi opsi penyematan, kami melihat bahwa ide utamanya adalah memisahkan kumpulan entitas kovarian dan polimorfik. Jadi, jika kita mengambil dua bentuk instruksi


masing-masing adalah contoh penerapan yang benar dari mekanisme OO penting: yang pertama - polimorfisme, redefinisi tipe kedua. Masalah dimulai saat menggabungkannya untuk entitas yang sama S. Demikian pula:


masalah dimulai dengan penyatuan dua operator independen dan sama sekali tidak bersalah.

Panggilan yang salah menyebabkan pelanggaran jenis. Pada contoh pertama, penugasan polimorfik melampirkan objek ANAK LAKI-LAKI ke esensi S, apa yang dia lakukan G argumen yang tidak sah membagikan, karena dikaitkan dengan objek GADIS. Dalam contoh kedua untuk entitas R objek terpasang PERSEGI PANJANG, yang mengesampingkan add_vertex dari komponen yang diekspor.

Inilah ide solusi baru: sebelumnya - secara statis, saat memeriksa jenis oleh kompiler atau alat lain - kami mendefinisikan mengeset setiap entitas, termasuk jenis objek yang dapat diasosiasikan dengan entitas tersebut pada waktu proses. Kemudian, sekali lagi secara statis, kami memastikan bahwa setiap panggilan benar untuk setiap elemen dari kumpulan argumen dan tipe target.

Dalam contoh kami, operator s:=b menunjukkan bahwa kelas ANAK LAKI-LAKI milik himpunan jenis untuk S(karena sebagai hasil dari mengeksekusi pernyataan create membuat b itu milik set jenis untuk B). GADIS, karena adanya instruksi buat g, milik kumpulan tipe untuk G. Tapi kemudian tantangannya membagikan akan tidak valid untuk target S jenis ANAK LAKI-LAKI dan argumen G jenis GADIS. Demikian pula PERSEGI PANJANG adalah tipe yang ditetapkan untuk P, yang disebabkan oleh penugasan polimorfik, bagaimanapun, panggilan tersebut add_vertex Untuk P jenis PERSEGI PANJANG akan menjadi tidak valid.

Pengamatan ini membawa kita untuk berpikir tentang penciptaan global pendekatan berdasarkan aturan pengetikan baru:

Aturan kebenaran sistem

Panggilan x.f(arg) adalah sistem-benar jika dan hanya jika itu adalah kelas-benar untuk X, Dan arg, yang memiliki tipe apa pun dari kumpulan tipenya masing-masing.

Dalam definisi ini, panggilan dianggap class-correct jika tidak melanggar aturan Pemanggilan Komponen, yang mengatakan: jika C ada kelas dasar seperti X, komponen F harus diekspor C, dan jenisnya arg harus kompatibel dengan jenis parameter formal F. (Ingat, untuk penyederhanaan, kita asumsikan bahwa setiap subrutin hanya memiliki satu parameter, tetapi tidak sulit untuk memperluas aturan ke sejumlah argumen yang berubah-ubah.)

Ketepatan sistem panggilan direduksi menjadi kebenaran kelas, kecuali bahwa itu diperiksa bukan untuk elemen individual, tetapi untuk setiap pasangan dari kumpulan kumpulan. Berikut adalah aturan dasar untuk membuat sekumpulan tipe untuk setiap entitas:

1 Untuk setiap entitas, kumpulan tipe awal kosong.

2 Setelah bertemu dengan instruksi formulir lainnya buat (SOME_TYPE) a, menambahkan BEBERAPA_TYPE ke set tipe untuk A. (Untuk penyederhanaan, kami akan menganggap bahwa setiap instruksi membuat akan diganti dengan instruksi buat (ATYPE) a, Di mana SEBUAH TIPE- tipe entitas A.)

3 Menghadapi penugasan lain dari formulir a:=b, tambahkan ke set tipe untuk A B.

4 Jika A adalah parameter formal dari subrutin, kemudian, setelah memenuhi pemanggilan berikutnya dengan parameter aktual B, tambahkan ke set tipe untuk A semua elemen dari himpunan tipe untuk B.

5 Kami akan mengulangi langkah (3) dan (4) hingga rangkaian tipe berhenti berubah.

Rumusan ini tidak memperhitungkan mekanisme universalitas, namun dimungkinkan untuk memperluas aturan sesuai kebutuhan tanpa masalah khusus. Langkah (5) diperlukan karena kemungkinan rantai tugas dan transfer (dari B Ke A, dari C Ke B dll.). Sangat mudah untuk memahami bahwa setelah beberapa langkah, proses ini akan berhenti.

Seperti yang mungkin telah Anda ketahui, aturan tersebut tidak memperhitungkan urutan instruksi. Kapan


buat(TYPE1) t; s:=t; buat (TYPE2) t

ke set tipe untuk S masukkan sebagai TIPE 1, Dan JENIS2, Meskipun S, mengingat urutan instruksi, hanya mampu mengambil nilai tipe pertama. Mempertimbangkan lokasi instruksi akan membutuhkan kompiler untuk menganalisis aliran instruksi secara mendalam, yang akan menyebabkan peningkatan berlebihan dalam tingkat kompleksitas algoritma. Sebaliknya, aturan yang lebih pesimis berlaku: urutan operasi:


akan dinyatakan salah sistem, meskipun faktanya urutan pelaksanaannya tidak mengarah pada pelanggaran jenis.

Analisis global sistem (lebih detail) disajikan dalam bab ke-22 dari monografi. Pada saat yang sama, masalah kovarians dan masalah pembatasan ekspor selama pewarisan diselesaikan. Namun, pendekatan ini memiliki kelemahan praktis yang tidak menguntungkan, yaitu: harus diperiksa sistem secara keseluruhan daripada masing-masing kelas secara terpisah. Aturan (4) ternyata fatal, yang ketika memanggil subrutin perpustakaan, akan memperhitungkan semua kemungkinan panggilannya di kelas lain.

Meskipun kemudian algoritma diusulkan untuk bekerja dengan kelas individu di , nilai praktisnya tidak dapat ditentukan. Ini berarti bahwa dalam lingkungan pemrograman yang mendukung kompilasi inkremental, perlu mengatur pemeriksaan seluruh sistem. Dianjurkan untuk memperkenalkan pengecekan sebagai elemen pemrosesan lokal (cepat) dari perubahan yang dibuat oleh pengguna ke beberapa kelas. Meskipun contoh pendekatan global diketahui, misalnya, pemrogram C menggunakan alat ini serat untuk menemukan ketidakkonsistenan dalam sistem yang tidak terdeteksi oleh kompiler - semua ini tidak terlihat menarik.

Alhasil, setahu saya pemeriksaan kebenaran sistem tidak dilakukan oleh siapapun. (Alasan lain untuk hasil ini mungkin adalah kerumitan aturan validasi itu sendiri.)

Kebenaran kelas melibatkan validasi terbatas pada kelas dan oleh karena itu dimungkinkan dengan kompilasi tambahan. Kebenaran sistem menyiratkan pemeriksaan global dari seluruh sistem, yang bertentangan dengan kompilasi tambahan.

Namun, terlepas dari namanya, sebenarnya dimungkinkan untuk memeriksa kebenaran sistem hanya dengan menggunakan pemeriksaan kelas tambahan (selama kompiler normal). Ini akan menjadi kontribusi terakhir untuk memecahkan masalah.

Waspadai catcall polimorfik!

Aturan Ketepatan Sistem bersifat pesimistis: demi kesederhanaan, ia juga menolak kombinasi instruksi yang sepenuhnya aman. Tampaknya paradoks, tetapi kami akan membuat versi terakhir dari solusi berdasarkan aturan bahkan lebih pesimis. Secara alami, ini menimbulkan pertanyaan tentang seberapa realistis hasil kami nantinya.

Kembali ke Yalta

Inti dari solusi Panggilan Kucing (Panggilan Kucing), - arti dari konsep ini akan kami jelaskan nanti - sebagai kembali ke semangat perjanjian Yalta, membagi dunia menjadi polimorfik dan kovarian (dan pendamping kovarian - menyembunyikan keturunan), tetapi tanpa perlu memiliki kebijaksanaan yang tak terbatas.

Seperti sebelumnya, kami mempersempit pertanyaan tentang kovarian menjadi dua operasi. Dalam contoh utama kami, ini adalah penugasan polimorfik: s:=b, dan memanggil subrutin kovarian: s.bagikan(g). Menganalisis siapa pelaku pelanggaran yang sebenarnya, kami mengecualikan argumen tersebut G dari para tersangka. Jenis argumen apa pun PEMAIN SKI atau diturunkan darinya, tidak cocok untuk kita karena polimorfisme S dan kovarians membagikan. Oleh karena itu, jika Anda menggambarkan esensi secara statis lainnya Bagaimana PEMAIN SKI dan secara dinamis menempel pada objek PEMAIN SKI, lalu panggilan s.bagikan (lainnya) secara statis akan memberikan kesan ideal, tetapi akan menghasilkan pelanggaran tipe jika ditetapkan secara polimorfik S arti B.

Masalah mendasar adalah bahwa kita mencoba untuk menggunakan S dalam dua cara yang tidak kompatibel: sebagai entitas polimorfik dan sebagai target panggilan subrutin kovarian. (Dalam contoh kami yang lain, masalahnya adalah menggunakan P sebagai entitas polimorfik dan sebagai target pemanggilan subrutin anak yang menyembunyikan komponen add_vertex.)

Solusi Catcall, seperti Pinning, bersifat radikal: ia melarang penggunaan entitas sebagai polimorfik dan kovarian pada saat yang bersamaan. Seperti penguraian global, ini secara statis menentukan entitas mana yang dapat menjadi polimorfik, tetapi tidak mencoba terlalu pintar dengan mencari kumpulan tipe entitas yang mungkin. Sebaliknya, entitas polimorfik apa pun dianggap cukup mencurigakan sehingga dilarang bersekutu dengan lingkaran orang terhormat, termasuk kovarians dan persembunyian keturunan.

Satu aturan dan beberapa definisi

Aturan tipe untuk solusi Catcall memiliki formulasi sederhana:

Ketik Aturan untuk Catcall

Catcall polimorfik salah.

Ini didasarkan pada definisi yang sama sederhananya. Pertama-tama, entitas polimorfik:

Definisi: entitas polimorfik

Esensi X tipe referensi (tidak diperluas) adalah polimorfik jika memiliki salah satu dari properti berikut:

1 Terjadi dalam penugasan x:= y, dimana intinya y memiliki tipe yang berbeda atau polimorfik dengan rekursi.

2 Ditemukan dalam instruksi pembuatan buat(OTHER_TYPE) x, Di mana OTHER_TYPE bukan tipe yang ditentukan dalam deklarasi X.

3 Ini adalah argumen formal untuk subrutin.

4 Adalah fungsi eksternal.

Tujuan dari definisi ini adalah untuk memberikan status polimorfik ("berpotensi polimorfik") ke setiap entitas yang dapat dilampirkan ke objek dari berbagai jenis selama eksekusi program. Definisi ini hanya berlaku untuk tipe referensi, karena entitas yang diperluas tidak dapat bersifat polimorfik.

Dalam contoh kami, pemain ski S dan poligon P polimorfik menurut aturan (1). Yang pertama diberi objek ANAK laki-laki b, yang kedua - objek RECTANGLE r.

Jika Anda telah melihat definisi kumpulan tipe, Anda akan melihat betapa lebih pesimisnya definisi entitas polimorfik, dan betapa lebih mudahnya untuk mengujinya. Tanpa mencoba menemukan semua kemungkinan tipe dinamis dari suatu entitas, kami puas dengan pertanyaan umum: dapatkah entitas tertentu menjadi polimorfik atau tidak? Yang paling mengejutkan adalah aturan (3), yang menurutnya polimorfik penting setiap parameter formal(kecuali tipenya diperpanjang, seperti halnya bilangan bulat, dll.). Kami bahkan tidak peduli dengan analisis panggilan. Jika subrutin memiliki argumen, maka itu sepenuhnya tersedia untuk klien, yang berarti Anda tidak dapat mengandalkan jenis yang ditentukan dalam deklarasi. Aturan ini terkait erat dengan penggunaan kembali - tujuan dari teknologi objek - di mana setiap kelas berpotensi dimasukkan ke dalam pustaka dan dipanggil berkali-kali oleh klien yang berbeda.

Properti karakteristik dari aturan ini adalah tidak memerlukan pemeriksaan global. Untuk mengidentifikasi polimorfisme suatu entitas, cukup dengan melihat teks dari kelas itu sendiri. Jika untuk semua permintaan (atribut atau fungsi) informasi tentang status polimorfiknya disimpan, maka teks leluhur pun tidak perlu dipelajari. Tidak seperti menemukan kumpulan tipe, Anda dapat menemukan entitas polimorfik dengan memeriksa kelas demi kelas selama kompilasi inkremental.

Panggilan, seperti entitas, dapat bersifat polimorfik:

Definisi: panggilan polimorfik

Panggilan bersifat polimorfik jika targetnya polimorfik.

Kedua panggilan dalam contoh kami bersifat polimorfik: s.bagikan(g) karena polimorfisme S, p.add_vertex(...) karena polimorfisme P. Menurut definisi, hanya panggilan yang memenuhi syarat yang dapat bersifat polimorfik. (Memberi panggilan yang tidak memenuhi syarat F(...) jenis yang berkualitas Saat ini.f(...), kami tidak mengubah esensi masalah, karena Saat ini, yang tidak dapat diberikan apa pun, bukanlah objek polimorfik.)

Selanjutnya, kita membutuhkan konsep Catcall, berdasarkan konsep CAT. (CAT adalah singkatan dari Mengubah Ketersediaan atau Jenis). Subrutin adalah subrutin CAT jika beberapa redefinisi oleh seorang anak menghasilkan salah satu dari dua jenis perubahan yang telah kita lihat berpotensi berbahaya: mengubah jenis argumen (secara kovarian) atau menyembunyikan komponen yang diekspor sebelumnya.

Definisi: rutinitas CAT

Rutin disebut rutin CAT jika beberapa redefinisinya mengubah status ekspor atau jenis argumennya.

Properti ini sekali lagi memungkinkan pemeriksaan inkremental: setiap redefinisi tipe argumen atau status ekspor menjadikan prosedur atau fungsi sebagai subrutin CAT. Di sinilah gagasan Catcall masuk: memanggil subrutin CAT yang bisa salah.

Definisi: Panggilan kucing

Panggilan disebut Catcall jika beberapa redefinisi rutin akan membuatnya gagal karena perubahan status ekspor atau jenis argumen.

Klasifikasi yang kami buat memungkinkan kami membedakan grup panggilan khusus: polimorfik dan panggilan kucing. Panggilan polimorfik memberikan kekuatan ekspresif pada pendekatan objek, panggilan kucing memungkinkan Anda mendefinisikan ulang jenis dan membatasi ekspor. Menggunakan terminologi yang diperkenalkan di awal bab ini, kita dapat mengatakan bahwa panggilan polimorfik meluas kegunaan, panggilan telepon - kegunaan.

Tantangan membagikan Dan add_vertex, yang dipertimbangkan dalam contoh kami, adalah panggilan kucing. Yang pertama melakukan redefinisi kovarian dari argumennya. Yang kedua diekspor oleh kelas PERSEGI PANJANG, tetapi disembunyikan oleh kelas POLIGON. Kedua panggilan tersebut juga bersifat polimorfik, sehingga merupakan contoh yang sangat baik dari panggilan kucing polimorfik. Mereka salah menurut aturan tipe Catcall.

Nilai

Sebelum kita menyelesaikan semua yang telah kita pelajari tentang kovarian dan persembunyian anak, mari rekap bahwa pelanggaran terhadap kebenaran sistem memang jarang terjadi. Properti paling penting dari pengetikan OO statis dirangkum di awal kuliah. Susunan mekanisme pengetikan yang mengesankan ini, bersama dengan validasi berbasis kelas, membuka jalan bagi metode konstruksi perangkat lunak yang aman dan fleksibel.

Kami telah melihat tiga solusi untuk masalah kovarians, dua di antaranya juga menyentuh pembatasan ekspor. Yang mana yang benar?

Tidak ada jawaban pasti untuk pertanyaan ini. Konsekuensi dari interaksi berbahaya antara pengetikan OO dan polimorfisme tidak dipahami sebaik masalah yang dibahas dalam kuliah sebelumnya. Dalam beberapa tahun terakhir, banyak publikasi tentang topik ini telah muncul, referensi yang diberikan dalam bibliografi di akhir kuliah. Selain itu, saya berharap dalam kuliah ini saya telah mampu menyajikan elemen-elemen dari solusi akhir, atau setidaknya mendekatinya.

Analisis global tampaknya tidak praktis karena pemeriksaan lengkap seluruh sistem. Namun, itu membantu kami lebih memahami masalahnya.

Solusi Pinning sangat menarik. Ini sederhana, intuitif, mudah diimplementasikan. Terlebih lagi kita harus menyesali ketidakmungkinan mendukung di dalamnya sejumlah persyaratan utama metode OO, yang tercermin dalam prinsip Terbuka-Tertutup. Jika kita benar-benar memiliki intuisi yang hebat, maka menyematkan akan menjadi solusi yang bagus, tetapi pengembang mana yang berani menegaskan hal ini, atau, terlebih lagi, mengakui bahwa penulis kelas perpustakaan yang diwarisi dalam proyeknya memiliki intuisi seperti itu?

Jika kita terpaksa meninggalkan fiksasi, maka solusi Catcall tampaknya paling tepat, yang cukup mudah dijelaskan dan diterapkan dalam praktik. Pesimismenya seharusnya tidak menghalangi kombinasi operator yang berguna. Dalam kasus di mana catcall polimorfik dihasilkan oleh pernyataan "sah", selalu aman untuk mengakuinya dengan memperkenalkan upaya penugasan. Dengan demikian, sejumlah pemeriksaan dapat ditransfer ke waktu eksekusi program. Namun, jumlah kasus seperti itu seharusnya sangat kecil.

Sebagai klarifikasi, saya harus mencatat bahwa pada saat penulisan ini, solusi Catcall belum diimplementasikan. Sampai kompiler disesuaikan dengan pemeriksaan aturan tipe Catcall dan berhasil diterapkan pada sistem representasional besar dan kecil, masih terlalu dini untuk mengatakan bahwa kata terakhir telah dikatakan tentang masalah rekonsiliasi pengetikan statis dengan polimorfisme, dikombinasikan dengan kovarians dan penyembunyian turunan. .

Kepatuhan penuh

Mengakhiri pembahasan tentang kovarians, akan berguna untuk memahami bagaimana metode umum dapat diterapkan pada masalah yang cukup umum. Metode tersebut muncul sebagai hasil dari teori Catcall, tetapi dapat digunakan dalam kerangka versi dasar bahasa tanpa memperkenalkan aturan baru.

Biarkan ada dua daftar yang cocok, di mana yang pertama menentukan pemain ski dan yang kedua menentukan teman sekamar pemain ski dari daftar pertama. Kami ingin melakukan prosedur penempatan yang sesuai membagikan, hanya jika diizinkan oleh aturan deskripsi tipe yang mengizinkan gadis dengan gadis, gadis penghargaan dengan gadis penghargaan, dan seterusnya. Masalah seperti ini biasa terjadi.

Mungkin ada solusi sederhana berdasarkan diskusi sebelumnya dan usaha penugasan. Pertimbangkan fungsi universal pas(menyetujui):


pas (lainnya: UMUM): seperti lainnya
-- Objek saat ini (Saat ini), jika tipenya cocok dengan tipe objek,
-- melekat pada yang lain, jika tidak batal.
jika lainnya /= Batal lalu sesuai_ke (lainnya) lalu

Fungsi pas mengembalikan objek saat ini, tetapi dikenal sebagai entitas dari tipe yang dilampirkan ke argumen. Jika tipe objek saat ini tidak cocok dengan tipe objek yang dilampirkan pada argumen, maka kembalikan Ruang kosong. Perhatikan peran upaya penugasan. Fungsi menggunakan komponen sesuai_untuk dari kelas UMUM, yang menentukan kompatibilitas jenis sepasang objek.

Penggantian sesuai_untuk ke komponen lain UMUM Dengan nama Tipe yang sama memberi kita fungsi perfect_fitted (kepatuhan penuh) yang kembali Ruang kosong jika jenis kedua objek tidak identik.

Fungsi pas- memberi kami solusi sederhana untuk masalah pencocokan pemain ski tanpa melanggar aturan untuk mendeskripsikan jenis. Jadi, dalam kode kelas PEMAIN SKI kami dapat memperkenalkan prosedur baru dan menggunakannya sebagai gantinya membagikan, (yang terakhir dapat dijadikan prosedur tersembunyi).


-- Pilih, jika ada, selain sebagai tetangga nomor.
-- gender_ascertained - jenis kelamin ditetapkan
gender_ascertained_other: seperti Aktual
gender_ascertained_other:= lainnya .fitted(Saat ini)
jika gender_ascertained_other /= Void maka
bagikan(gender_ascertained_other)
"Kesimpulan: Colocation dengan yang lain tidak mungkin"

Untuk lainnya tipe sewenang-wenang PEMAIN SKI(tidak hanya seperti Arus) menentukan versi gender_ascertained_other, yang memiliki tipe yang ditetapkan untuk Saat ini. Fungsi ini akan membantu kami untuk menjamin identitas tipe perfect_fitted.

Jika ada dua daftar pemain ski paralel yang mewakili akomodasi yang direncanakan:


penghuni1, penghuni2: DAFTAR

dimungkinkan untuk mengatur loop dengan mengeksekusi panggilan di setiap langkah:


penghuni1.item.safe_share(penghuni2.item)

elemen daftar yang cocok jika dan hanya jika tipenya kompatibel sepenuhnya.

Konsep Kunci

[X]. Pengetikan statis adalah kunci keandalan, keterbacaan, dan efisiensi.

[X]. Agar realistis, pengetikan statis memerlukan kombinasi mekanisme: pernyataan, pewarisan berganda, penugasan yang dicoba, generikitas terbatas dan tidak terbatas, deklarasi berlabuh. Sistem tipe tidak boleh mengizinkan jebakan (tipe gips).

[X]. Aturan praktis untuk deklarasi ulang harus memungkinkan untuk definisi ulang kovarian. Hasil yang diganti dan tipe argumen harus kompatibel dengan yang asli.

[X]. Kovarian, serta kemampuan anak untuk menyembunyikan komponen yang diekspor oleh leluhur, dikombinasikan dengan polimorfisme, menciptakan masalah pelanggaran jenis yang jarang namun sangat serius.

[X]. Pelanggaran ini dapat dihindari dengan menggunakan: analisis global (yang tidak praktis), membatasi kovarian ke tipe tetap (yang bertentangan dengan prinsip Open-Closed), solusi Catcall, yang mencegah target polimorfik memanggil subrutin dengan kovarian atau menyembunyikan anak.

Catatan bibliografi

Sejumlah materi dari kuliah ini disajikan dalam laporan di forum OOPSLA 95 dan TOOLS PACIFIC 95, dan juga dipublikasikan di . Sejumlah bahan ulasan dipinjam dari artikel.

Konsep inferensi tipe otomatis diperkenalkan di , di mana algoritme inferensi tipe dari bahasa ML fungsional dijelaskan. Hubungan antara polimorfisme dan pengecekan tipe telah dieksplorasi di .

Teknik untuk meningkatkan efisiensi kode dalam bahasa yang diketik secara dinamis dalam konteks bahasa Diri dapat ditemukan di .

Luca Cardelli dan Peter Wegner menulis artikel teoretis tentang tipe dalam bahasa pemrograman yang memiliki pengaruh besar pada spesialis. Pekerjaan ini, dibangun atas dasar kalkulus lambda (lihat), berfungsi sebagai dasar untuk banyak studi lebih lanjut. Itu didahului oleh makalah fundamental lainnya oleh Cardelli.

Manual ISE mencakup pengantar masalah aplikasi bersama polimorfisme, kovarians, dan penyembunyian keturunan. Kurangnya analisis yang tepat dalam edisi pertama buku ini menyebabkan sejumlah diskusi kritis (yang pertama adalah komentar Philippe Elinck dalam karya sarjana "De la Conception-Programmation par Objets", Memoire de licence, Universite Libre de Bruxelles (Belgia), 1988), diekspresikan dalam karya dan . Artikel Cook memberikan beberapa contoh terkait masalah kovarians dan upaya untuk menyelesaikannya. Solusi berdasarkan parameter tipe untuk entitas kovarian di TOOLS EROPA 1992 diusulkan oleh Franz Weber. Definisi yang tepat dari konsep kebenaran sistem, serta kebenaran kelas, diberikan di , dan solusi menggunakan analisis sistem lengkap juga diusulkan di sana. Solusi Catcall pertama kali diusulkan di ; Lihat juga .

Solusi Memperbaiki disajikan dalam ceramah saya di seminar TOOLS EUROPE 1994. Namun, pada saat itu, saya tidak melihat perlunya jangkar-ads dan batasan kompatibilitas terkait. Paul Dubois dan Amiram Yehudai dengan cepat menunjukkan bahwa masalah kovarian tetap ada dalam kondisi ini. Mereka, serta Reinhardt Budde, Karl-Heinz Sylla, Kim Walden, dan James McKim, memberikan banyak komentar yang sangat penting dalam pekerjaan yang menyebabkan penulisan kuliah ini.

Sejumlah besar literatur dikhususkan untuk masalah kovarians. Di dalam dan Anda akan menemukan bibliografi yang luas dan ikhtisar aspek matematika dari masalah tersebut. Untuk daftar tautan ke materi online tentang teori tipe OOP dan halaman Web penulisnya, lihat halaman Laurent Dami. Konsep kovarian dan kontravarian dipinjam dari teori kategori. Kami berhutang penampilan mereka dalam konteks pengetikan program kepada Luca Cardelli, yang mulai menggunakannya dalam pidatonya di awal tahun 80-an, tetapi tidak menggunakannya dalam bentuk cetak hingga akhir tahun 80-an.

Teknik berdasarkan variabel tipe dijelaskan dalam , , .

Contravariance diimplementasikan dalam bahasa Sather. Penjelasan diberikan dalam.

  • Pengetikan dinamis adalah teknik yang banyak digunakan dalam bahasa pemrograman dan bahasa spesifikasi, di mana suatu variabel dikaitkan dengan suatu jenis pada saat nilai ditetapkan, dan bukan pada saat variabel tersebut dideklarasikan. Jadi, di berbagai bagian program, variabel yang sama dapat mengambil nilai dari tipe yang berbeda. Contoh bahasa yang diketik secara dinamis adalah Smalltalk, Python, Objective-C, Ruby, PHP, Perl, JavaScript, Lisp, xBase, Erlang, Visual Basic.

    Teknik kebalikannya adalah pengetikan statis.

    Dalam beberapa bahasa dengan pengetikan dinamis yang lemah, terdapat masalah dalam membandingkan nilai, misalnya PHP memiliki operator pembanding "==", "!=" dan "===", "!==", di mana pasangan kedua operasi membandingkan nilai, dan jenis variabel. Operator "===" mengevaluasi ke true hanya jika cocok dengan sempurna, tidak seperti "==" yang menganggap ekspresi berikut benar: (1=="1"). Perlu dicatat bahwa ini bukan masalah pengetikan dinamis secara umum, tetapi bahasa pemrograman tertentu.

Konsep terkait

Bahasa pemrograman adalah bahasa formal untuk menulis program komputer. Bahasa pemrograman mendefinisikan seperangkat aturan leksikal, sintaksis, dan semantik yang menentukan tampilan program dan tindakan yang akan dilakukan oleh pemain (biasanya komputer) di bawah kendalinya.

Gula sintaksis dalam bahasa pemrograman adalah fitur sintaksis yang penggunaannya tidak memengaruhi perilaku program, tetapi membuat penggunaan bahasa lebih nyaman bagi manusia.

Properti adalah cara untuk mengakses keadaan internal suatu objek, meniru variabel dari beberapa jenis. Mengakses properti objek terlihat sama dengan mengakses bidang struct (dalam pemrograman terstruktur), tetapi sebenarnya diimplementasikan melalui pemanggilan fungsi. Saat Anda mencoba menetapkan nilai properti ini, satu metode dipanggil, dan saat Anda mencoba mendapatkan nilai properti ini, metode lain dipanggil.

Extended Backus–Naur Form (EBNF) adalah sistem definisi sintaksis formal di mana beberapa kategori sintaksis didefinisikan secara berurutan melalui yang lain. Digunakan untuk mendeskripsikan tata bahasa formal bebas konteks. Disarankan oleh Niklaus Wirth. Ini adalah pengerjaan ulang yang diperpanjang dari bentuk Backus-Naur, berbeda dari BNF dalam konstruksi yang lebih "luas", yang, dengan kemampuan ekspresif yang sama, memungkinkan untuk disederhanakan...

Pemrograman aplikatif adalah jenis pemrograman deklaratif di mana penulisan program terdiri dari aplikasi sistematis dari satu objek ke objek lainnya. Hasil dari aplikasi semacam itu sekali lagi adalah objek yang dapat berpartisipasi dalam aplikasi baik sebagai fungsi maupun sebagai argumen, dan seterusnya. Ini membuat rekaman program jelas secara matematis. Fakta bahwa suatu fungsi dilambangkan dengan ekspresi menunjukkan kemungkinan menggunakan fungsi-nilai - fungsional ...

Bahasa pemrograman concatenative adalah bahasa pemrograman yang didasarkan pada fakta bahwa gabungan dari dua potongan kode mengekspresikan komposisi mereka. Dalam bahasa seperti itu, spesifikasi implisit argumen fungsi digunakan secara luas (lihat pemrograman tanpa tujuan), fungsi baru didefinisikan sebagai komposisi fungsi, dan penggabungan digunakan sebagai pengganti aplikasi. Pendekatan ini berlawanan dengan pemrograman aplikatif.

Variabel adalah atribut dari sistem fisik atau abstrak yang dapat mengubah nilainya, biasanya numerik. Konsep variabel banyak digunakan dalam bidang-bidang seperti matematika, ilmu alam, teknik dan pemrograman. Contoh variabel adalah: suhu udara, parameter fungsi, dan banyak lagi.

Analisis sintaksis (atau penguraian, penguraian slang ← penguraian bahasa Inggris) dalam linguistik dan ilmu komputer adalah proses membandingkan urutan linier leksem (kata, token) dari bahasa alami atau formal dengan tata bahasa formalnya. Hasilnya biasanya pohon parse (pohon sintaksis). Biasanya digunakan bersamaan dengan analisis leksikal.

Tipe data aljabar umum (GADT) adalah salah satu tipe tipe data aljabar, yang dicirikan oleh fakta bahwa konstruktornya dapat mengembalikan nilai yang bukan tipenya yang terkait dengannya. Dirancang di bawah pengaruh karya pada keluarga induktif di antara peneliti tipe dependen.

Semantik dalam pemrograman adalah disiplin yang mempelajari formalisasi makna konstruksi bahasa pemrograman dengan membangun model matematika formal mereka. Berbagai alat dapat digunakan sebagai alat untuk membangun model tersebut, misalnya logika matematika, kalkulus λ, teori himpunan, teori kategori, teori model, aljabar universal. Formalisasi semantik bahasa pemrograman dapat digunakan baik untuk mendeskripsikan bahasa, untuk menentukan sifat-sifat bahasa...

Pemrograman berorientasi objek (OOP) adalah metodologi pemrograman yang didasarkan pada representasi program sebagai sekumpulan objek, yang masing-masing merupakan turunan dari kelas tertentu, dan kelas membentuk hierarki pewarisan.

Variabel dinamis - variabel dalam program, tempat di RAM yang dialokasikan selama eksekusi program. Faktanya, itu adalah bagian dari memori yang dialokasikan oleh sistem ke program untuk tujuan tertentu saat program sedang berjalan. Dalam hal ini berbeda dari variabel statis global - bagian dari memori yang dialokasikan oleh sistem ke program untuk tujuan tertentu sebelum program dimulai. Variabel dinamis adalah salah satu kelas penyimpanan variabel.

Untuk menjelaskan dua teknologi yang sangat berbeda sesederhana mungkin, mari kita mulai dari awal. Hal pertama yang ditemui programmer saat menulis kode adalah mendeklarasikan variabel. Anda mungkin memperhatikan bahwa, misalnya, dalam bahasa pemrograman C++, Anda harus menentukan tipe variabel. Artinya, jika Anda mendeklarasikan variabel x, maka Anda harus menambahkan int - untuk menyimpan data integer, float - untuk menyimpan data floating point, char - untuk data karakter, dan tipe lain yang tersedia. Oleh karena itu, C++ menggunakan pengetikan statis, seperti pendahulunya, C.

Bagaimana cara kerja pengetikan statis?

Pada saat mendeklarasikan variabel, kompiler perlu mengetahui fungsi dan parameter mana yang dapat digunakan sehubungan dengannya, dan mana yang tidak. Oleh karena itu, programmer harus segera menunjukkan jenis variabel dengan jelas. Perhatikan juga bahwa jenis variabel tidak dapat diubah saat kode sedang berjalan. Tapi Anda bisa membuat tipe data Anda sendiri dan menggunakannya di masa mendatang.

Mari kita pertimbangkan contoh kecil. Saat menginisialisasi variabel x (int x;), kami menentukan pengidentifikasi int - ini adalah singkatan yang hanya menyimpan bilangan bulat dalam kisaran dari - 2 147 483 648 hingga 2 147 483 647. Dengan demikian, kompiler memahami bahwa ia dapat melakukan nilai matematika pada variabel ini - jumlah, selisih, perkalian dan pembagian. Tapi, misalnya, fungsi strcat(), yang menghubungkan dua nilai char, tidak bisa diterapkan ke x. Lagi pula, jika Anda menghapus batasan dan mencoba menghubungkan dua nilai int menggunakan metode simbolik, kesalahan akan terjadi.

Mengapa kita membutuhkan bahasa dengan pengetikan dinamis?

Terlepas dari beberapa keterbatasan, pengetikan statis memiliki sejumlah keunggulan, dan tidak menimbulkan banyak ketidaknyamanan dalam penulisan algoritme. Namun, untuk berbagai keperluan, lebih banyak "aturan longgar" terkait tipe data mungkin diperlukan.

Contoh yang baik untuk diberikan adalah JavaScript. Bahasa pemrograman ini biasanya digunakan untuk menyematkan kerangka kerja untuk mendapatkan akses fungsional ke objek. Karena fitur ini, ia mendapatkan popularitas besar dalam teknologi web, di mana pengetikan dinamis sangat ideal. Terkadang, menulis skrip kecil dan makro disederhanakan. Dan juga ada keuntungan dalam penggunaan kembali variabel. Tetapi kemungkinan ini jarang digunakan, karena kemungkinan kebingungan dan kesalahan.

Jenis pengetikan apa yang terbaik?

Perdebatan bahwa pengetikan dinamis lebih baik daripada pengetikan ketat berlanjut hingga hari ini. Biasanya mereka terjadi pada programmer yang sangat terspesialisasi. Tentu saja, pengembang web menggunakan semua keunggulan pengetikan dinamis setiap hari untuk membuat kode berkualitas tinggi dan produk perangkat lunak akhir. Pada saat yang sama, pemrogram sistem yang mengembangkan algoritme kompleks dalam bahasa pemrograman tingkat rendah biasanya tidak memerlukan kemampuan seperti itu, sehingga pengetikan statis sudah cukup bagi mereka. Tentu saja ada pengecualian terhadap aturan tersebut. Misalnya, pengetikan dinamis diimplementasikan sepenuhnya dengan Python.

Oleh karena itu, perlu ditentukan kepemimpinan teknologi tertentu hanya berdasarkan parameter input. Pengetikan dinamis lebih baik untuk mengembangkan kerangka kerja yang ringan dan fleksibel, sedangkan pengetikan yang kuat lebih baik untuk menciptakan arsitektur yang masif dan kompleks.

Pemisahan menjadi pengetikan "kuat" dan "lemah".

Di antara materi berbahasa Rusia dan bahasa Inggris tentang pemrograman, orang dapat menemukan ungkapan - pengetikan "kuat". Ini bukan konsep yang terpisah, atau lebih tepatnya, konsep seperti itu sama sekali tidak ada dalam leksikon profesional. Meski banyak yang mencoba menafsirkannya secara berbeda. Nyatanya, pengetikan yang "kuat" harus dipahami sebagai pengetikan yang nyaman bagi Anda dan yang paling nyaman untuk digunakan. Sistem yang "lemah" adalah sistem yang tidak nyaman dan tidak efisien untuk Anda.

Fitur dinamis

Anda pasti telah memperhatikan bahwa pada tahap penulisan kode, kompiler menganalisis konstruksi tertulis dan menghasilkan kesalahan jika tipe datanya tidak cocok. Tapi bukan JavaScript. Keunikannya terletak pada kenyataan bahwa ia akan melakukan operasi dalam hal apa pun. Ini contoh mudah - kami ingin menambahkan karakter dan angka, yang tidak masuk akal: "x" + 1.

Dalam bahasa statis, bergantung pada bahasa itu sendiri, operasi ini dapat memiliki konsekuensi yang berbeda. Tetapi dalam banyak kasus, itu bahkan tidak diizinkan untuk dikompilasi, karena kompiler akan memberikan kesalahan segera setelah menulis konstruksi seperti itu. Dia hanya akan menganggapnya salah dan akan sepenuhnya benar.

Dalam bahasa dinamis, operasi ini dapat dilakukan, tetapi dalam banyak kasus kesalahan akan mengikuti pada tahap eksekusi kode, karena kompiler tidak menganalisis tipe data secara waktu nyata dan tidak dapat menentukan kesalahan di area ini. JavaScript unik karena akan melakukan operasi seperti itu dan berakhir dengan serangkaian karakter yang tidak dapat dibaca. Berbeda dengan bahasa lain yang hanya akan menghentikan program.

Apakah arsitektur yang berdekatan mungkin?

Saat ini, tidak ada teknologi terkait yang secara bersamaan dapat mendukung pengetikan statis dan dinamis dalam bahasa pemrograman. Dan kami dapat dengan yakin mengatakan bahwa itu tidak akan muncul. Karena arsitektur berbeda satu sama lain dalam istilah fundamental dan tidak dapat digunakan secara bersamaan.

Namun, bagaimanapun, dalam beberapa bahasa, Anda dapat mengubah pengetikan menggunakan kerangka kerja tambahan.

  • Dalam bahasa pemrograman Delphi, subsistem Variant.
  • Dalam bahasa pemrograman AliceML - paket tambahan.
  • Dalam bahasa pemrograman Haskell, perpustakaan Data.Dynamic.

Kapan pengetikan yang kuat benar-benar lebih baik daripada pengetikan dinamis?

Anda dapat dengan tegas menyetujui keunggulan pengetikan yang kuat daripada yang dinamis hanya jika Anda seorang programmer pemula. Benar-benar semua spesialis IT setuju akan hal ini. Saat mengajarkan keterampilan pemrograman dasar dan dasar, lebih baik menggunakan pengetikan yang kuat untuk mendapatkan disiplin saat bekerja dengan variabel. Kemudian, jika perlu, Anda dapat beralih ke dinamika, tetapi keterampilan yang diperoleh dengan pengetikan yang kuat akan memainkan peran penting. Anda akan mempelajari cara memeriksa variabel dengan cermat dan mempertimbangkan jenisnya saat merancang dan menulis kode.

Manfaat pengetikan dinamis

  • Meminimalkan jumlah karakter dan baris kode karena pra-deklarasi variabel yang tidak perlu dan menentukan jenisnya. Jenis akan ditentukan secara otomatis setelah nilai ditetapkan.
  • Dalam blok kode kecil, persepsi visual dan logis konstruksi disederhanakan karena tidak adanya garis deklarasi "ekstra".
  • Dinamika memiliki efek positif pada kecepatan kompiler, karena tidak memperhitungkan jenis akun, dan tidak memeriksa kepatuhannya.
  • Meningkatkan fleksibilitas dan memungkinkan Anda membuat desain serbaguna. Misalnya, saat membuat metode yang harus berinteraksi dengan larik data, Anda tidak perlu membuat fungsi terpisah untuk bekerja dengan numerik, teks, dan jenis larik lainnya. Cukup menulis satu metode, dan itu akan bekerja dengan semua jenis.
  • Ini menyederhanakan keluaran data dari sistem manajemen basis data, sehingga pengetikan dinamis digunakan secara aktif dalam pengembangan aplikasi web.

Pelajari lebih lanjut tentang bahasa pemrograman dengan pengetikan statis

  • C ++ adalah bahasa pemrograman tujuan umum yang paling banyak digunakan. Saat ini ia memiliki beberapa edisi utama dan banyak pengguna. Itu menjadi populer karena fleksibilitasnya, kemungkinan perluasan tanpa batas dan dukungan untuk berbagai paradigma pemrograman.

  • Java adalah bahasa pemrograman yang menggunakan pendekatan berorientasi objek. Mendapatkan popularitas karena multi-platform. Saat dikompilasi, kode diinterpretasikan menjadi bytecode yang dapat dijalankan di sistem operasi apa pun. Java dan pengetikan dinamis tidak kompatibel karena bahasanya diketik dengan kuat.

  • Haskell juga merupakan salah satu bahasa populer yang kodenya dapat diintegrasikan dan berinteraksi dengan bahasa lain. Namun, terlepas dari fleksibilitasnya, ia memiliki pengetikan yang kuat. Dilengkapi dengan kumpulan tipe bawaan yang besar dan kemampuan untuk membuatnya sendiri.

Pelajari lebih lanjut tentang bahasa pemrograman dengan pengetikan dinamis

  • Python adalah bahasa pemrograman yang diciptakan terutama untuk memudahkan pekerjaan seorang programmer. Ini memiliki sejumlah peningkatan fungsional, yang meningkatkan keterbacaan kode dan penulisannya. Dalam banyak hal, ini tercapai berkat pengetikan dinamis.

  • PHP adalah bahasa scripting. Banyak digunakan dalam pengembangan web, menyediakan interaksi dengan database untuk membuat halaman web dinamis interaktif. Berkat pengetikan dinamis, bekerja dengan basis data menjadi sangat mudah.

  • JavaScript adalah bahasa pemrograman yang telah disebutkan di atas, yang telah menemukan aplikasi dalam teknologi web untuk membuat skrip web yang berjalan di sisi klien. Pengetikan dinamis digunakan untuk membuat kode lebih mudah ditulis, karena biasanya dipecah menjadi blok-blok kecil.

Jenis Pengetikan Dinamis - Kerugian

  • Jika salah ketik atau kesalahan dibuat saat menggunakan atau mendeklarasikan variabel, kompiler tidak akan menampilkannya. Dan masalah akan muncul selama pelaksanaan program.
  • Saat menggunakan pengetikan statis, semua deklarasi variabel dan fungsi biasanya ditempatkan berkas terpisah, yang memungkinkan Anda membuat dokumentasi dengan mudah di masa mendatang atau bahkan menggunakan file itu sendiri sebagai dokumentasi. Karenanya, pengetikan dinamis tidak mengizinkan penggunaan fitur semacam itu.

Meringkaskan

Pengetikan statis dan dinamis digunakan untuk tujuan yang sangat berbeda. Dalam beberapa kasus, pengembang mengejar manfaat fungsional, dan dalam kasus lain murni motif pribadi. Bagaimanapun, untuk menentukan sendiri jenis pengetikan, Anda perlu mempelajarinya dengan cermat dalam praktik. Di masa mendatang, saat membuat proyek baru dan memilih pengetikan untuknya, ini akan memainkan peran besar dan memberikan pemahaman tentang pilihan yang efektif.

Saat Anda mempelajari bahasa pemrograman, Anda sering mendengar frasa seperti "diketik secara statis" atau "diketik secara dinamis" dalam percakapan. Konsep-konsep ini menjelaskan proses pengecekan tipe, dan pengecekan tipe statis dan pengecekan tipe dinamis mengacu pada sistem tipe yang berbeda. Sistem tipe adalah seperangkat aturan yang menetapkan properti yang disebut "tipe" ke berbagai entitas dalam suatu program: variabel, ekspresi, fungsi, atau modul - dengan tujuan akhir mengurangi kesalahan dengan memastikan bahwa data ditampilkan dengan benar.

Jangan khawatir, saya tahu ini terdengar membingungkan, jadi kita akan mulai dari dasar-dasarnya. Apa itu "pemeriksaan tipe" dan apa tipe secara umum?

Jenis

Kode yang melewati pemeriksaan tipe dinamis umumnya kurang dioptimalkan; selain itu, ada kemungkinan kesalahan runtime dan, sebagai akibatnya, perlu dilakukan pemeriksaan sebelum setiap kali dijalankan. Namun, pengetikan dinamis membuka pintu ke teknik pemrograman canggih lainnya, seperti metaprogramming.

Kesalahpahaman umum

Mitos 1: Pengetikan statis/dinamis == pengetikan kuat/lemah

Kesalahpahaman yang umum adalah bahwa semua bahasa yang diketik secara statis diketik dengan kuat dan semua bahasa yang diketik secara dinamis diketik dengan lemah. Ini salah, dan inilah alasannya.

Bahasa yang diketik dengan kuat adalah bahasa yang variabelnya terikat pada tipe data tertentu, dan yang akan memunculkan kesalahan tipe jika tipe yang diharapkan dan aktual tidak cocok, setiap kali pemeriksaan dilakukan. Paling mudah untuk menganggap bahasa yang diketik dengan kuat sebagai bahasa yang sangat aman untuk mengetik. Misalnya, dalam potongan kode yang sudah digunakan di atas, bahasa yang diketik dengan kuat akan menghasilkan kesalahan ketik eksplisit yang akan membatalkan program:

X = 1 + "2"

Kami sering mengaitkan bahasa yang diketik secara statis seperti Java dan C# dengan bahasa yang diketik dengan kuat (yang memang demikian) karena tipe datanya diatur secara eksplisit ketika variabel diinisialisasi - seperti dalam contoh Java ini:

String foo = new String("halo dunia");

Namun, Ruby, Python, dan JavaScript (semuanya diketik secara dinamis) juga diketik dengan kuat, meskipun pengembang tidak perlu menentukan tipe variabel saat mendeklarasikannya. Pertimbangkan contoh yang sama, tetapi ditulis dalam Ruby:

Foo = "halo dunia"

Kedua bahasa diketik dengan kuat tetapi menggunakan metode pemeriksaan jenis yang berbeda. Bahasa seperti Ruby, Python, dan JavaScript tidak memerlukan definisi tipe eksplisit karena inferensi tipe, kemampuan untuk secara terprogram menyimpulkan tipe variabel yang benar berdasarkan nilainya. Jenis inferensi adalah properti terpisah dari bahasa, dan tidak berlaku untuk sistem tipe.

Bahasa yang diketik secara longgar adalah bahasa di mana variabel tidak terikat pada tipe data tertentu; mereka masih memiliki tipe, tetapi batasan keamanan tipe jauh lebih lemah. Perhatikan contoh kode PHP berikut:

$foo = "x"; $foo = $foo + 2; // bukan kesalahan echo $foo; // 2

Karena PHP diketik dengan lemah, tidak ada kesalahan dalam kode ini. Mirip dengan saran sebelumnya, tidak semua bahasa yang diketik dengan lemah diketik secara dinamis: PHP adalah bahasa yang diketik secara dinamis, tetapi C, juga bahasa yang diketik dengan lemah, benar-benar diketik secara statis.

Mitos itu rusak.

Meskipun statis/dinamis dan kuat/ sistem lemah jenis dan berbeda, keduanya terkait dengan keamanan jenis. Cara termudah untuk mengatakannya adalah bahwa sistem pertama memberi tahu Anda kapan keamanan jenis diperiksa, dan yang kedua memberi tahu Anda caranya.

Mitos 2: Pengetikan statis/dinamis == bahasa yang dikompilasi/ditafsirkan

Memang benar untuk mengatakan bahwa sebagian besar bahasa yang diketik secara statis biasanya dikompilasi dan ditafsirkan secara dinamis, tetapi pernyataan ini tidak dapat digeneralisasikan, dan ada contoh sederhana untuk ini.

Ketika kita berbicara tentang pengetikan bahasa, kita berbicara tentang bahasa secara keseluruhan. Misalnya, apa pun versi Java yang Anda gunakan - Java akan selalu diketik secara statis. Ini berbeda dari kasus ketika bahasa dikompilasi atau ditafsirkan, karena dalam hal ini kita berbicara tentang implementasi spesifik dari bahasa tersebut. Secara teori, bahasa apa pun dapat dikompilasi dan ditafsirkan. Implementasi bahasa Java yang paling populer menggunakan kompilasi ke bytecode, yang diinterpretasikan oleh JVM - tetapi ada implementasi lain dari bahasa ini yang dikompilasi langsung ke kode mesin atau diinterpretasikan apa adanya.

Jika ini masih belum jelas, saya menyarankan Anda untuk membaca seri ini.

Kesimpulan

Saya tahu ada banyak informasi dalam artikel ini - tetapi saya yakin Anda benar. Saya ingin memindahkan informasi tentang pengetikan yang kuat / lemah ke artikel terpisah, tetapi ini bukan topik yang penting; selain itu, harus ditunjukkan bahwa pengetikan semacam ini tidak ada hubungannya dengan pemeriksaan jenis.

Tidak ada jawaban tunggal untuk pertanyaan "pengetikan mana yang lebih baik?" - masing-masing memiliki kelebihan dan kekurangannya sendiri. Beberapa bahasa - seperti Perl dan C# - bahkan memungkinkan Anda memilih sendiri antara sistem pemeriksaan tipe statis dan dinamis. Memahami sistem ini akan memungkinkan Anda untuk lebih memahami sifat kesalahan yang terjadi, serta mempermudah penanganannya.



Memuat...
Atas